Skip to content
Development
Skill

/cmux-browser

End-user browser automation with cmux. Use when you need to open sites, inspect or interact with browser surfaces, wait for page state, and extract data without stealing focus.

From plugin
cmux
29k25 skills7 commands

Claude Code

$ npx -y skills add manaflow-ai/cmux --skill cmux-browser --agent claude-code

SKILL.md

cmux-browser.SKILL.md
name: cmux-browser
description: "End-user browser automation with cmux. Use when you need to open sites, inspect or interact with browser surfaces, wait for page state, and extract data without stealing focus."

Browser Automation with cmux

Read the CLI contract first

Check the binary that will actually run before giving an exact command:

cmux browser --help
cmux --version

There are two deliberately different command shapes:

  • **Create a browser surface** with `open`, `open-split`, or `new`. These commands may be workspace-scoped and do not need a surface handle.
  • **Use an existing surface** with every surface-bound navigation, inspection, interaction, tab, state, or diagnostic command. Pass the handle explicitly with `--surface <handle>` or as the first positional token.

Prefer the flag form in scripts because it makes the target unmissable:

SURFACE="surface:7" # use a ref returned by discovery; do not guess an index
cmux browser --surface "$SURFACE" get url
cmux browser --surface "$SURFACE" get-url       # accepted alias
cmux browser --surface "$SURFACE" snapshot --interactive
cmux browser --surface "$SURFACE" snapshot -i   # accepted alias
cmux browser --surface "$SURFACE" url            # accepted alias
cmux browser --surface "$SURFACE" tab list
cmux browser --surface "$SURFACE" click e1 --snapshot-after

The positional form is equivalent (`cmux browser "$SURFACE" get url`). `url` and `get-url` are accepted URL aliases, and the short interactive snapshot flag is accepted when a surface is already present; use `get url` and `snapshot --interactive` in new documentation so the target and operation are clear. Surface-bound operations have no unscoped form. The current CLI's explicitly global browser verbs (`open`, `open-split`, `new`, `identify`, `import`, `profile`, `profiles`, `react-grab`, `reactgrab`, `devtools`, `dev-tools`, `focus-mode`, `design-mode`, `zoom`, and `history`) may omit the handle and use caller/workspace routing; do not infer a target from visible focus for any other verb.

Find an existing browser surface without changing focus

`identify`, `tree`, and list commands are read-only and do not select a workspace, pane, or browser. Do not infer that the visually focused surface is the one the user wants.

First inspect the caller context (useful for the default workspace):

cmux identify --json

To discover browser surfaces in the caller or another workspace/window, use the all-window tree. It includes parent refs, so a browser in a different workspace can be targeted directly without selecting that workspace:

cmux tree --all --json \
  | jq -r '
      .windows[]? as $window
      | $window.workspaces[]? as $workspace
      | $workspace.panes[]? as $pane
      | $pane.surfaces[]?
      | select(.type == "browser")
      | [$window.ref, $workspace.ref, $pane.ref, .ref]
      | @tsv'

The filtered output is `window`, `workspace`, `pane`, and `surface` refs. Keep the `surface` ref, then target it explicitly:

SURFACE="surface:N" # copied from the filtered tree output
cmux browser --surface "$SURFACE" get url
cmux browser --surface "$SURFACE" snapshot --interactive

If the user gives a URL or title instead of a workspace/pane, match that metadata locally and emit only the unique surface ref. This never prints the matched URL or title:

MATCH_FIELD="url" # use "title" when matching a page title
MATCH_VALUE="${BROWSER_URL_OR_TITLE:?set BROWSER_URL_OR_TITLE without logging it}"
SURFACE="$(
  cmux tree --all --json |
    jq -r --arg field "$MATCH_FIELD" --arg value "$MATCH_VALUE" '
      [
        .windows[]? as $window
        | $window.workspaces[]? as $workspace
        | $workspace.panes[]? as $pane
        | $pane.surfaces[]?
        | select(.type == "browser")
        | select((if $field == "url" then (.url // "") else (.title // "") end) == $value)
        | .ref
      ] as $matches
      | if ($matches | length) == 1 then $matches[0]
        elif ($matches | length) == 0 then error("no matching browser surface")
        else error("multiple matches; use workspace/pane context")
        end'
)"
if [[ -z "$SURFACE" ]]; then
  printf '%s\n' 'no uniquely matching browser surface; provide workspace/pane context' >&2
  exit 1
fi
cmux browser --surface "$SURFACE" get url

For one known workspace, `cmux --json list-pane-surfaces --workspace <workspace>` is a smaller read-only query. Raw tree/list payloads can contain page URLs and titles; filter or redact them before logging or pasting them. Never use a focus/select command merely to discover a surface.

Core workflow

Open (or create) a surface without stealing focus, capture the returned ref, then use that ref for every existing-surface operation:

OPEN_JSON="$(cmux --json browser open https://example.com --focus false)"
SURFACE="$(printf '%s' "$OPEN_JSON" | jq -r '.surface_ref // .surface_id // empty')"
[ -n "$SURFACE" ] || { printf '%s\n' 'browser open did not return a surface ref' >&2; exit 1; }
cmux browser --surface "$SURFACE" get url
cmux browser --surface "$SURFACE" wait --load-state complete --timeout-ms 15000
cmux browser --surface "$SURFACE" snapshot --interactive
cmux browser --surface "$SURFACE" fill e1 "hello"
cmux browser --surface "$SURFACE" click e2 --snapshot-after
cmux browser --surface "$SURFACE" snapshot --interactive

After a browser download finishes, inspect the same surface's bounded history without opening the file or consuming a waiter:

cmux browser --surface "$SURFACE" download list
cmux browser --surface "$SURFACE" download list --limit 5 --json

The JSON records expose the stable `download_id`, filename, actual saved path when known, status (`downloading`, `saved`, or `failed`), byte count when known, and whether a known path still exists. Listing is newest first and repeatable; it remains scoped to the requested surface. Use `download wait` to keep the existing event-wait

Read more
@manaflow-ai
Ships withcmux

Open source Ghostty-based macOS terminal with vertical tabs and notifications for AI coding agents. Built for multitasking, organization, and programmability.

Stars on GitHub

Momentum

stars in total
28.6k
Star history of manaflow-ai/cmux on GitHub, a chart by star-history.com

Other skills on cmux.

cmux-architecture

cmux-architecture

cmux package architecture, refactor layering, dependency inversion, file organization, DocC documentation, package design…

@manaflow-ai@manaflow-aiInstall Skill
cmux-backend

cmux-backend

Backend TypeScript and Cloud VM development rules for cmux. Use when editing web/app/api, web/services, backend scripts, Cloud VM…

@manaflow-ai@manaflow-aiInstall Skill
cmux-billing

cmux-billing

Stripe checkout, pricing, subscription, Pro plan, webhook, and entitlement runbook for cmux billing work. Use when editing or…

@manaflow-ai@manaflow-aiInstall Skill
cmux-capture

cmux-capture

Screenshot or record a cmux window from the CLI, for a pull request, a bug report, or to see what the app actually looks like…

@manaflow-ai@manaflow-aiInstall Skill
cmux-cloud-vm

cmux-cloud-vm

Operate cmux Cloud machines, run durable remote commands or agents, and present their workspaces or services. Use for cmux…

@manaflow-ai@manaflow-aiInstall Skill
cmux-cua

cmux-cua

Use only after the user explicitly asks for cmux Computer Use through the cmux-cua skill: drive real macOS apps from a cmux agent…

@manaflow-ai@manaflow-aiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about cmux-browser.

Which coding agents can use cmux-browser?
cmux-browser installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.