AgentStack
SKILL verified MIT Self-run

Cmux

skill-boundsj-agent-skills-cmux · by boundsj

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

No reviews yet
0 installs
5 views
0.0% view→install

Install

$ agentstack add skill-boundsj-agent-skills-cmux

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Cmux? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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

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

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

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)

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

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:

# 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

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

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

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)

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

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

Wait for Conditions

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

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

Tabs, Cookies & Storage

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

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)

# 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)

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

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

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

# 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)

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 & ``

  1. 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:

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.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.