# Cmux

> cmux Terminal Multiplexer — Agent Integration. Use when orchestrating terminal sessions, running parallel commands, monitoring output, or reporting progress inside cmux.

- **Type:** Skill
- **Install:** `agentstack add skill-boundsj-agent-skills-cmux`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [boundsj](https://agentstack.voostack.com/s/boundsj)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [boundsj](https://github.com/boundsj)
- **Source:** https://github.com/boundsj/agent-skills/tree/main/cmux

## Install

```sh
agentstack add skill-boundsj-agent-skills-cmux
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# cmux Terminal Multiplexer — Agent Integration

Use when orchestrating terminal sessions, running parallel commands, monitoring output, or reporting progress inside cmux. Works for Claude Code, Cursor, and Codex.

## Detection

Check for the `CMUX_WORKSPACE_ID` environment variable. If set, you are inside cmux and can use the `cmux` CLI. If unset, do NOT attempt any cmux commands.

The CLI binary is at `/Applications/cmux.app/Contents/Resources/bin/cmux` (also available as `cmux` on PATH inside cmux terminals).

Environment variables automatically set in cmux terminals:
- `CMUX_WORKSPACE_ID` — current workspace ref
- `CMUX_SURFACE_ID` — current surface ref
- `CMUX_SOCKET_PATH` — Unix socket path (usually `/tmp/cmux.sock`)

## Hierarchy

Window > Workspace (sidebar tab) > Pane (split region) > Surface (terminal tab in pane).

Use short refs: `workspace:1`, `pane:1`, `surface:2`.

## Core Commands

### Orientation

```bash
cmux identify --json              # get caller context (workspace/surface/pane refs)
cmux list-workspaces              # list all workspaces
cmux list-panes                   # list panes in current workspace
cmux list-pane-surfaces --pane   # list surfaces (tabs) in a pane
```

### Create Terminals

```bash
cmux new-workspace --command "cd /path && cmd"  # new workspace tab (does NOT switch to it)
cmux new-split              # split current pane
cmux new-surface                                # new tab in current pane
cmux new-pane --direction                  # new pane
```

### Send Input / Read Output

```bash
cmux send --surface  "text\n"          # send text to a surface (include \n for enter)
cmux send-key --surface           # send key (enter, ctrl-c, etc.)
cmux read-screen --surface  --lines   # read terminal output (last n lines)
```

### Progress Reporting (shows in cmux sidebar)

```bash
cmux set-status   --icon  --color 
cmux set-progress  --label "text"
cmux log --level  --source "agent" -- "message"
cmux notify --title "Title" --body "Body"   # desktop notification
cmux clear-status 
cmux clear-progress
cmux clear-log
```

### Workspace Management

```bash
cmux rename-workspace "name"
cmux rename-tab --surface  "name"
cmux close-surface --surface 
cmux close-workspace --workspace 
```

## Browser Panel Commands

cmux has a built-in browser engine. You can open web pages in splits/panes and interact with them programmatically — navigate, click, type, read DOM, take screenshots, etc.

All browser commands use the form: `cmux browser   [args...]`

### Open & Navigate

**IMPORTANT:** `open-split --url` is unreliable — the URL often fails to load on initial creation.
Always use a two-step approach: create the split first, then navigate separately with a small delay:

```bash
# Two-step open (reliable)
cmux browser  open-split --direction   # 1. create the split (note the returned surface ref)
sleep 1 && cmux browser  navigate  # 2. navigate after surface is ready

# Single-surface commands
cmux browser  open                     # open URL in existing surface
cmux browser  navigate                 # navigate to URL
cmux browser  back                          # go back
cmux browser  forward                       # go forward
cmux browser  reload                        # reload page
cmux browser  url                           # get current URL
cmux browser  get title                     # get page title
```

### DOM Inspection

```bash
cmux browser  snapshot [--selector ] [--compact] [--max-depth ]  # get DOM snapshot
cmux browser  get text            # get element text
cmux browser  get html            # get element HTML
cmux browser  get value           # get element value
cmux browser  get attr  --attr   # get attribute
cmux browser  get count           # count matching elements
cmux browser  get box             # get bounding box
cmux browser  get styles          # get CSS styles
cmux browser  is visible          # check visibility
cmux browser  is enabled          # check if enabled
cmux browser  is checked          # check if checked
```

### Element Interaction

```bash
cmux browser  click               # click element
cmux browser  dblclick            # double-click
cmux browser  hover               # hover
cmux browser  focus               # focus element
cmux browser  scroll-into-view    # scroll element into view
cmux browser  scroll [--dy ]        # scroll page
```

### Form Input

```bash
cmux browser  type  "text"        # type into element (appends)
cmux browser  fill  "text"        # fill element (clears first)
cmux browser  check               # check checkbox
cmux browser  uncheck             # uncheck checkbox
cmux browser  select  "value"     # select dropdown value
cmux browser  press                    # press key (Enter, Tab, Escape, etc.)
```

### Find Elements (Locators)

```bash
cmux browser  find role  [--name ]      # find by ARIA role
cmux browser  find text "text" [--exact]            # find by text content
cmux browser  find label "label" [--exact]          # find by label
cmux browser  find placeholder "text" [--exact]     # find by placeholder
cmux browser  find testid "id"                      # find by test ID
cmux browser  find first                  # first match
cmux browser  find nth             # nth match
```

### JavaScript & Screenshots

```bash
cmux browser  eval "document.title"         # evaluate JavaScript
cmux browser  screenshot [--out ]     # take screenshot
```

### Wait for Conditions

```bash
cmux browser  wait                # wait for element
cmux browser  wait --text "text"             # wait for text
cmux browser  wait --url "url"               # wait for URL
cmux browser  wait --load-state       # wait for load state
cmux browser  wait --function "js expr"      # wait for JS condition
```

### Console & Errors

```bash
cmux browser  console list                  # list console messages
cmux browser  errors list                   # list page errors
```

### Tabs, Cookies & Storage

```bash
cmux browser  tab list                      # list browser tabs
cmux browser  tab new []               # new browser tab
cmux browser  cookies get [--domain ]    # get cookies
cmux browser  cookies set      # set cookie
cmux browser  storage local get []     # get localStorage
cmux browser  storage local set   # set localStorage
```

### Network & Emulation

```bash
cmux browser  viewport       # set viewport size
cmux browser  offline true|false             # toggle offline mode
cmux browser  geolocation          # set geolocation
cmux browser  network route  [--abort] [--body ]  # mock network
cmux browser  network requests              # get network requests
```

## Workflow Patterns

### Fan out into splits (parallel tasks in one workspace)

```bash
# Create splits for build and test
cmux new-split right
cmux send --surface surface:2 "npm run dev\n"

cmux new-split down
cmux send --surface surface:3 "npm test -- --watch\n"

# Report progress
cmux set-status build "Running" --icon hammer --color "#1565C0"

# ... do work ...

# Check results
cmux read-screen --surface surface:3 --lines 20
cmux set-status build "Done" --icon checkmark --color "#196F3D"
```

### Fan out into workspace tabs (isolated environments)

```bash
cmux new-workspace --command "cd ~/project/backend && npm run build"
cmux new-workspace --command "cd ~/project/frontend && npm run build"
```

### Run tests, read failures, fix, re-run

```bash
cmux new-split right
cmux send --surface surface:2 "npm test 2>&1\n"
# wait, then read output
cmux read-screen --surface surface:2 --lines 50
# fix code based on output, then re-run
cmux send --surface surface:2 "npm test 2>&1\n"
```

### Report progress throughout a task

```bash
cmux set-progress 0.0 --label "Starting build"
# ... step 1 ...
cmux set-progress 0.33 --label "Compiling"
# ... step 2 ...
cmux set-progress 0.66 --label "Running tests"
# ... step 3 ...
cmux set-progress 1.0 --label "Complete"
cmux clear-progress
cmux notify --title "Build Complete" --body "All tests passed"
```

### Open a website, inspect it, interact with it

```bash
# Open a browser panel in a split (two-step for reliability)
cmux browser surface:1 open-split --direction right
sleep 1 && cmux browser surface:2 navigate "https://example.com"
# Wait for it to load
cmux browser surface:2 wait --load-state networkidle
# Get a DOM snapshot to understand the page
cmux browser surface:2 snapshot --compact
# Find and click a button
cmux browser surface:2 click "button.submit"
# Read resulting text
cmux browser surface:2 get text ".result-message"
# Take a screenshot
cmux browser surface:2 screenshot --out /tmp/result.png
```

### Check a web app's state (e.g., verify a deploy)

```bash
cmux browser surface:1 open-split --direction right
sleep 1 && cmux browser surface:2 navigate "https://myapp.com"
cmux browser surface:2 wait --load-state networkidle
cmux browser surface:2 get title
cmux browser surface:2 eval "document.querySelector('.version')?.textContent"
cmux browser surface:2 console list    # check for errors
cmux browser surface:2 errors list
```

## Markdown Preview in Browser Panel

When the user asks to open/view/preview a `.md` file in cmux (e.g., "open foo.md on the right", "show the plan"), render it as styled HTML in a cmux browser panel. Do NOT use `less`, `cat`, or `file://` URLs.

### File naming

Derive the HTML filename from the source markdown filename:
- `/path/to/my-plan.md` → `/tmp/my-plan.html`
- Use `os.path.basename` and replace `.md` with `.html`

**Track which HTML file you created.** When the user asks to update/refresh the preview, or when you modify the source markdown, regenerate the **same HTML file** and `cmux browser  reload`. Do not create a second HTML file with a different name.

### Steps

1. **Convert markdown to HTML** using the Python script below. Write output to `/tmp/.html`.
2. **Start a local HTTP server** (if not already running — check with `lsof -ti:18923`):
   ```bash
   python3 -m http.server 18923 --directory /tmp --bind 127.0.0.1 &>/dev/null &
   ```
3. **Open in cmux browser panel** using the direction the user requested:
   ```bash
   cmux browser  open-split "http://127.0.0.1:18923/.html"
   ```

### HTML conversion script

Use this Python script (no external dependencies). It strips YAML frontmatter and converts headings, ordered/unordered lists, code blocks, inline code, bold, links, and horizontal rules:

```python
python3 -c "
import re, sys, os, html as html_mod

src = sys.argv[1]
out_name = os.path.basename(src).rsplit('.', 1)[0] + '.html'
out_path = '/tmp/' + out_name

with open(src) as f:
    content = f.read()

# Strip YAML frontmatter
if content.startswith('---'):
    end = content.index('---', 3)
    content = content[end+3:].strip()

DARK = '''
  body { font-family: -apple-system, system-ui, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; line-height: 1.6; color: #c9d1d9; background: #0d1117; }
  h1 { border-bottom: 2px solid #30363d; padding-bottom: 8px; color: #e6edf3; }
  h2 { border-bottom: 1px solid #30363d; padding-bottom: 4px; margin-top: 32px; color: #e6edf3; }
  h3 { margin-top: 24px; color: #e6edf3; }
  code { background: #161b22; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; color: #f0883e; }
  pre { background: #161b22; padding: 16px; border-radius: 6px; overflow-x: auto; color: #c9d1d9; }
  pre code { background: none; padding: 0; color: #c9d1d9; }
  ul { padding-left: 24px; margin: 8px 0; }
  ol { padding-left: 24px; margin: 8px 0; }
  li { margin-bottom: 6px; }
  a { color: #58a6ff; }
  p { margin: 4px 0; }
  strong { color: #e6edf3; }
  hr { border: none; border-top: 1px solid #30363d; margin: 32px 0; }
  table { border-collapse: collapse; width: 100%; margin: 16px 0; }
  th { background: #161b22; padding: 8px 12px; border: 1px solid #30363d; text-align: left; color: #e6edf3; font-weight: 600; }
  td { padding: 8px 12px; border: 1px solid #30363d; }
  tr:nth-child(even) td { background: rgba(22,27,34,0.5); }
'''

LIGHT = '''
  body { font-family: -apple-system, system-ui, sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; line-height: 1.6; color: #1a1a1a; background: #fff; }
  h1 { border-bottom: 2px solid #e1e4e8; padding-bottom: 8px; }
  h2 { border-bottom: 1px solid #e1e4e8; padding-bottom: 4px; margin-top: 32px; }
  h3 { margin-top: 24px; }
  code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
  pre { background: #f6f8fa; padding: 16px; border-radius: 6px; overflow-x: auto; }
  pre code { background: none; padding: 0; }
  ul { padding-left: 24px; margin: 8px 0; }
  ol { padding-left: 24px; margin: 8px 0; }
  li { margin-bottom: 6px; }
  a { color: #0366d6; }
  p { margin: 4px 0; }
  hr { border: none; border-top: 1px solid #e1e4e8; margin: 32px 0; }
  table { border-collapse: collapse; width: 100%; margin: 16px 0; }
  th { background: #f6f8fa; padding: 8px 12px; border: 1px solid #e1e4e8; text-align: left; font-weight: 600; }
  td { padding: 8px 12px; border: 1px solid #e1e4e8; }
  tr:nth-child(even) td { background: #f9f9f9; }
'''

theme = DARK if '--dark' in sys.argv else LIGHT

def inline(t):
    t = re.sub(r'\x60([^\x60]+)\x60', r'\1', t)
    t = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'\1', t)
    t = re.sub(r'\*\*([^*]+)\*\*', r'\1', t)
    t = re.sub(r'(?)]+', lambda m: f'{m.group()}', t)
    return t

def flush_tbl(rows, sep):
    out = ['']
    started_body = False
    for i, row in enumerate(rows):
        if i == sep: continue
        cells = [c.strip() for c in row.strip().strip('|').split('|')]
        if sep >= 0 and i ' + ''.join(f'{inline(c)}' for c in cells) + '')
        else:
            if not started_body: out.append(''); started_body = True
            out.append('' + ''.join(f'{inline(c)}' for c in cells) + '')
    if started_body: out.append('')
    out.append('')
    return out

lines = content.split('\n')
h, in_ul, in_ol, in_code, code_buf, in_tbl, tbl_rows, tbl_sep = [], False, False, False, [], False, [], -1
for line in lines:
    s = line.strip()
    if s.startswith('\x60\x60\x60'):
        if in_code:
            h.append('' + '\n'.join(code_buf) + '')
            code_buf = []; in_code = False
        else:
            if in_ul: h.append(''); in_ul = False
            if in_ol: h.append(''); in_ol = False
            in_code = True
        continue
    if in_code:
        code_buf.append(html_mod.escape(line)); continue
    if s.startswith('|') and '|' in s[1:]:
        if in_ul: h.append(''); in_ul = False
        if in_ol: h.append(''); in_ol = False
        if not in_tbl: in_tbl = True; tbl_rows = []; tbl_sep = -1
        if all(re.match(r'^[-:]+$', c.strip()) for c in s.strip().strip('|').split('|') if c.strip()):
            tbl_sep = len(tbl_rows)
        tbl_rows.append(s)
        continue
    if in_tbl:
        h.extend(flush_tbl(tbl_rows, tbl_sep)); in_tbl = False
    if not s:
        if in_ul: h.append(''); in_ul = False
        if in_ol: h.append(''); in_ol = False
        h.append(''); continue
    if s == '---':
        if in_ul: h.append(''); in_ul = False
        if in_ol: h.append(''); in_ol = False
        h.append(''); continue
    m = re.match(r'^(#{1,4})\s+(.*)', s)
    if m:
        if in_ul: h.append(''); in_ul = False
        if in_ol: h.append(''); in_ol = False
        n = len(m.group(1))
        h.append(f'{inline(m.group(2))}'); continue
    m = re.match(r'^(\d+)\.\s+(.*)', s)
    if m:
        if in_ul: h.append(''); in_ul = False
        if not in_ol: h.append(''); in_ol = True
        h.append(f'{inline(m.group(2))}'); continue
    m = re.match(r'^[-*]\s+(.*)', s)
    if m:
        if in_ol: h.append(''); in_ol = False
        if not in_ul: h.append(''); in_ul = True
        h.append(f'{inline(m.group(1))}'); continue
    if in_ul: h.append(''); in_ul = False
    if in_ol: h.append(''); in_ol = False
    h.append(f'{inline(s)}')
if in_ul: h.append('')
if in_ol: h.append('')
if in_tbl: h.extend(flush_tbl(tbl_rows, tbl_sep))

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [boundsj](https://github.com/boundsj)
- **Source:** [boundsj/agent-skills](https://github.com/boundsj/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-boundsj-agent-skills-cmux
- Seller: https://agentstack.voostack.com/s/boundsj
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
