elements-of-style
Applies the classic writing rules from Strunk's style guide to any prose people will read, such as documentation, commit messages, error messages and reports, so the text comes out clearer and more concise.
Gives Claude Code direct control of an existing Chrome browser through the Chrome DevTools Protocol. It handles multi-tab management, form automation, and content extraction, and can run as a skill or as an MCP server.
Claude Code
> /plugin marketplace add obra/superpowers-chrome> /plugin install superpowers-chrome@superpowers-chrome-dev
Codex
$ npx skills add obra/superpowers-chrome -a codex
Cursor
$ npx skills add obra/superpowers-chrome -a cursor
Gemini CLI
$ npx skills add obra/superpowers-chrome -a gemini-cli
GitHub Copilot
$ copilot plugin marketplace add obra/superpowers-chrome$ copilot plugin install superpowers-chrome@superpowers-chrome-dev
OpenCode
$ npx skills add obra/superpowers-chrome -a opencode
Windsurf
$ npx skills add obra/superpowers-chrome -a windsurf
Direct browser control via Chrome DevTools Protocol. Two modes available:
browsing skill)0, 1, 2) instead of WebSocket URLschrome-ws start works on macOS, Linux, Windows/plugin marketplace add obra/superpowers-marketplace
/plugin install superpowers-chrome@superpowers-marketplace
# Find your plugin installation path (varies by marketplace and version)
# Common locations:
# ~/.claude/plugins/cache/superpowers-marketplace/superpowers-chrome/<version>/skills/browsing
# ~/.claude/plugins/cache/superpowers-chrome/skills/browsing
cd ~/.claude/plugins/cache/superpowers-marketplace/superpowers-chrome/*/skills/browsing
./chrome-ws start # Launch Chrome
./chrome-ws new "https://example.com" # Create tab
./chrome-ws navigate 0 "https://google.com"
./chrome-ws fill 0 "textarea[name=q]" "test"
./chrome-ws click 0 "button[name=btnK]"
Port allocation: Chrome gets a dynamically allocated port (range 9222-12111) to avoid conflicts. Port assignment is persisted per profile in ~/.cache/superpowers/browser-profiles/{name}.meta.json. Override with --port=N flag or CHROME_WS_PORT env var. Multiple profiles can run in parallel on different ports.
Parallel MCPs on one host (3.0+): the bridge auto-disambiguates the default profile. The first MCP claims superpowers-chrome:9222, the next silently falls through to superpowers-chrome-2:9223, then -3:9224, etc., each driving its own Chrome with its own profile dir. To intentionally share a Chrome between processes (e.g., a chrome-ws CLI session + a Claude MCP attaching to it), set a fixed profile via CHROME_WS_PROFILE=name (env var) or call {action: "set_profile", payload: "name"} at runtime — explicit profiles share rather than disambiguate.
Windows tip: The tooling defaults to 127.0.0.1 for DevTools traffic. Override via CHROME_WS_HOST / CHROME_WS_PORT or --port=N if you forward Chrome elsewhere.
Linux/WSL2 tip: For headed mode (visible browser), the MCP server needs the DISPLAY environment variable. If show_browser doesn't work, configure "env": {"DISPLAY": ":0"} in your MCP server config. See mcp/README.md for details. Running as root or inside a container is detected automatically and disables Chrome's sandbox; on a headless box add CHROME_EXTRA_ARGS="--headless=new --disable-gpu".
Custom Chrome flags: Set CHROME_EXTRA_ARGS to a whitespace-separated list of flags that will be appended to the Chrome command line on launch. Useful for headless containers that need software WebGL:
CHROME_EXTRA_ARGS="--use-gl=angle --use-angle=swiftshader-webgl --enable-unsafe-swiftshader"
node skills/browsing/chrome-ws start launched Chrome with remote debugging enabled on a fresh Windows 11 Pro install.node skills/browsing/chrome-ws tabs and node skills/browsing/chrome-ws navigate 0 https://example.com confirmed CLI control with the IPv4 default binding.codex exec -c "mcp_servers.superpowers-chrome.enabled=true" "List Chrome tabs via MCP to verify the Windows override patch." listed the Example Domain tab through the MCP server, demonstrating that the overrides also work through Codex.start (auto-detects platform)tabs, new, closenavigate, wait-for, wait-textclick, fill, selecteval, extract, attr, htmlscreenshot, markdownraw (full CDP access)Pages that open JavaScript dialogs (alert, confirm, prompt, beforeunload), WebUSB/Bluetooth/Serial/HID device choosers, HTTP basic-auth challenges, or permission prompts (camera, microphone, notifications, geolocation, clipboard) no longer wedge the connection. The dialog is surfaced as a synthetic page response and the agent interacts with it using the existing click and type actions against a small dialog::* selector grammar.
While a dialog is open, any page-targeted action (extract, screenshot, eval, attr, click <real-selector>, etc.) returns a clear refusal with the dialog content and instructions:
Page is behind a dialog. Handle dialog::accept or dialog::dismiss first.
# Dialog: confirm
Tab origin: https://example.com
> Are you sure you want to leave?
Buttons:
- dialog::accept (OK)
- dialog::dismiss (Cancel)
To interact:
click selector="dialog::accept"
click selector="dialog::dismiss"
Browser-targeted actions (list_tabs, new_tab, close_tab, etc.) pass through unaffected.
| Selector | Purpose |
|---|---|
click dialog::accept | OK / Grant / Provide credentials, depending on dialog kind |
click dialog::dismiss | Cancel / Deny |
type dialog::prompt <value> | Stage prompt text; commit on dialog::accept |
click dialog::device[id="…"] | Pick a device in the chooser (USB, BT, Serial, HID) |
type dialog::username <value> / type dialog::password <value> | Basic-auth credentials |
# 1. Page on load: alert('Saved!')
extract payload=text
# → refused with synthetic dialog markdown
# 2. Dismiss
click selector="dialog::accept"
# 3. Page is interactive again
extract payload=text
# → returns the page text
Permission prompts (getUserMedia, Notification.requestPermission, geolocation, clipboard) are caught by a document_start JS-API shim and surfaced through the same flow.
See docs/superpowers/specs/2026-05-13-dialog-handling-design.md for the full design.
Ultra-lightweight MCP server with a single use_browser tool. Perfect for minimal context usage with automatic page captures.
Option 1: NPX from GitHub (Recommended)
{
"mcpServers": {
"chrome": {
"command": "npx",
"args": [
"github:obra/superpowers-chrome"
]
}
}
}
Option 1b: NPX with Headless Mode
{
"mcpServers": {
"chrome": {
"command": "npx",
"args": [
"github:obra/superpowers-chrome",
"--headless"
]
}
}
}
Option 2: Git Clone + Local Path (Current)
git clone https://github.com/obra/superpowers-chrome.git
cd superpowers-chrome/mcp && npm install && npm run build
{
"mcpServers": {
"chrome": {
"command": "node",
"args": [
"/path/to/superpowers-chrome/mcp/dist/index.js"
]
}
}
}
DOM-changing actions (navigate, click, type, select, eval) automatically capture:
Pages showing credential-shaped content are not captured; see Credential-shaped pages.
Response format:
→ https://example.com (capture #001)
Size: 1200×765
Snapshot: /tmp/chrome-session-123/001-navigate-456/
Resources: page.html, page.md, screenshot.png, console-log.txt
DOM:
Example Domain
Interactive: 0 buttons, 0 inputs, 1 links
Layout: body
When a page shows credential-shaped content, auto-capture writes no files for that action and the response carries only metadata (URL, size, element counts, layout) plus a ⚠️ Page shows credential-shaped content; auto-capture and DOM output suppressed. line. No markdown, headings, title, or DOM diff is returned. This covers Slack tokens (xox[abposr]-, xoxe./xoxe-, xapp-), GitHub tokens (ghp_, gho_, ghu_, ghs_, ghr_, github_pat_), 1Password service-account tokens (ops_eyJ…) and Secret Keys (A3-…), otpauth:// URIs carrying a secret=, and any page containing an element with the data-sen-secret attribute (for secrets with no distinctive shape, like backup codes; see below for what marking does not cover). The check covers the HTML, the rendered markdown, the DOM summary, the page's rendered text (innerText, which joins a token split across inline spans), open shadow roots, and live input/textarea values. It runs after the screenshot, and any match deletes every artifact already written for that action, so a token revealed while the capture runs doesn't survive in the PNG. It can't see inside closed shadow roots or cross-origin iframes.
In addition, every use_browser result and error has credential-shaped substrings replaced with [REDACTED credential-shaped], and screenshot refuses on such a page.
data-sen-secret marker: an accident guard, not a boundaryMark an element with the data-sen-secret attribute (via set_attr) when it holds a secret with no distinctive shape — a bare base32 TOTP seed, a backup code — that the credential-shape check above can't recognize. When to mark: as soon as the action that revealed the secret returns, before any other action on that page, then capture the value with the credential broker. Marking only affects actions after it: the capture files the revealing action already wrote (typically its NNN-navigate.html/.md or NNN-click.html/.md) still hold the value, and marking does not delete them. Once marked:
eval refuses outright, page-wide, if ANY element on the page currently carries data-sen-secret — checked against the live DOM immediately before the expression runs. This is a point check at the moment of the call: it does not remember that the page was ever marked, and it does not re-check after the expression finishes. extract and attr with a selector naming a specific ELEMENT refuse when the resolved element or any ancestor of it (through shadow-root hosts) is marked; where that element-scoped read doesn't require touching the marked value at all (extract's HTML form on a selector), it instead reads off a clone imported into a fresh, inert document with marked descendants stripped, rather than refusing over one marked corner of the element it was asked for. A WHOLE-PAGE read — no selector, or a selector that RESOLVES to the whole document (see "Explicit reads on a sensitive page" below for exactly which selectors that covers), on extract's text/HTML/markdown forms — refuses outright instead of stripping, the moment ANY element anywhere on the page carries the marker (see "Explicit reads on a sensitive page" below): stripping only removes DESCENDANTS of the clone's root, so a marker on document.body itself (or a page-level container above wherever the clone-and-strip read starts) would otherwise still leak. Screenshot and auto-capture use the same live scan, extended to open shadow roots and same-origin iframe/frame/object/embed, and write nothing to disk for a marked page from then on.Applies the classic writing rules from Strunk's style guide to any prose people will read, such as documentation, commit messages, error messages and reports, so the text comes out clearer and more concise.
Semantic search over past Claude Code conversations, so a session can recall earlier discussions, decisions, and patterns. It adds a remembering-conversations skill that checks prior sessions before the assistant says it doesn't know.
Turn one coding-agent session into a project manager that delegates tasks to other coding-agent sessions, Claude Code, Codex, or Pi.
Fair-code platform to build and deploy AI agents and workflows. Combine a visual canvas with custom code, run it self-hosted or in the cloud, and connect to 1500+ integrations. AI automation you can trust with real work, from prototype to production.
A browser automation plugin for Claude Code with skills for direct browser control, scraping, testing, and QA scoring of websites, plus guidance on the Browser Use cloud browser API and Python library.
Organizes your AI agents into a team that runs around the clock, handling hiring, scheduling, delegation and reporting so you can stay in charge without staying online.
A browser automation command line tool for AI agents, letting them navigate pages, fill forms, click buttons, take screenshots and extract data from websites.
Install it with the command on its page. It includes browsing. Its skills do not fire on their own yet. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it