browse — Claude Code skill for browser automation and installation
The skill operates a browser and checks page changes visually. Use it to inspect a live site, test a form, or compare pages across tabs.
npx skills add SeifBenayed/cloclo --skill browse -a claude-codeWhat browse does
The Browser tool lets the skill navigate to pages, interact with controls, enter text, upload files, and extract selected page data. It inspects the page state and takes a screenshot before acting, then checks again after a change. That makes each interaction verifiable rather than a blind click.
Workflows can use separate tabs, isolated sessions with named profiles, and DOM actions inside iframes. To connect to an existing Chrome instance, provide cdp_url or set BROWSER_CDP_URL. A task is limited to 10 observe-act-verify cycles; if the goal is still out of reach, the skill stops and reports the outcome.
For Claude Code, run npx skills add SeifBenayed/cloclo --skill browse -a claude-code. The installed skill file is at ~/.claude/skills/browse/SKILL.md.
What it helps with
- check whether a page renders as expected
- submit a web form and inspect the result
- compare two pages in separate tabs
- upload a file through a site form
- extract a page title, price, or description
Example prompts for the agent
- Open the site and check whether its sign-in form appears correctly.
- Fill out the form and tell me what changes after submission.
- Compare these pages in separate tabs and describe their interface differences.
How to install browse
1Skills CLI (Vercel)
npx skills add SeifBenayed/cloclo --skill browse -a claude-code2Manually: put the files in place
~/.claude/skills/browse/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/browse/
1Skills CLI (Vercel)
npx skills add SeifBenayed/cloclo --skill browse -a codex2Manually: put the files in place
.agents/skills/browse/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add SeifBenayed/cloclo --skill browse -a cursor2Manually: put the files in place
.cursor/skills/browse/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install SeifBenayed/cloclo browse2Manually: put the files in place
.github/skills/browse/SKILL.md1Skills CLI (Vercel)
npx skills add SeifBenayed/cloclo --skill browse -a gemini-cli2Platform CLI
gemini skills install https://github.com/SeifBenayed/cloclo --path .claude/skills/browseManually: put the files in place
.devin/skills/browse/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add SeifBenayed/cloclo --skill browse -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about browse
How do I install browse in Claude Code?
Run npx skills add SeifBenayed/cloclo --skill browse -a claude-code. The skill file will be located at ~/.claude/skills/browse/SKILL.md.
What does browse do?
It controls a browser through the Browser tool, interacting with web pages and checking changes through page state and screenshots.
Can browse work with multiple sessions and iframes?
Yes. Separate sessions can use a session_id and profile, while iframe actions use a frame_id returned by list_frames.
Source text
SKILL.md · MIT
---
name: browse
description: |
Visual feedback browser automation with multi-session, multi-tab, iframe support,
and enterprise primitives. Navigate sites, interact with elements, manage tabs/sessions,
handle file uploads, dropdowns, iframes, and verify results using the
screenshot-analyze-act-verify loop. Use when asked to "open a site", "test a page",
"fill a form", "check a deployment", "browse to", "click on", "verify the UI", "compare
pages side by side", or any task involving web interaction. Also use proactively when a task
would benefit from checking a live URL.
allowed-tools: Browser,Read,Grep,Glob
---
# Browse — Visual Feedback Browser Automation
You have a `Browser` tool with an action-based interface. This skill teaches you how to use it
effectively with a **screenshot → analyze → act → verify** loop that prevents blind clicking.
## Core Principle
Never act without seeing. Never assume an action worked. Always verify.
```
OBSERVE → DECIDE → ACT → VERIFY
(get_state (analyze (click/ (get_state
+ screenshot elements) type) + screenshot
+ compare)
```
## Starting a Session
Every browser session starts the same way:
1. **Navigate** to the target URL
2. **Observe** the page with `get_state` — this returns the DOM with indexed interactive elements AND the page text
3. **Screenshot** to see the visual layout
```
Browser { action: "navigate", url: "https://example.com" }
Browser { action: "get_state" }
Browser { action: "screenshot" }
```
Read the screenshot to understand the visual layout. The `get_state` output gives you:
- URL and title
- Scroll position (how far down the page you are)
- Interactive element count (links, inputs, buttons)
- Indexed elements: `[0] <button> "Submit"`, `[1] <input:text> name="email"`
- Page text (first 3000 chars)
### JSON Format
For structured processing, use `format: "json"`:
```
Browser { action: "get_state", format: "json" }
```
Returns: `{ url, title, scroll, stats, elements: [{index, tag, text, ...}], text, session_id, active_tab_id }`
## Interacting with Elements
**Always prefer element indices over CSS selectors.** The indices come from `get_state` and are
reliable. CSS selectors can break if the page structure changes.
```
Browser { action: "click_element", index: 5 }
Browser { action: "type_element", index: 3, value: "[email protected]" }
```
Fall back to CSS selectors only when:
- The element doesn't appear in the indexed list
- You need to target something very specific (like `input[name="csrf_token"]`)
```
Browser { action: "click", selector: "#submit-btn" }
Browser { action: "fill", selector: "input[name='email']", value: "[email protected]" }
```
## Keyboard Input (send_keys)
For named keys and key combinations:
```
Browser { action: "send_keys", keys: "Enter" }
Browser { action: "send_keys", keys: "Tab Tab Enter" }
Browser { action: "send_keys", keys: "Ctrl+a" }
Browser { action: "send_keys", keys: "Shift+Tab" }
```
Supported named keys: Enter, Tab, Escape, Backspace, Delete, Space, ArrowUp/Down/Left/Right, Home, End, PageUp, PageDown, F1-F12.
Modifiers: Ctrl, Alt, Shift, Meta/Cmd. Sequences: space-separated.
## File Upload
```
Browser { action: "upload_file", selector: "input[type='file']", file_path: "/path/to/file.pdf" }
```
## Dropdowns
```
Browser { action: "dropdown_options", selector: "#my-select" }
Browser { action: "select_dropdown", selector: "#my-select", value: "option_value" }
```
## Structured Data Extraction
Extract structured data from the page using CSS selectors:
```
Browser { action: "extract", schema: {"title": "h1", "price": ".price", "description": ".desc"} }
```
Returns JSON: `{"title": "Product Name", "price": "$29.99", "description": "..."}`
## Multi-Tab Workflows
Open, switch between, and close tabs for side-by-side comparison:
```
Browser { action: "navigate", url: "https://site-a.com" }
Browser { action: "new_tab", url: "https://site-b.com" }
Browser { action: "list_tabs" }
Browser { action: "switch_tab", tab_id: "PREVIOUS_TAB_ID" }
Browser { action: "close_tab", tab_id: "TAB_TO_CLOSE" }
```
Tab IDs are strings (CDP target IDs). Get them from `list_tabs`.
## Multi-Session Workflows
Use separate sessions for isolated browser instances (different profiles, auth states):
```
Browser { action: "new_session", session_id: "admin", profile_name: "admin-profile" }
Browser { action: "navigate", url: "https://app.com/admin", session_id: "admin" }
Browser { action: "new_session", session_id: "user", profile_name: "user-profile" }
Browser { action: "navigate", url: "https://app.com/dashboard", session_id: "user" }
Browser { action: "list_sessions" }
Browser { action: "close_session", session_id: "admin" }
```
### Named Profiles
Profiles persist cookies and state across sessions:
- `profile_name: "my-profile"` → stored in `~/.claude/browser-profiles/my-profile/`
- `user_data_dir: "/custom/path"` → raw Chrome user data directory
- `profile_dir: "Profile 1"` → Chrome's `--profile-directory` flag
## Attach Mode (Remote Chrome)
Connect to an already-running Chrome instance:
```
Browser { action: "new_session", session_id: "remote", cdp_url: "http://localhost:9222" }
```
Or set `BROWSER_CDP_URL=http://localhost:9222` env var for the default session.
In attach mode, `close` disconnects without killing Chrome.
## Iframe Support
```
Browser { action: "list_frames" }
Browser { action: "click_element", index: 0, frame_id: "FRAME_ID" }
Browser { action: "fill", selector: "input", value: "text", frame_id: "FRAME_ID" }
```
Frame IDs come from `list_frames`. Use `frame_id` on any DOM action to target elements inside iframes.
## Events & Dialogs
The browser captures events (dialogs, navigations, crashes, downloads):
```
Browser { action: "get_events" }
Browser { action: "set_dialog_auto_dismiss", enabled: true }
```
Dialogs (alert/confirm/prompt) are auto-dismissed by default. Disable with `enabled: false` to handle manually.
## The Verify Loop
After **every** action that changes the page, you must verify:
```
1. Take action (click, type, navigate, submit)
2. Wait briefly if needed: Browser { action: "wait_for", selector: ".result", timeout: 3000 }
3. Observe again: Browser { action: "get_state" }
4. Screenshot again: Browser { action: "screenshot" }
5. Compare: did the page change as expected?
- YES → continue to next step
- NO → try a different approach (different element, different selector, scroll first)
```
## Iteration Budget
You have a maximum of **10 iterations** (observe-act-verify cycles) per task. This prevents
infinite loops. If you haven't achieved the goal in 10 iterations:
1. Stop
2. Report what you accomplished and what failed
3. Include the last screenshot as evidence
Count your iterations. Mention the count when reporting.
## Error Recovery
When something doesn't work:
1. **Element not found** — the page may have changed. Run `get_state` again to refresh the element indices.
2. **Click didn't work** — the element might be obscured. Try `scroll_to` first, then click again.
3. **Page didn't load** — try `wait_for` with a key selector, or `reload`.
4. **Form submission failed** — screenshot to see error messages, read them, adjust input.
5. **Same action 3 times** — the Browser tool has loop detection. If you get a loop warning, you MUST try a completely different approach.
Fallback chain for clicking:
```
click_element by index → click by CSS selector → evaluate with JS click → scroll + retry
```
## Scrolling
Pages are often longer than the viewport. The `get_state` output shows scroll position.
If you need elements below the fold:
```
Browser { action: "scroll_to", value: "500" } // scroll down 500px
Browser { action: "scroll_to", selector: "#footer" } // scroll to element
Browser { action: "get_state" } // refresh elements after scroll
```
Always `get_state` after scrolling — the element indices change.
## Evidence and Reporting
When completing a task, provide evidence:
1. **Before state** — screenshot of the page before your actions
2. **Actions taken** — list of what you did (clicked X, typed Y, navigated to Z)
3. **After state** — screenshot showing the result
4. **Verification** — what you checked to confirm the task is done
Format your report:
```
## Browser Task: [description]
**URL:** https://example.com/page
**Session:** default
**Iterations:** 4/10
### Actions
1. Navigated to https://example.com
2. Clicked [3] <button> "Login"
3. Typed email into [5] <input:email>
4. Typed password into [7] <input:password>
5. Sent keys: Enter
### Verification
- Page title changed to "Dashboard"
- User avatar visible in header
- No error messages present
### Screenshots
- Before: [screenshot 1]
- After: [screenshot 2]
```
## JavaScript Evaluation
For complex checks or actions not covered by built-in actions:
```
Browser { action: "evaluate", value: "document.querySelectorAll('.error').length" }
Browser { action: "evaluate", value: "window.localStorage.getItem('token')" }
```
This is powerful but use it sparingly — prefer the built-in actions.
## Cookies
For authenticated sessions:
```
Browser { action: "cookies_get" }
Browser { action: "cookies_set", cookie: { name: "session", value: "abc123", domain: ".example.com" } }
Browser { action: "cookies_clear" }
```
## Closing
Always close the browser when done:
```
Browser { action: "close" }
```
This frees system resources. The browser will auto-launch again if needed.
## Quick Reference
| Goal | Action | Notes |
|------|--------|-------|
| See the page | `get_state` + `screenshot` | Always first |
| See page (structured) | `get_state` with `format: "json"` | For parsing |
| Click a button | `click_element` with index | From get_state |
| Type text | `type_element` with index and value | |
| Press keys | `send_keys` with keys | "Enter", "Tab", "Ctrl+a" |
| Navigate | `navigate` with url | |
| Upload a file | `upload_file` with selector, file_path | |
| Read dropdown | `dropdown_options` with selector | Returns [{value,text,selected}] |
| Select dropdown | `select_dropdown` with selector, value | |
| Extract data | `extract` with schema | Returns structured JSON |
| Wait for content | `wait_for` with selector | |
| Scroll down | `scroll_to` with value or selector | |
| Open new tab | `new_tab` with optional url | |
| Switch tab | `switch_tab` with tab_id | |
| List tabs | `list_tabs` | |
| Close tab | `close_tab` with tab_id | |
| New session | `new_session` with session_id | Optional: profile_name, cdp_url |
| List sessions | `list_sessions` | |
| List frames | `list_frames` | For iframe inspection |
| Get events | `get_events` | Dialog, crash, download, navigation |
| Run JS | `evaluate` with value | |
| Go back | `back` | |
| Save page as PDF | `pdf` | |
| Check cookies | `cookies_get` | |
| Done | `close` | |
© SeifBenayed · MIT · SeifBenayed/cloclo