claw-mo-open
Add a specific file, directory, or piped markdown content to the running mo server and open the browser directly on that file.
For config schema, HTTP API, deep-linking, and browser opening: read ${CLAUDE_PLUGIN_ROOT}/references/shared.md
Arguments
$ARGUMENTS — one of:
- File path:
docs/spec.md
- Directory:
docs/ (added as a watch pattern)
- or --stdin: read markdown from stdin (e.g., cat notes.md | /claw-mo-open -)
- Optional
--group <name> to pin the target group (otherwise auto-detected)
- Examples:
/claw-mo-open docs/spec.md
/claw-mo-open plans/ --group plans
some-tool | /claw-mo-open - --group scratch
Steps
Parse $ARGUMENTS:
- Detect
--stdin or a bare - → stdin mode
- Otherwise resolve file/dir path with
realpath
- Extract
--group; if absent, auto-detect from config (match the file path against each group's patterns, fall back to default)
Prerequisites: command -v mo >/dev/null 2>&1.
Project key: git rev-parse --show-toplevel (fallback: $PWD).
Read config from ${CLAUDE_PLUGIN_DATA}/config.json.
Check running server: mo --status --json → is a server on $PORT?
If server is running AND config exists — verify sync before mutating:
- Compare live group→patterns to saved config (see
shared.md)
- If drifted: tell the user that
/claw-mo-up would clear+rebuild this runtime; ask whether to resync first or add into the drifted runtime
- If they resync: clear, restart from config, then continue
- If they decline: continue but warn the change won't survive the next
/claw-mo-up unless config is updated
Add the content based on mode:
(a) File path, server running:
curl -s -X POST "http://localhost:$PORT/_/api/groups/$GROUP/files" \
-H 'Content-Type: application/json' \
-d "{\"path\": \"$(realpath file.md)\"}"
(b) Directory, server running:
curl -s -X POST "http://localhost:$PORT/_/api/patterns" \
-H 'Content-Type: application/json' \
-d "{\"pattern\": \"$(realpath dir)/**/*.md\", \"group\": \"$GROUP\"}"
(c) Stdin, server running: mo's single-instance detection pushes stdin content to the running server automatically:
cat | mo --no-open -p $PORT -t "$GROUP"
(d) Server NOT running:
- Has config → perform
/claw-mo-up behavior first (restart if server reappears mid-flight), then retry step 7
- No config → quick-open with auto-config:
- Auto-assign port:
echo $((6300 + $(echo "$PROJECT_ROOT" | cksum | cut -d' ' -f1) % 100))
- Start mo:
- File:
mo --no-open -p $PORT "$(realpath file.md)"
- Dir:
mo --no-open -p $PORT "$(realpath dir)" (mo converts dir → dir/*.md watch)
- Stdin:
cat | mo --no-open -p $PORT
- Save minimal config to
${CLAUDE_PLUGIN_DATA}/config.json:{ "PROJECT_ROOT": { "port": PORT, "groups": { "default": ["*.md"] } } }
- Tell the user: "Saved a minimal config. Run
/claw-mo-setup to customize watch groups."
Resolve deep-link URL — try to open directly on the added file, not just the group:
File mode:
FILE_ID=$(curl -s "http://localhost:$PORT/_/api/groups" | python3 -c "
import sys, json, os
target = '$(realpath file.md)'
for g in json.load(sys.stdin):
for f in g.get('files', []):
if f.get('path') == target:
print(f['id']); break
")
URL="http://localhost:$PORT/$GROUP"
[ -n "$FILE_ID" ] && URL="$URL?file=$FILE_ID"
Stdin mode: mo generates stdin-<hash>.md. Look up its ID the same way, matching by name prefix stdin-.
Directory mode: no single file to link to; use http://localhost:$PORT/$GROUP.
Open browser using the cmux-preferred logic from shared.md (reuse existing surface if one targets this port).
Report — what was added (path or stdin-<hash>.md), which group, whether the session was reused/resynced, the resolved URL.
Gotchas
- HTTP API needs absolute paths — always
realpath before sending.
- Compare live to config before reusing a running session — matching port or group names alone doesn't prove correctness.
- Drift warning is a feature, not a chore: runtime-only additions get wiped by the next
/claw-mo-up unless persisted through /claw-mo-setup or /claw-mo-manage.
- Directory args ≠ watch patterns at the API layer: the HTTP API requires you to form the glob (
dir/**/*.md) explicitly. mo's CLI does that conversion for free when you pass a dir as a positional arg — that's why quick-open uses the CLI form.
- Deep-linking falls back gracefully: if the file ID lookup fails (file just added, async), opening
/GROUP still works and the new file will appear in the sidebar via SSE.
- Stdin content dedupes by hash: piping the same content twice reuses the existing entry (
stdin-<hash>.md).
cat | mo requires stdin to not be a TTY. Fine when a pipe is present; fails silently if Claude somehow passes an interactive stdin. The shell pipe cat | mo forces non-TTY.
- Quick-open saves a minimal config so
/claw-mo-up, /claw-mo-down, and /claw-mo-manage can find the session.
- If the group doesn't exist in mo yet, the API creates it. The group name becomes a URL path segment — keep it simple lowercase.
- Adding a file already in mo is safe — dedup by path.
1---2name: claw-mo-open3description: Open a specific markdown file, directory, or piped content in mo. Use when the user wants to quickly view one markdown file, add a file to mo, pipe markdown to the viewer, or preview a specific document.4---56# claw-mo-open78Add a specific file, directory, or piped markdown content to the running mo server and open the browser directly on that file.910For config schema, HTTP API, deep-linking, and browser opening: read `${CLAUDE_PLUGIN_ROOT}/references/shared.md`1112## Arguments1314- `$ARGUMENTS` — one of:15 - File path: `docs/spec.md`16 - Directory: `docs/` (added as a watch pattern)17 - `-` or `--stdin`: read markdown from stdin (e.g., `cat notes.md | /claw-mo-open -`)18 - Optional `--group <name>` to pin the target group (otherwise auto-detected)19- Examples:20 - `/claw-mo-open docs/spec.md`21 - `/claw-mo-open plans/ --group plans`22 - `some-tool | /claw-mo-open - --group scratch`2324## Steps25261. **Parse `$ARGUMENTS`**:27 - Detect `--stdin` or a bare `-` → stdin mode28 - Otherwise resolve file/dir path with `realpath`29 - Extract `--group`; if absent, auto-detect from config (match the file path against each group's patterns, fall back to `default`)30312. **Prerequisites**: `command -v mo >/dev/null 2>&1`.32333. **Project key**: `git rev-parse --show-toplevel` (fallback: `$PWD`).34354. **Read config** from `${CLAUDE_PLUGIN_DATA}/config.json`.36375. **Check running server**: `mo --status --json` → is a server on `$PORT`?38396. **If server is running AND config exists** — verify sync before mutating:40 - Compare live group→patterns to saved config (see `shared.md`)41 - If drifted: tell the user that `/claw-mo-up` would clear+rebuild this runtime; ask whether to resync first or add into the drifted runtime42 - If they resync: clear, restart from config, then continue43 - If they decline: continue but warn the change won't survive the next `/claw-mo-up` unless config is updated44457. **Add the content** based on mode:4647 **(a) File path, server running**:48 ```bash49 curl -s -X POST "http://localhost:$PORT/_/api/groups/$GROUP/files" \50 -H 'Content-Type: application/json' \51 -d "{\"path\": \"$(realpath file.md)\"}"52 ```5354 **(b) Directory, server running**:55 ```bash56 curl -s -X POST "http://localhost:$PORT/_/api/patterns" \57 -H 'Content-Type: application/json' \58 -d "{\"pattern\": \"$(realpath dir)/**/*.md\", \"group\": \"$GROUP\"}"59 ```6061 **(c) Stdin, server running**: mo's single-instance detection pushes stdin content to the running server automatically:62 ```bash63 cat | mo --no-open -p $PORT -t "$GROUP"64 ```6566 **(d) Server NOT running**:67 - Has config → perform `/claw-mo-up` behavior first (restart if server reappears mid-flight), then retry step 768 - No config → **quick-open with auto-config**:69 1. Auto-assign port: `echo $((6300 + $(echo "$PROJECT_ROOT" | cksum | cut -d' ' -f1) % 100))`70 2. Start mo:71 - File: `mo --no-open -p $PORT "$(realpath file.md)"`72 - Dir: `mo --no-open -p $PORT "$(realpath dir)"` (mo converts dir → `dir/*.md` watch)73 - Stdin: `cat | mo --no-open -p $PORT`74 3. Save minimal config to `${CLAUDE_PLUGIN_DATA}/config.json`:75 ```json76 { "PROJECT_ROOT": { "port": PORT, "groups": { "default": ["*.md"] } } }77 ```78 4. Tell the user: "Saved a minimal config. Run `/claw-mo-setup` to customize watch groups."79808. **Resolve deep-link URL** — try to open directly on the added file, not just the group:8182 **File mode**:83 ```bash84 FILE_ID=$(curl -s "http://localhost:$PORT/_/api/groups" | python3 -c "85 import sys, json, os86 target = '$(realpath file.md)'87 for g in json.load(sys.stdin):88 for f in g.get('files', []):89 if f.get('path') == target:90 print(f['id']); break91 ")92 URL="http://localhost:$PORT/$GROUP"93 [ -n "$FILE_ID" ] && URL="$URL?file=$FILE_ID"94 ```9596 **Stdin mode**: mo generates `stdin-<hash>.md`. Look up its ID the same way, matching by name prefix `stdin-`.9798 **Directory mode**: no single file to link to; use `http://localhost:$PORT/$GROUP`.991009. **Open browser** using the cmux-preferred logic from `shared.md` (reuse existing surface if one targets this port).10110210. **Report** — what was added (path or `stdin-<hash>.md`), which group, whether the session was reused/resynced, the resolved URL.103104## Gotchas105106- **HTTP API needs absolute paths** — always `realpath` before sending.107- **Compare live to config before reusing a running session** — matching port or group names alone doesn't prove correctness.108- **Drift warning is a feature**, not a chore: runtime-only additions get wiped by the next `/claw-mo-up` unless persisted through `/claw-mo-setup` or `/claw-mo-manage`.109- **Directory args ≠ watch patterns at the API layer**: the HTTP API requires you to form the glob (`dir/**/*.md`) explicitly. mo's CLI does that conversion for free when you pass a dir as a positional arg — that's why quick-open uses the CLI form.110- **Deep-linking falls back gracefully**: if the file ID lookup fails (file just added, async), opening `/GROUP` still works and the new file will appear in the sidebar via SSE.111- **Stdin content dedupes by hash**: piping the same content twice reuses the existing entry (`stdin-<hash>.md`).112- **`cat | mo` requires stdin to not be a TTY**. Fine when a pipe is present; fails silently if Claude somehow passes an interactive stdin. The shell pipe `cat | mo` forces non-TTY.113- **Quick-open saves a minimal config** so `/claw-mo-up`, `/claw-mo-down`, and `/claw-mo-manage` can find the session.114- **If the group doesn't exist in mo yet**, the API creates it. The group name becomes a URL path segment — keep it simple lowercase.115- **Adding a file already in mo is safe** — dedup by path.