Add cmux skills (browser, debug-windows, markdown, core)
Skills for cmux terminal integration: browser automation for visual verification, debug windows, markdown viewer, and core topology control. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
30d40de7cf
commit
b4c0ee954d
24 changed files with 1368 additions and 0 deletions
141
.claude/skills/cmux-browser/SKILL.md
Normal file
141
.claude/skills/cmux-browser/SKILL.md
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
---
|
||||
name: cmux-browser
|
||||
description: End-user browser automation with cmux. Use when you need to open sites, interact with pages, wait for state changes, and extract data from cmux browser surfaces.
|
||||
---
|
||||
|
||||
# Browser Automation with cmux
|
||||
|
||||
Use this skill for browser tasks inside cmux webviews.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
1. Open or target a browser surface.
|
||||
2. Verify navigation with `get url` before waiting or snapshotting.
|
||||
3. Snapshot (`--interactive`) to get fresh element refs.
|
||||
4. Act with refs (`click`, `fill`, `type`, `select`, `press`).
|
||||
5. Wait for state changes.
|
||||
6. Re-snapshot after DOM/navigation changes.
|
||||
|
||||
```bash
|
||||
cmux --json browser open https://example.com
|
||||
# use returned surface ref, for example: surface:7
|
||||
|
||||
cmux browser surface:7 get url
|
||||
cmux browser surface:7 wait --load-state complete --timeout-ms 15000
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
cmux browser surface:7 fill e1 "hello"
|
||||
cmux --json browser surface:7 click e2 --snapshot-after
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
## Surface Targeting
|
||||
|
||||
```bash
|
||||
# identify current context
|
||||
cmux identify --json
|
||||
|
||||
# open routed to a specific topology target
|
||||
cmux browser open https://example.com --workspace workspace:2 --window window:1 --json
|
||||
```
|
||||
|
||||
Notes:
|
||||
- CLI output defaults to short refs (`surface:N`, `pane:N`, `workspace:N`, `window:N`).
|
||||
- UUIDs are still accepted on input; only request UUID output when needed (`--id-format uuids|both`).
|
||||
- Keep using one `surface:N` per task unless you intentionally switch.
|
||||
|
||||
## Wait Support
|
||||
|
||||
cmux supports wait patterns similar to agent-browser:
|
||||
|
||||
```bash
|
||||
cmux browser <surface> wait --selector "#ready" --timeout-ms 10000
|
||||
cmux browser <surface> wait --text "Success" --timeout-ms 10000
|
||||
cmux browser <surface> wait --url-contains "/dashboard" --timeout-ms 10000
|
||||
cmux browser <surface> wait --load-state complete --timeout-ms 15000
|
||||
cmux browser <surface> wait --function "document.readyState === 'complete'" --timeout-ms 10000
|
||||
```
|
||||
|
||||
## Common Flows
|
||||
|
||||
### Form Submit
|
||||
|
||||
```bash
|
||||
cmux --json browser open https://example.com/signup
|
||||
cmux browser surface:7 get url
|
||||
cmux browser surface:7 wait --load-state complete --timeout-ms 15000
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
cmux browser surface:7 fill e1 "Jane Doe"
|
||||
cmux browser surface:7 fill e2 "jane@example.com"
|
||||
cmux --json browser surface:7 click e3 --snapshot-after
|
||||
cmux browser surface:7 wait --url-contains "/welcome" --timeout-ms 15000
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
### Clear an Input
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 fill e11 "" --snapshot-after --json
|
||||
cmux browser surface:7 get value e11 --json
|
||||
```
|
||||
|
||||
### Stable Agent Loop (Recommended)
|
||||
|
||||
```bash
|
||||
# navigate -> verify -> wait -> snapshot -> action -> snapshot
|
||||
cmux browser surface:7 get url
|
||||
cmux browser surface:7 wait --load-state complete --timeout-ms 15000
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
cmux --json browser surface:7 click e5 --snapshot-after
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
If `get url` is empty or `about:blank`, navigate first instead of waiting on load state.
|
||||
|
||||
## Deep-Dive References
|
||||
|
||||
| Reference | When to Use |
|
||||
|-----------|-------------|
|
||||
| [references/commands.md](references/commands.md) | Full browser command mapping and quick syntax |
|
||||
| [references/snapshot-refs.md](references/snapshot-refs.md) | Ref lifecycle and stale-ref troubleshooting |
|
||||
| [references/authentication.md](references/authentication.md) | Login/OAuth/2FA patterns and state save/load |
|
||||
| [references/authentication.md#saving-authentication-state](references/authentication.md#saving-authentication-state) | Save authenticated state right after login |
|
||||
| [references/session-management.md](references/session-management.md) | Multi-surface isolation and state persistence patterns |
|
||||
| [references/video-recording.md](references/video-recording.md) | Current recording status and practical alternatives |
|
||||
| [references/proxy-support.md](references/proxy-support.md) | Proxy behavior in WKWebView and workarounds |
|
||||
|
||||
## Ready-to-Use Templates
|
||||
|
||||
| Template | Description |
|
||||
|----------|-------------|
|
||||
| [templates/form-automation.sh](templates/form-automation.sh) | Snapshot/ref form fill loop |
|
||||
| [templates/authenticated-session.sh](templates/authenticated-session.sh) | Login once, save/load state |
|
||||
| [templates/capture-workflow.sh](templates/capture-workflow.sh) | Navigate + capture snapshots/screenshots |
|
||||
|
||||
## Limits (WKWebView)
|
||||
|
||||
These commands currently return `not_supported` because they rely on Chrome/CDP-only APIs not exposed by WKWebView:
|
||||
- viewport emulation
|
||||
- offline emulation
|
||||
- trace/screencast recording
|
||||
- network route interception/mocking
|
||||
- low-level raw input injection
|
||||
|
||||
Use supported high-level commands (`click`, `fill`, `press`, `scroll`, `wait`, `snapshot`) instead.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `js_error` on `snapshot --interactive` or `eval`
|
||||
|
||||
Some complex pages can reject or break the JavaScript used for rich snapshots and ad-hoc evaluation.
|
||||
|
||||
Recovery steps:
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 get url
|
||||
cmux browser surface:7 get text body
|
||||
cmux browser surface:7 get html body
|
||||
```
|
||||
|
||||
- Use `get url` first so you know whether the page actually navigated.
|
||||
- Fall back to `get text body` or `get html body` when `snapshot --interactive` or `eval` returns `js_error`.
|
||||
- If the page is still failing, navigate to a simpler intermediate page, then retry the task from there.
|
||||
4
.claude/skills/cmux-browser/agents/openai.yaml
Normal file
4
.claude/skills/cmux-browser/agents/openai.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface:
|
||||
display_name: "cmux Browser"
|
||||
short_description: "Automate cmux webview surfaces with snapshot/ref workflows."
|
||||
default_prompt: "Use this skill for browser automation via cmux CLI: identify target surface, snapshot interactive refs, perform actions, wait for state changes, and re-snapshot to avoid stale refs."
|
||||
122
.claude/skills/cmux-browser/references/authentication.md
Normal file
122
.claude/skills/cmux-browser/references/authentication.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Authentication Patterns
|
||||
|
||||
Login flows, session persistence, OAuth, and 2FA patterns for cmux browser surfaces.
|
||||
|
||||
**Related**: [session-management.md](session-management.md), [SKILL.md](../SKILL.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [Basic Login Flow](#basic-login-flow)
|
||||
- [Saving Authentication State](#saving-authentication-state)
|
||||
- [Restoring Authentication](#restoring-authentication)
|
||||
- [OAuth / SSO Flows](#oauth--sso-flows)
|
||||
- [Two-Factor Authentication](#two-factor-authentication)
|
||||
- [Cookie-Based Auth](#cookie-based-auth)
|
||||
- [Token Refresh Handling](#token-refresh-handling)
|
||||
- [Security Best Practices](#security-best-practices)
|
||||
|
||||
## Basic Login Flow
|
||||
|
||||
```bash
|
||||
cmux browser open https://app.example.com/login --json
|
||||
cmux browser surface:7 wait --load-state complete --timeout-ms 15000
|
||||
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
# [ref=e1] email, [ref=e2] password, [ref=e3] submit
|
||||
|
||||
cmux browser surface:7 fill e1 "user@example.com"
|
||||
cmux browser surface:7 fill e2 "$APP_PASSWORD"
|
||||
cmux browser surface:7 click e3 --snapshot-after --json
|
||||
cmux browser surface:7 wait --url-contains "/dashboard" --timeout-ms 20000
|
||||
```
|
||||
|
||||
## Saving Authentication State
|
||||
|
||||
After logging in, save state for reuse:
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 state save ./auth-state.json
|
||||
```
|
||||
|
||||
State includes cookies, localStorage, sessionStorage, and open tab metadata for that surface.
|
||||
|
||||
## Restoring Authentication
|
||||
|
||||
```bash
|
||||
cmux browser open https://app.example.com --json
|
||||
cmux browser surface:8 state load ./auth-state.json
|
||||
cmux browser surface:8 goto https://app.example.com/dashboard
|
||||
cmux browser surface:8 snapshot --interactive
|
||||
```
|
||||
|
||||
## OAuth / SSO Flows
|
||||
|
||||
```bash
|
||||
cmux browser open https://app.example.com/auth/google --json
|
||||
cmux browser surface:7 wait --url-contains "accounts.google.com" --timeout-ms 30000
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
|
||||
cmux browser surface:7 fill e1 "user@gmail.com"
|
||||
cmux browser surface:7 click e2 --snapshot-after --json
|
||||
|
||||
cmux browser surface:7 wait --url-contains "app.example.com" --timeout-ms 45000
|
||||
cmux browser surface:7 state save ./oauth-state.json
|
||||
```
|
||||
|
||||
## Two-Factor Authentication
|
||||
|
||||
```bash
|
||||
cmux browser open https://app.example.com/login --json
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
cmux browser surface:7 fill e1 "user@example.com"
|
||||
cmux browser surface:7 fill e2 "$APP_PASSWORD"
|
||||
cmux browser surface:7 click e3
|
||||
|
||||
# complete 2FA manually in the webview, then:
|
||||
cmux browser surface:7 wait --url-contains "/dashboard" --timeout-ms 120000
|
||||
cmux browser surface:7 state save ./2fa-state.json
|
||||
```
|
||||
|
||||
## Cookie-Based Auth
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 cookies set session_token "abc123xyz"
|
||||
cmux browser surface:7 goto https://app.example.com/dashboard
|
||||
```
|
||||
|
||||
## Token Refresh Handling
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
STATE_FILE="./auth-state.json"
|
||||
SURFACE="surface:7"
|
||||
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
cmux browser "$SURFACE" state load "$STATE_FILE"
|
||||
fi
|
||||
|
||||
cmux browser "$SURFACE" goto https://app.example.com/dashboard
|
||||
URL=$(cmux browser "$SURFACE" get url)
|
||||
|
||||
if printf '%s' "$URL" | grep -q '/login'; then
|
||||
cmux browser "$SURFACE" snapshot --interactive
|
||||
cmux browser "$SURFACE" fill e1 "$APP_USERNAME"
|
||||
cmux browser "$SURFACE" fill e2 "$APP_PASSWORD"
|
||||
cmux browser "$SURFACE" click e3
|
||||
cmux browser "$SURFACE" wait --url-contains "/dashboard" --timeout-ms 20000
|
||||
cmux browser "$SURFACE" state save "$STATE_FILE"
|
||||
fi
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. Never commit state files (they include auth tokens).
|
||||
2. Use environment variables for credentials.
|
||||
3. Clear state/cookies after sensitive tasks:
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 cookies clear
|
||||
rm -f ./auth-state.json
|
||||
```
|
||||
108
.claude/skills/cmux-browser/references/commands.md
Normal file
108
.claude/skills/cmux-browser/references/commands.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# Command Reference (cmux Browser)
|
||||
|
||||
This maps common `agent-browser` usage to `cmux browser` usage.
|
||||
|
||||
## Direct Equivalents
|
||||
|
||||
- `agent-browser open <url>` -> `cmux browser open <url>`
|
||||
- `agent-browser goto|navigate <url>` -> `cmux browser <surface> goto|navigate <url>`
|
||||
- `agent-browser snapshot -i` -> `cmux browser <surface> snapshot --interactive`
|
||||
- `agent-browser click <ref>` -> `cmux browser <surface> click <ref>`
|
||||
- `agent-browser fill <ref> <text>` -> `cmux browser <surface> fill <ref> <text>`
|
||||
- `agent-browser type <ref> <text>` -> `cmux browser <surface> type <ref> <text>`
|
||||
- `agent-browser select <ref> <value>` -> `cmux browser <surface> select <ref> <value>`
|
||||
- `agent-browser get text <ref>` -> `cmux browser <surface> get text <ref-or-selector>`
|
||||
- `agent-browser get url` -> `cmux browser <surface> get url`
|
||||
- `agent-browser get title` -> `cmux browser <surface> get title`
|
||||
|
||||
## Core Command Groups
|
||||
|
||||
### Navigation
|
||||
|
||||
```bash
|
||||
cmux browser open <url> # opens in caller's workspace (uses CMUX_WORKSPACE_ID)
|
||||
cmux browser open <url> --workspace <id|ref> # opens in a specific workspace
|
||||
cmux browser <surface> goto <url>
|
||||
cmux browser <surface> back|forward|reload
|
||||
cmux browser <surface> get url|title
|
||||
```
|
||||
|
||||
> **Workspace context:** `browser open` targets the workspace of the terminal where the command is run (via `CMUX_WORKSPACE_ID`), even if a different workspace is currently focused. Use `--workspace` to override.
|
||||
|
||||
### Snapshot and Inspection
|
||||
|
||||
```bash
|
||||
cmux browser <surface> snapshot --interactive
|
||||
cmux browser <surface> snapshot --interactive --compact --max-depth 3
|
||||
cmux browser <surface> get text body
|
||||
cmux browser <surface> get html body
|
||||
cmux browser <surface> get value "#email"
|
||||
cmux browser <surface> get attr "#email" --attr placeholder
|
||||
cmux browser <surface> get count ".row"
|
||||
cmux browser <surface> get box "#submit"
|
||||
cmux browser <surface> get styles "#submit" --property color
|
||||
cmux browser <surface> eval '<js>'
|
||||
```
|
||||
|
||||
### Interaction
|
||||
|
||||
```bash
|
||||
cmux browser <surface> click|dblclick|hover|focus <selector-or-ref>
|
||||
cmux browser <surface> fill <selector-or-ref> [text] # empty text clears
|
||||
cmux browser <surface> type <selector-or-ref> <text>
|
||||
cmux browser <surface> press|keydown|keyup <key>
|
||||
cmux browser <surface> select <selector-or-ref> <value>
|
||||
cmux browser <surface> check|uncheck <selector-or-ref>
|
||||
cmux browser <surface> scroll [--selector <css>] [--dx <n>] [--dy <n>]
|
||||
```
|
||||
|
||||
### Wait
|
||||
|
||||
```bash
|
||||
cmux browser <surface> wait --selector "#ready" --timeout-ms 10000
|
||||
cmux browser <surface> wait --text "Done" --timeout-ms 10000
|
||||
cmux browser <surface> wait --url-contains "/dashboard" --timeout-ms 10000
|
||||
cmux browser <surface> wait --load-state complete --timeout-ms 15000
|
||||
cmux browser <surface> wait --function "document.readyState === 'complete'" --timeout-ms 10000
|
||||
```
|
||||
|
||||
### Session/State
|
||||
|
||||
```bash
|
||||
cmux browser <surface> cookies get|set|clear ...
|
||||
cmux browser <surface> storage local|session get|set|clear ...
|
||||
cmux browser <surface> tab list|new|switch|close ...
|
||||
cmux browser <surface> state save|load <path>
|
||||
```
|
||||
|
||||
### Diagnostics
|
||||
|
||||
```bash
|
||||
cmux browser <surface> console list|clear
|
||||
cmux browser <surface> errors list|clear
|
||||
cmux browser <surface> highlight <selector>
|
||||
cmux browser <surface> screenshot
|
||||
cmux browser <surface> download wait --timeout-ms 10000
|
||||
```
|
||||
|
||||
## Agent Reliability Tips
|
||||
|
||||
- Use `--snapshot-after` on mutating actions to return a fresh post-action snapshot.
|
||||
- Re-snapshot after navigation, modal open/close, or major DOM changes.
|
||||
- Prefer short handles in outputs by default (`surface:N`, `pane:N`, `workspace:N`, `window:N`).
|
||||
- Use `--id-format both` only when a UUID must be logged/exported.
|
||||
|
||||
## Known WKWebView Gaps (`not_supported`)
|
||||
|
||||
- `browser.viewport.set`
|
||||
- `browser.geolocation.set`
|
||||
- `browser.offline.set`
|
||||
- `browser.trace.start|stop`
|
||||
- `browser.network.route|unroute|requests`
|
||||
- `browser.screencast.start|stop`
|
||||
- `browser.input_mouse|input_keyboard|input_touch`
|
||||
|
||||
See also:
|
||||
- [snapshot-refs.md](snapshot-refs.md)
|
||||
- [authentication.md](authentication.md)
|
||||
- [session-management.md](session-management.md)
|
||||
37
.claude/skills/cmux-browser/references/proxy-support.md
Normal file
37
.claude/skills/cmux-browser/references/proxy-support.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Proxy Support
|
||||
|
||||
How proxy behavior works for cmux browser automation.
|
||||
|
||||
**Related**: [commands.md](commands.md), [SKILL.md](../SKILL.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [Current Behavior](#current-behavior)
|
||||
- [What Is Not Exposed via CLI](#what-is-not-exposed-via-cli)
|
||||
- [Workarounds](#workarounds)
|
||||
- [Verification](#verification)
|
||||
|
||||
## Current Behavior
|
||||
|
||||
cmux browser uses WKWebView networking. Proxy behavior follows macOS/system networking and app process environment.
|
||||
|
||||
## What Is Not Exposed via CLI
|
||||
|
||||
There is currently no first-class `cmux browser proxy ...` command for per-surface proxy routing.
|
||||
|
||||
Why: WKWebView does not provide CDP-style per-context proxy controls equivalent to Chrome automation stacks.
|
||||
|
||||
## Workarounds
|
||||
|
||||
1. Configure system/network-level proxy for the environment where cmux runs.
|
||||
2. Route traffic through an upstream gateway you control.
|
||||
3. Validate behavior with explicit IP checks.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cmux browser open https://httpbin.org/ip --json
|
||||
cmux browser surface:7 get text body
|
||||
```
|
||||
|
||||
Compare returned IP against expected proxy egress.
|
||||
94
.claude/skills/cmux-browser/references/session-management.md
Normal file
94
.claude/skills/cmux-browser/references/session-management.md
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
# Session Management
|
||||
|
||||
cmux uses isolated browser contexts per surface. Treat each browser surface as its own session.
|
||||
|
||||
**Related**: [authentication.md](authentication.md), [SKILL.md](../SKILL.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [Surface-Based Sessions](#surface-based-sessions)
|
||||
- [Isolation Properties](#isolation-properties)
|
||||
- [State Persistence](#state-persistence)
|
||||
- [Common Patterns](#common-patterns)
|
||||
- [Cleanup](#cleanup)
|
||||
- [Best Practices](#best-practices)
|
||||
|
||||
## Surface-Based Sessions
|
||||
|
||||
```bash
|
||||
# session A
|
||||
cmux browser open https://app.example.com/login --json
|
||||
# -> surface:7
|
||||
|
||||
# session B
|
||||
cmux browser open https://example.com --json
|
||||
# -> surface:8
|
||||
|
||||
cmux browser surface:7 get url
|
||||
cmux browser surface:8 get url
|
||||
```
|
||||
|
||||
## Isolation Properties
|
||||
|
||||
Each surface has independent:
|
||||
- cookies
|
||||
- localStorage/sessionStorage
|
||||
- tab list and active tab
|
||||
- navigation history
|
||||
|
||||
## State Persistence
|
||||
|
||||
### Save State
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 state save /tmp/auth-state.json
|
||||
```
|
||||
|
||||
### Load State
|
||||
|
||||
```bash
|
||||
cmux browser surface:8 state load /tmp/auth-state.json
|
||||
cmux browser surface:8 goto https://app.example.com/dashboard
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Reuse Auth Across New Surface
|
||||
|
||||
```bash
|
||||
cmux browser open https://app.example.com/login --json
|
||||
# login on surface:7 ...
|
||||
cmux browser surface:7 state save /tmp/auth.json
|
||||
|
||||
cmux browser open https://app.example.com --json
|
||||
# assume surface:8
|
||||
cmux browser surface:8 state load /tmp/auth.json
|
||||
cmux browser surface:8 goto https://app.example.com/dashboard
|
||||
```
|
||||
|
||||
### Parallel Multi-Site Tasks
|
||||
|
||||
```bash
|
||||
cmux browser open https://site-a.example --json
|
||||
cmux browser open https://site-b.example --json
|
||||
cmux browser open https://site-c.example --json
|
||||
|
||||
cmux browser surface:11 get text body > /tmp/a.txt
|
||||
cmux browser surface:12 get text body > /tmp/b.txt
|
||||
cmux browser surface:13 get text body > /tmp/c.txt
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
```bash
|
||||
cmux close-surface --surface surface:7
|
||||
cmux close-surface --surface surface:8
|
||||
rm -f /tmp/auth-state.json
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Name/log surfaces in your script output so actions stay attributable.
|
||||
2. Keep one task per surface to avoid ref churn.
|
||||
3. Save state after successful auth milestones.
|
||||
4. Re-snapshot after switching tabs/pages inside a surface.
|
||||
88
.claude/skills/cmux-browser/references/snapshot-refs.md
Normal file
88
.claude/skills/cmux-browser/references/snapshot-refs.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# Snapshot and Refs
|
||||
|
||||
Element refs from snapshots make browser automation compact and reliable.
|
||||
|
||||
**Related**: [commands.md](commands.md), [SKILL.md](../SKILL.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [How Refs Work](#how-refs-work)
|
||||
- [The Snapshot Command](#the-snapshot-command)
|
||||
- [Using Refs](#using-refs)
|
||||
- [Ref Lifecycle](#ref-lifecycle)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## How Refs Work
|
||||
|
||||
Classic flow:
|
||||
|
||||
```text
|
||||
full DOM/HTML -> selector guessing -> action
|
||||
```
|
||||
|
||||
cmux flow:
|
||||
|
||||
```text
|
||||
snapshot -> refs (e1/e2/...) -> direct action
|
||||
```
|
||||
|
||||
## The Snapshot Command
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 snapshot
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
cmux browser surface:7 snapshot --interactive --compact --max-depth 3
|
||||
```
|
||||
|
||||
## Using Refs
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 click e6
|
||||
cmux browser surface:7 fill e10 "user@example.com"
|
||||
cmux browser surface:7 fill e11 "password123"
|
||||
cmux browser surface:7 click e12
|
||||
```
|
||||
|
||||
## Ref Lifecycle
|
||||
|
||||
Refs are invalidated when page structure changes.
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
# e1 is "Next"
|
||||
|
||||
cmux browser surface:7 click e1
|
||||
|
||||
# page changed, take a fresh snapshot
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Snapshot before interacting.
|
||||
2. Re-snapshot after navigation/modal/open-close flows.
|
||||
3. Use `--snapshot-after` on mutating actions.
|
||||
4. Scope snapshots with `--selector` for very large pages.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### not_found / stale ref
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
### Element missing due visibility/timing
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 wait --selector "#target" --timeout-ms 10000
|
||||
cmux browser surface:7 scroll --dy 400
|
||||
cmux browser surface:7 snapshot --interactive
|
||||
```
|
||||
|
||||
### Too many elements
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 snapshot --selector "form#checkout" --interactive
|
||||
```
|
||||
52
.claude/skills/cmux-browser/references/video-recording.md
Normal file
52
.claude/skills/cmux-browser/references/video-recording.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Video Recording
|
||||
|
||||
Status and alternatives for capturing browser automation evidence in cmux.
|
||||
|
||||
**Related**: [commands.md](commands.md), [SKILL.md](../SKILL.md)
|
||||
|
||||
## Contents
|
||||
|
||||
- [Current Status](#current-status)
|
||||
- [Recommended Alternatives](#recommended-alternatives)
|
||||
- [Use Cases](#use-cases)
|
||||
- [Best Practices](#best-practices)
|
||||
|
||||
## Current Status
|
||||
|
||||
`cmux browser` currently does not expose a built-in video recording command.
|
||||
|
||||
Why: cmux browser automation runs on WKWebView, and the agent-browser style recording pipeline is Chrome/CDP-specific.
|
||||
|
||||
## Recommended Alternatives
|
||||
|
||||
### 1. Step Screenshots
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 screenshot > /tmp/step1.b64
|
||||
cmux browser surface:7 click e3 --snapshot-after --json
|
||||
cmux browser surface:7 screenshot > /tmp/step2.b64
|
||||
```
|
||||
|
||||
### 2. Snapshot Timeline
|
||||
|
||||
```bash
|
||||
cmux browser surface:7 snapshot --interactive > /tmp/snap-1.txt
|
||||
cmux browser surface:7 click e3 --snapshot-after --json > /tmp/action-1.json
|
||||
cmux browser surface:7 snapshot --interactive > /tmp/snap-2.txt
|
||||
```
|
||||
|
||||
### 3. macOS Window Capture (external)
|
||||
|
||||
Use an external screen recorder if full-motion capture is required.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- Debug flaky browser automation.
|
||||
- Produce artifacts for CI logs.
|
||||
- Document flow changes between releases.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Capture snapshot before and after each mutating action.
|
||||
2. Add `--snapshot-after` on clicks/fills/types that change state.
|
||||
3. Keep artifacts grouped by timestamp/run id.
|
||||
18
.claude/skills/cmux-browser/templates/authenticated-session.sh
Executable file
18
.claude/skills/cmux-browser/templates/authenticated-session.sh
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SURFACE="${1:-surface:1}"
|
||||
STATE_FILE="${2:-./auth-state.json}"
|
||||
DASHBOARD_URL="${3:-https://app.example.com/dashboard}"
|
||||
|
||||
if [ -f "$STATE_FILE" ]; then
|
||||
cmux browser "$SURFACE" state load "$STATE_FILE"
|
||||
fi
|
||||
|
||||
cmux browser "$SURFACE" goto "$DASHBOARD_URL"
|
||||
cmux browser "$SURFACE" get url
|
||||
cmux browser "$SURFACE" wait --load-state complete --timeout-ms 15000
|
||||
cmux browser "$SURFACE" snapshot --interactive
|
||||
|
||||
echo "If redirected to login, complete login flow then run:"
|
||||
echo " cmux browser $SURFACE state save $STATE_FILE"
|
||||
13
.claude/skills/cmux-browser/templates/capture-workflow.sh
Executable file
13
.claude/skills/cmux-browser/templates/capture-workflow.sh
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SURFACE="${1:-surface:1}"
|
||||
OUT_DIR="${2:-./browser-artifacts}"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
TS="$(date +%Y%m%d-%H%M%S)"
|
||||
cmux browser "$SURFACE" snapshot --interactive > "$OUT_DIR/snapshot-$TS.txt"
|
||||
cmux browser "$SURFACE" screenshot > "$OUT_DIR/screenshot-$TS.b64"
|
||||
|
||||
echo "Wrote: $OUT_DIR/snapshot-$TS.txt"
|
||||
echo "Wrote: $OUT_DIR/screenshot-$TS.b64"
|
||||
12
.claude/skills/cmux-browser/templates/form-automation.sh
Executable file
12
.claude/skills/cmux-browser/templates/form-automation.sh
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
URL="${1:-https://example.com/form}"
|
||||
SURFACE="${2:-surface:1}"
|
||||
|
||||
cmux browser "$SURFACE" goto "$URL"
|
||||
cmux browser "$SURFACE" get url
|
||||
cmux browser "$SURFACE" wait --load-state complete --timeout-ms 15000
|
||||
cmux browser "$SURFACE" snapshot --interactive
|
||||
|
||||
echo "Now run fill/click commands using refs from the snapshot above."
|
||||
51
.claude/skills/cmux-debug-windows/SKILL.md
Normal file
51
.claude/skills/cmux-debug-windows/SKILL.md
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
name: cmux-debug-windows
|
||||
description: Manage cmux debug windows and related debug menu wiring for Sidebar Debug, Background Debug, and Menu Bar Extra Debug. Use this when the user asks to open/tune these debug controls, add or adjust Debug menu entries, or capture/copy a combined debug config snapshot.
|
||||
---
|
||||
|
||||
# cmux Debug Windows
|
||||
|
||||
Keep this workflow focused on existing debug windows and menu entries. Do not add a new utility/debug control window unless the user asks explicitly.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Verify debug menu wiring in `Sources/cmuxApp.swift` under `CommandMenu("Debug")`.
|
||||
- Menu path in app: `Debug` → `Debug Windows` → window entry.
|
||||
- The `Debug` menu only exists in DEBUG builds (`./scripts/reload.sh --tag ...`).
|
||||
- Release builds (`reloadp.sh`, `reloads.sh`) do not show this menu.
|
||||
2. Keep these actions available in `Menu("Debug Windows")`:
|
||||
- `Sidebar Debug…`
|
||||
- `Background Debug…`
|
||||
- `Menu Bar Extra Debug…`
|
||||
- `Open All Debug Windows`
|
||||
3. Reuse existing per-window copy buttons (`Copy Config`) in each debug window before adding new UI.
|
||||
4. For one combined payload, run:
|
||||
```bash
|
||||
skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh --copy
|
||||
```
|
||||
5. After code edits, run build + tagged reload:
|
||||
```bash
|
||||
xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination 'platform=macOS' build
|
||||
./scripts/reload.sh --tag <tag>
|
||||
```
|
||||
|
||||
## Key Files
|
||||
|
||||
- `Sources/cmuxApp.swift`: Debug menu entries and debug window controllers/views.
|
||||
- `Sources/AppDelegate.swift`: Menu bar extra debug settings payload and defaults keys.
|
||||
|
||||
## Script
|
||||
|
||||
- `scripts/debug_windows_snapshot.sh`
|
||||
|
||||
Purpose:
|
||||
- Reads current debug-related defaults values.
|
||||
- Prints one combined snapshot for sidebar/background/menu bar extra.
|
||||
- Optionally copies it to clipboard.
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh
|
||||
skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh --copy
|
||||
skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh --domain <bundle-id> --copy
|
||||
```
|
||||
4
.claude/skills/cmux-debug-windows/agents/openai.yaml
Normal file
4
.claude/skills/cmux-debug-windows/agents/openai.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface:
|
||||
display_name: "cmux Debug Windows"
|
||||
short_description: "Tune cmux debug windows and copy snapshots."
|
||||
default_prompt: "Use this skill to manage cmux sidebar/background/menu-bar debug windows, keep Debug menu wiring clean, and capture copyable debug snapshots."
|
||||
190
.claude/skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh
Executable file
190
.claude/skills/cmux-debug-windows/scripts/debug_windows_snapshot.sh
Executable file
|
|
@ -0,0 +1,190 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: debug_windows_snapshot.sh [--domain <defaults-domain>] [--copy]
|
||||
|
||||
Collect Sidebar Debug, Background Debug, Menu Bar Extra, and Browser DevTools debug values
|
||||
from macOS defaults and print a combined payload. Use --copy to also copy the payload.
|
||||
|
||||
Examples:
|
||||
debug_windows_snapshot.sh
|
||||
debug_windows_snapshot.sh --copy
|
||||
debug_windows_snapshot.sh --domain dev.manaflow.cmux --copy
|
||||
USAGE
|
||||
}
|
||||
|
||||
domain=""
|
||||
copy_flag=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--domain)
|
||||
shift
|
||||
[[ $# -gt 0 ]] || { echo "Missing value for --domain" >&2; exit 1; }
|
||||
domain="$1"
|
||||
;;
|
||||
--copy)
|
||||
copy_flag=1
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
discover_domain() {
|
||||
defaults domains 2>/dev/null \
|
||||
| tr ',' '\n' \
|
||||
| tr -d ' ' \
|
||||
| grep -E 'cmux' \
|
||||
| head -n1 || true
|
||||
}
|
||||
|
||||
read_value() {
|
||||
local key="$1"
|
||||
local fallback="$2"
|
||||
local value
|
||||
value=$(defaults read "$domain" "$key" 2>/dev/null || true)
|
||||
if [[ -z "$value" ]]; then
|
||||
printf '%s' "$fallback"
|
||||
else
|
||||
printf '%s' "$value"
|
||||
fi
|
||||
}
|
||||
|
||||
format_number() {
|
||||
local raw="$1"
|
||||
local precision="$2"
|
||||
if [[ "$raw" =~ ^-?[0-9]+([.][0-9]+)?$ ]]; then
|
||||
printf "%.*f" "$precision" "$raw"
|
||||
else
|
||||
printf "%.*f" "$precision" 0
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -z "$domain" ]]; then
|
||||
domain="$(discover_domain)"
|
||||
fi
|
||||
|
||||
if [[ -z "$domain" ]]; then
|
||||
echo "Could not auto-detect a cmux defaults domain. Pass --domain <bundle-id>." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! defaults domains 2>/dev/null | tr ',' '\n' | tr -d ' ' | grep -Fxq "$domain"; then
|
||||
echo "Defaults domain '$domain' was not found on this machine." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sidebarPreset="$(read_value sidebarPreset nativeSidebar)"
|
||||
sidebarMaterial="$(read_value sidebarMaterial sidebar)"
|
||||
sidebarBlendMode="$(read_value sidebarBlendMode behindWindow)"
|
||||
sidebarState="$(read_value sidebarState followWindow)"
|
||||
sidebarBlurOpacity="$(format_number "$(read_value sidebarBlurOpacity 0.79)" 2)"
|
||||
sidebarTintHex="$(read_value sidebarTintHex '#101010')"
|
||||
sidebarTintOpacity="$(format_number "$(read_value sidebarTintOpacity 0.54)" 2)"
|
||||
sidebarCornerRadius="$(format_number "$(read_value sidebarCornerRadius 0.0)" 1)"
|
||||
sidebarActiveTabIndicatorStyle="$(read_value sidebarActiveTabIndicatorStyle solidFill)"
|
||||
shortcutHintSidebarXOffset="$(format_number "$(read_value shortcutHintSidebarXOffset 0.0)" 1)"
|
||||
shortcutHintSidebarYOffset="$(format_number "$(read_value shortcutHintSidebarYOffset 0.0)" 1)"
|
||||
shortcutHintTitlebarXOffset="$(format_number "$(read_value shortcutHintTitlebarXOffset 4.0)" 1)"
|
||||
shortcutHintTitlebarYOffset="$(format_number "$(read_value shortcutHintTitlebarYOffset 0.0)" 1)"
|
||||
shortcutHintPaneTabXOffset="$(format_number "$(read_value shortcutHintPaneTabXOffset 0.0)" 1)"
|
||||
shortcutHintPaneTabYOffset="$(format_number "$(read_value shortcutHintPaneTabYOffset 0.0)" 1)"
|
||||
shortcutHintAlwaysShow="$(read_value shortcutHintAlwaysShow 0)"
|
||||
|
||||
bgGlassEnabled="$(read_value bgGlassEnabled 1)"
|
||||
bgGlassMaterial="$(read_value bgGlassMaterial hudWindow)"
|
||||
bgGlassTintHex="$(read_value bgGlassTintHex '#000000')"
|
||||
bgGlassTintOpacity="$(format_number "$(read_value bgGlassTintOpacity 0.05)" 2)"
|
||||
|
||||
menubarDebugPreviewEnabled="$(read_value menubarDebugPreviewEnabled 0)"
|
||||
menubarDebugPreviewCount="$(read_value menubarDebugPreviewCount 1)"
|
||||
menubarDebugBadgeRectX="$(format_number "$(read_value menubarDebugBadgeRectX 5.38)" 2)"
|
||||
menubarDebugBadgeRectY="$(format_number "$(read_value menubarDebugBadgeRectY 6.43)" 2)"
|
||||
menubarDebugBadgeRectWidth="$(format_number "$(read_value menubarDebugBadgeRectWidth 10.75)" 2)"
|
||||
menubarDebugBadgeRectHeight="$(format_number "$(read_value menubarDebugBadgeRectHeight 11.58)" 2)"
|
||||
menubarDebugSingleDigitFontSize="$(format_number "$(read_value menubarDebugSingleDigitFontSize 6.70)" 2)"
|
||||
menubarDebugMultiDigitFontSize="$(format_number "$(read_value menubarDebugMultiDigitFontSize 6.70)" 2)"
|
||||
menubarDebugSingleDigitYOffset="$(format_number "$(read_value menubarDebugSingleDigitYOffset 0.60)" 2)"
|
||||
menubarDebugMultiDigitYOffset="$(format_number "$(read_value menubarDebugMultiDigitYOffset 0.60)" 2)"
|
||||
legacySingleDigitX="$(read_value menubarDebugTextRectXAdjust '')"
|
||||
if [[ -n "$legacySingleDigitX" ]]; then
|
||||
menubarDebugSingleDigitXAdjust="$(format_number "$legacySingleDigitX" 2)"
|
||||
else
|
||||
menubarDebugSingleDigitXAdjust="$(format_number "$(read_value menubarDebugSingleDigitXAdjust -1.10)" 2)"
|
||||
fi
|
||||
menubarDebugMultiDigitXAdjust="$(format_number "$(read_value menubarDebugMultiDigitXAdjust 2.42)" 2)"
|
||||
menubarDebugTextRectWidthAdjust="$(format_number "$(read_value menubarDebugTextRectWidthAdjust 1.80)" 2)"
|
||||
|
||||
browserDevToolsIconName="$(read_value browserDevToolsIconName 'wrench.and.screwdriver')"
|
||||
browserDevToolsIconColor="$(read_value browserDevToolsIconColor bonsplitInactive)"
|
||||
|
||||
payload="$(cat <<PAYLOAD
|
||||
# Defaults domain
|
||||
$domain
|
||||
|
||||
# Sidebar Debug
|
||||
sidebarPreset=$sidebarPreset
|
||||
sidebarMaterial=$sidebarMaterial
|
||||
sidebarBlendMode=$sidebarBlendMode
|
||||
sidebarState=$sidebarState
|
||||
sidebarBlurOpacity=$sidebarBlurOpacity
|
||||
sidebarTintHex=$sidebarTintHex
|
||||
sidebarTintOpacity=$sidebarTintOpacity
|
||||
sidebarCornerRadius=$sidebarCornerRadius
|
||||
sidebarActiveTabIndicatorStyle=$sidebarActiveTabIndicatorStyle
|
||||
shortcutHintSidebarXOffset=$shortcutHintSidebarXOffset
|
||||
shortcutHintSidebarYOffset=$shortcutHintSidebarYOffset
|
||||
shortcutHintTitlebarXOffset=$shortcutHintTitlebarXOffset
|
||||
shortcutHintTitlebarYOffset=$shortcutHintTitlebarYOffset
|
||||
shortcutHintPaneTabXOffset=$shortcutHintPaneTabXOffset
|
||||
shortcutHintPaneTabYOffset=$shortcutHintPaneTabYOffset
|
||||
shortcutHintAlwaysShow=$shortcutHintAlwaysShow
|
||||
|
||||
# Background Debug
|
||||
bgGlassEnabled=$bgGlassEnabled
|
||||
bgGlassMaterial=$bgGlassMaterial
|
||||
bgGlassTintHex=$bgGlassTintHex
|
||||
bgGlassTintOpacity=$bgGlassTintOpacity
|
||||
|
||||
# Menu Bar Extra Debug
|
||||
menubarDebugPreviewEnabled=$menubarDebugPreviewEnabled
|
||||
menubarDebugPreviewCount=$menubarDebugPreviewCount
|
||||
menubarDebugBadgeRectX=$menubarDebugBadgeRectX
|
||||
menubarDebugBadgeRectY=$menubarDebugBadgeRectY
|
||||
menubarDebugBadgeRectWidth=$menubarDebugBadgeRectWidth
|
||||
menubarDebugBadgeRectHeight=$menubarDebugBadgeRectHeight
|
||||
menubarDebugSingleDigitFontSize=$menubarDebugSingleDigitFontSize
|
||||
menubarDebugMultiDigitFontSize=$menubarDebugMultiDigitFontSize
|
||||
menubarDebugSingleDigitYOffset=$menubarDebugSingleDigitYOffset
|
||||
menubarDebugMultiDigitYOffset=$menubarDebugMultiDigitYOffset
|
||||
menubarDebugSingleDigitXAdjust=$menubarDebugSingleDigitXAdjust
|
||||
menubarDebugMultiDigitXAdjust=$menubarDebugMultiDigitXAdjust
|
||||
menubarDebugTextRectWidthAdjust=$menubarDebugTextRectWidthAdjust
|
||||
|
||||
# Browser DevTools Button
|
||||
browserDevToolsIconName=$browserDevToolsIconName
|
||||
browserDevToolsIconColor=$browserDevToolsIconColor
|
||||
PAYLOAD
|
||||
)"
|
||||
|
||||
printf '%s\n' "$payload"
|
||||
|
||||
if [[ "$copy_flag" -eq 1 ]]; then
|
||||
if command -v pbcopy >/dev/null 2>&1; then
|
||||
printf '%s' "$payload" | pbcopy
|
||||
echo "Copied debug snapshot to clipboard."
|
||||
else
|
||||
echo "pbcopy not available; skipped clipboard copy." >&2
|
||||
fi
|
||||
fi
|
||||
125
.claude/skills/cmux-markdown/SKILL.md
Normal file
125
.claude/skills/cmux-markdown/SKILL.md
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
---
|
||||
name: cmux-markdown
|
||||
description: Open markdown files in a formatted viewer panel with live reload. Use when you need to display plans, documentation, or notes alongside the terminal with rich rendering (headings, code blocks, tables, lists).
|
||||
---
|
||||
|
||||
# Markdown Viewer with cmux
|
||||
|
||||
Use this skill to display markdown files in a dedicated panel with rich formatting and live file watching.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
1. Write your plan or notes to a `.md` file.
|
||||
2. Open it in a markdown panel.
|
||||
3. The panel auto-updates when the file changes on disk.
|
||||
|
||||
```bash
|
||||
# Open a markdown file as a split panel next to the current terminal
|
||||
cmux markdown open plan.md
|
||||
|
||||
# Absolute path
|
||||
cmux markdown open /path/to/PLAN.md
|
||||
|
||||
# Target a specific workspace
|
||||
cmux markdown open design.md --workspace workspace:2
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
- Displaying an agent plan or task list alongside the terminal
|
||||
- Showing documentation, changelogs, or READMEs while working
|
||||
- Reviewing notes that update in real-time (e.g., a plan file being written by another process)
|
||||
|
||||
## Live File Watching
|
||||
|
||||
The panel automatically re-renders when the file changes on disk. This works with:
|
||||
|
||||
- Direct writes (`echo "..." >> plan.md`)
|
||||
- Editor saves (vim, nano, VS Code)
|
||||
- Atomic file replacement (write to temp, rename over original)
|
||||
- Agent-generated plan files that are updated progressively
|
||||
|
||||
If the file is deleted, the panel shows a "file unavailable" state. During atomic replace, the panel attempts automatic reconnection within its short retry window. If the file returns later, close and reopen the panel.
|
||||
|
||||
## Agent Integration
|
||||
|
||||
### Opening a plan file
|
||||
|
||||
Write your plan to a file, then open it:
|
||||
|
||||
```bash
|
||||
cat > plan.md << 'EOF'
|
||||
# Task Plan
|
||||
|
||||
## Steps
|
||||
1. Analyze the codebase
|
||||
2. Implement the feature
|
||||
3. Write tests
|
||||
4. Verify the build
|
||||
EOF
|
||||
|
||||
cmux markdown open plan.md
|
||||
```
|
||||
|
||||
### Updating a plan in real-time
|
||||
|
||||
The panel live-reloads, so simply overwrite the file as work progresses:
|
||||
|
||||
```bash
|
||||
# The markdown panel updates automatically when the file changes
|
||||
echo "## Step 1: Complete" >> plan.md
|
||||
```
|
||||
|
||||
### Recommended AGENTS.md instruction
|
||||
|
||||
Add this to your project's `AGENTS.md` to instruct coding agents to use the markdown viewer:
|
||||
|
||||
```markdown
|
||||
## Plan Display
|
||||
|
||||
When creating a plan or task list, write it to a `.md` file and open it in cmux:
|
||||
|
||||
cmux markdown open plan.md
|
||||
|
||||
The panel renders markdown with rich formatting and auto-updates when the file changes.
|
||||
```
|
||||
|
||||
## Routing
|
||||
|
||||
```bash
|
||||
# Open in the caller's workspace (default -- uses CMUX_WORKSPACE_ID)
|
||||
cmux markdown open plan.md
|
||||
|
||||
# Open in a specific workspace
|
||||
cmux markdown open plan.md --workspace workspace:2
|
||||
|
||||
# Open splitting from a specific surface
|
||||
cmux markdown open plan.md --surface surface:5
|
||||
|
||||
# Open in a specific window
|
||||
cmux markdown open plan.md --window window:1
|
||||
```
|
||||
|
||||
## Deep-Dive References
|
||||
|
||||
| Reference | When to Use |
|
||||
|-----------|-------------|
|
||||
| [references/commands.md](references/commands.md) | Full command syntax and options |
|
||||
| [references/live-reload.md](references/live-reload.md) | File watching behavior, atomic writes, edge cases |
|
||||
|
||||
## Rendering Support
|
||||
|
||||
The markdown panel renders:
|
||||
|
||||
- Headings (h1-h6) with dividers on h1/h2
|
||||
- Fenced code blocks with monospaced font
|
||||
- Inline code with highlighted background
|
||||
- Tables with alternating row colors
|
||||
- Ordered and unordered lists (nested)
|
||||
- Blockquotes with left border
|
||||
- Bold, italic, strikethrough
|
||||
- Links (clickable)
|
||||
- Horizontal rules
|
||||
- Images (inline)
|
||||
|
||||
Supports both light and dark mode.
|
||||
4
.claude/skills/cmux-markdown/agents/openai.yaml
Normal file
4
.claude/skills/cmux-markdown/agents/openai.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface:
|
||||
display_name: "cmux Markdown Viewer"
|
||||
short_description: "Open markdown files in a formatted panel with live reload alongside the terminal."
|
||||
default_prompt: "Use this skill to display markdown plans, docs, or notes in a cmux panel: write to a .md file, run 'cmux markdown open <path>', and the panel auto-updates when the file changes."
|
||||
69
.claude/skills/cmux-markdown/references/commands.md
Normal file
69
.claude/skills/cmux-markdown/references/commands.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Command Reference (cmux Markdown)
|
||||
|
||||
## Opening a Markdown Panel
|
||||
|
||||
```bash
|
||||
cmux markdown open <path>
|
||||
cmux markdown <path> # shorthand (implicit "open")
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
| Flag | Description | Default |
|
||||
|------|-------------|---------|
|
||||
| `--workspace <id\|ref\|index>` | Target workspace | `$CMUX_WORKSPACE_ID` |
|
||||
| `--surface <id\|ref\|index>` | Source surface to split from | Focused surface |
|
||||
| `--window <id\|ref>` | Target window | Current window |
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
OK surface=surface:8 pane=pane:3 path=/absolute/path/to/file.md
|
||||
```
|
||||
|
||||
With `--json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"window_id": "...",
|
||||
"workspace_id": "...",
|
||||
"pane_id": "...",
|
||||
"surface_id": "...",
|
||||
"path": "/absolute/path/to/file.md"
|
||||
}
|
||||
```
|
||||
|
||||
## Path Resolution
|
||||
|
||||
- Relative paths are resolved against the caller's current working directory.
|
||||
- `~` is expanded to the home directory.
|
||||
- The resolved absolute path is returned in the output.
|
||||
|
||||
```bash
|
||||
# These are equivalent when run from /Users/me/project
|
||||
cmux markdown open plan.md
|
||||
cmux markdown open ./plan.md
|
||||
cmux markdown open /Users/me/project/plan.md
|
||||
```
|
||||
|
||||
## Panel Behavior
|
||||
|
||||
- The panel opens as a **horizontal split** to the right of the source surface.
|
||||
- The tab title shows the filename (e.g., `plan.md`).
|
||||
- The tab icon is a document icon.
|
||||
- Content is **read-only** with text selection enabled.
|
||||
- The file path is displayed as a breadcrumb at the top of the panel.
|
||||
|
||||
## Session Persistence
|
||||
|
||||
Markdown panels are saved and restored across sessions. On restore, the panel re-reads the file from disk. If the file no longer exists at restore time, the panel is not recreated.
|
||||
|
||||
## Help
|
||||
|
||||
```bash
|
||||
cmux markdown --help
|
||||
cmux markdown -h
|
||||
```
|
||||
|
||||
See also:
|
||||
- [live-reload.md](live-reload.md)
|
||||
53
.claude/skills/cmux-markdown/references/live-reload.md
Normal file
53
.claude/skills/cmux-markdown/references/live-reload.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Live Reload Behavior
|
||||
|
||||
The markdown panel watches the file on disk and automatically re-renders when it changes. This enables real-time plan tracking as agents or editors update the file.
|
||||
|
||||
## How It Works
|
||||
|
||||
The panel uses a kernel-level file system watcher (`DispatchSource` with `O_EVTONLY`) that monitors the file for:
|
||||
|
||||
- **Write events** -- content was modified in place
|
||||
- **Extend events** -- content was appended
|
||||
- **Delete events** -- file was removed (atomic replace step 1)
|
||||
- **Rename events** -- file was moved or renamed
|
||||
|
||||
## Supported Write Patterns
|
||||
|
||||
| Pattern | Supported | Notes |
|
||||
|---------|-----------|-------|
|
||||
| Direct write (`echo >>`) | Yes | Triggers write/extend event |
|
||||
| Editor save (vim, nano) | Yes | Most editors use atomic write (see below) |
|
||||
| Atomic replace (write tmp + rename) | Yes | Handled via delete/rename recovery |
|
||||
| `sed -i` | Yes | Uses atomic replace internally |
|
||||
| VS Code / IDE save | Yes | Uses atomic replace |
|
||||
| Agent progressive writes | Yes | Each write triggers a re-render |
|
||||
|
||||
## Atomic File Replacement
|
||||
|
||||
Many editors and tools write files atomically: write to a temporary file, then rename it over the original. This shows up as a **delete** event followed by a new file appearing at the same path.
|
||||
|
||||
The panel handles this by:
|
||||
|
||||
1. Detecting the delete/rename event
|
||||
2. Attempting to re-read the file immediately (in case the rename already happened)
|
||||
3. If the file is missing, wait 500 ms and check again (the new file may not yet be in place)
|
||||
4. Reconnecting the file watcher to the new inode
|
||||
|
||||
## File Unavailable State
|
||||
|
||||
If the file is deleted and does not reappear within the retry window, the panel shows a "file unavailable" state with the original path. The panel does not close automatically -- the user must close it manually.
|
||||
|
||||
If the file later reappears at the same path (e.g., the user recreates it), the panel does NOT automatically reconnect. Close and reopen the panel to pick up the new file.
|
||||
|
||||
## Performance
|
||||
|
||||
- Re-reads are dispatched to the main thread and run synchronously.
|
||||
- Large files (100KB+) may cause brief UI hitches during re-render. For extremely large markdown files, consider splitting into smaller documents.
|
||||
- The file watcher runs on a low-priority background queue and has negligible CPU impact.
|
||||
|
||||
## Tips for Agents
|
||||
|
||||
- **Write the full plan file first, then open it.** This avoids the panel showing a partially written file.
|
||||
- **Append-style updates work well.** Adding sections to the end of a file triggers a smooth re-render.
|
||||
- **Overwriting the entire file is fine.** The atomic replace handling ensures no data is lost.
|
||||
- **Don't delete and recreate rapidly.** If writing a new version, prefer overwriting in place or using atomic replacement.
|
||||
54
.claude/skills/cmux/SKILL.md
Normal file
54
.claude/skills/cmux/SKILL.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
name: cmux
|
||||
description: End-user control of cmux topology and routing (windows, workspaces, panes/surfaces, focus, moves, reorder, identify, trigger flash). Use when automation needs deterministic placement and navigation in a multi-pane cmux layout.
|
||||
---
|
||||
|
||||
# cmux Core Control
|
||||
|
||||
Use this skill to control non-browser cmux topology and routing.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
- Window: top-level macOS cmux window.
|
||||
- Workspace: tab-like group within a window.
|
||||
- Pane: split container in a workspace.
|
||||
- Surface: a tab within a pane (terminal or browser panel).
|
||||
|
||||
## Fast Start
|
||||
|
||||
```bash
|
||||
# identify current caller context
|
||||
cmux identify --json
|
||||
|
||||
# list topology
|
||||
cmux list-windows
|
||||
cmux list-workspaces
|
||||
cmux list-panes
|
||||
cmux list-pane-surfaces --pane pane:1
|
||||
|
||||
# create/focus/move
|
||||
cmux new-workspace
|
||||
cmux new-split right --panel pane:1
|
||||
cmux move-surface --surface surface:7 --pane pane:2 --focus true
|
||||
cmux reorder-surface --surface surface:7 --before surface:3
|
||||
|
||||
# attention cue
|
||||
cmux trigger-flash --surface surface:7
|
||||
```
|
||||
|
||||
## Handle Model
|
||||
|
||||
- Default output uses short refs: `window:N`, `workspace:N`, `pane:N`, `surface:N`.
|
||||
- UUIDs are still accepted as inputs.
|
||||
- Request UUID output only when needed: `--id-format uuids|both`.
|
||||
|
||||
## Deep-Dive References
|
||||
|
||||
| Reference | When to Use |
|
||||
|-----------|-------------|
|
||||
| [references/handles-and-identify.md](references/handles-and-identify.md) | Handle syntax, self-identify, caller targeting |
|
||||
| [references/windows-workspaces.md](references/windows-workspaces.md) | Window/workspace lifecycle and reorder/move |
|
||||
| [references/panes-surfaces.md](references/panes-surfaces.md) | Splits, surfaces, move/reorder, focus routing |
|
||||
| [references/trigger-flash-and-health.md](references/trigger-flash-and-health.md) | Flash cue and surface health checks |
|
||||
| [../cmux-browser/SKILL.md](../cmux-browser/SKILL.md) | Browser automation on surface-backed webviews |
|
||||
| [../cmux-markdown/SKILL.md](../cmux-markdown/SKILL.md) | Markdown viewer panel with live file watching |
|
||||
4
.claude/skills/cmux/agents/openai.yaml
Normal file
4
.claude/skills/cmux/agents/openai.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
interface:
|
||||
display_name: "cmux Core"
|
||||
short_description: "Control windows/workspaces/panes/surfaces and routing with cmux CLI."
|
||||
default_prompt: "Use this skill to inspect and manipulate cmux topology: identify context, target handles, create/focus/move/reorder windows/workspaces/panes/surfaces, and use trigger-flash for visual confirmation."
|
||||
35
.claude/skills/cmux/references/handles-and-identify.md
Normal file
35
.claude/skills/cmux/references/handles-and-identify.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Handles and Identify
|
||||
|
||||
Use `identify` and short handles for deterministic automation targeting.
|
||||
|
||||
## Handle Inputs
|
||||
|
||||
Most v2-backed commands accept:
|
||||
- UUID
|
||||
- short ref (`window:N`, `workspace:N`, `pane:N`, `surface:N`)
|
||||
- index (where legacy/index-based commands still allow it)
|
||||
|
||||
## Self Identify
|
||||
|
||||
```bash
|
||||
cmux identify --json
|
||||
```
|
||||
|
||||
Returns current focused topology plus optional caller resolution.
|
||||
|
||||
## Caller Override
|
||||
|
||||
```bash
|
||||
cmux identify --workspace workspace:2
|
||||
cmux identify --workspace workspace:2 --surface surface:8
|
||||
```
|
||||
|
||||
Useful for agents that need to route relative actions from a known caller anchor.
|
||||
|
||||
## Output Shaping
|
||||
|
||||
```bash
|
||||
cmux --json identify # refs-first output
|
||||
cmux --json --id-format both identify
|
||||
cmux --json --id-format uuids identify
|
||||
```
|
||||
36
.claude/skills/cmux/references/panes-surfaces.md
Normal file
36
.claude/skills/cmux/references/panes-surfaces.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
# Panes and Surfaces
|
||||
|
||||
Split layout, surface creation, focus, move, and reorder.
|
||||
|
||||
## Inspect
|
||||
|
||||
```bash
|
||||
cmux list-panes
|
||||
cmux list-pane-surfaces --pane pane:1
|
||||
```
|
||||
|
||||
## Create Splits/Surfaces
|
||||
|
||||
```bash
|
||||
cmux new-split right --panel pane:1
|
||||
cmux new-surface --type terminal --pane pane:1
|
||||
cmux new-surface --type browser --pane pane:1 --url https://example.com
|
||||
```
|
||||
|
||||
## Focus and Close
|
||||
|
||||
```bash
|
||||
cmux focus-pane --pane pane:2
|
||||
cmux focus-panel --panel surface:7
|
||||
cmux close-surface --surface surface:7
|
||||
```
|
||||
|
||||
## Move/Reorder Surfaces
|
||||
|
||||
```bash
|
||||
cmux move-surface --surface surface:7 --pane pane:2 --focus true
|
||||
cmux move-surface --surface surface:7 --workspace workspace:2 --window window:1 --after surface:4
|
||||
cmux reorder-surface --surface surface:7 --before surface:3
|
||||
```
|
||||
|
||||
Surface identity is stable across move/reorder operations.
|
||||
23
.claude/skills/cmux/references/trigger-flash-and-health.md
Normal file
23
.claude/skills/cmux/references/trigger-flash-and-health.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Trigger Flash and Surface Health
|
||||
|
||||
Operational checks useful in automation loops.
|
||||
|
||||
## Trigger Flash
|
||||
|
||||
Flash a surface or workspace to provide visual confirmation in UI:
|
||||
|
||||
```bash
|
||||
cmux trigger-flash --surface surface:7
|
||||
cmux trigger-flash --workspace workspace:2
|
||||
```
|
||||
|
||||
## Surface Health
|
||||
|
||||
Use health output to detect hidden/detached/non-windowed surfaces:
|
||||
|
||||
```bash
|
||||
cmux surface-health
|
||||
cmux surface-health --workspace workspace:2
|
||||
```
|
||||
|
||||
Use this before routing focused input if UI state may be stale.
|
||||
31
.claude/skills/cmux/references/windows-workspaces.md
Normal file
31
.claude/skills/cmux/references/windows-workspaces.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Windows and Workspaces
|
||||
|
||||
Window/workspace lifecycle and ordering operations.
|
||||
|
||||
## Inspect
|
||||
|
||||
```bash
|
||||
cmux list-windows
|
||||
cmux current-window
|
||||
cmux list-workspaces
|
||||
cmux current-workspace
|
||||
```
|
||||
|
||||
## Create/Focus/Close
|
||||
|
||||
```bash
|
||||
cmux new-window
|
||||
cmux focus-window --window window:2
|
||||
cmux close-window --window window:2
|
||||
|
||||
cmux new-workspace
|
||||
cmux select-workspace --workspace workspace:4
|
||||
cmux close-workspace --workspace workspace:4
|
||||
```
|
||||
|
||||
## Reorder and Move
|
||||
|
||||
```bash
|
||||
cmux reorder-workspace --workspace workspace:4 --before workspace:2
|
||||
cmux move-workspace-to-window --workspace workspace:4 --window window:1
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue