| name | preview-markdown |
|---|---|
| description | Present a markdown file to the user in a separate terminal pane with rich rendering, instead of pasting its content into the conversation. Use when writing a tmp markdown doc for a long explanation, plan, report, or complex point, or when the user says "preview this", "show me the doc", "open it in a pane", or asks to view a markdown file alongside the terminal. Works in herdr, cmux, Ghostty (≥1.3), and iTerm2; falls back gracefully elsewhere. |
When presenting a markdown doc to the user — a long explanation, plan, report, or anything written to a tmp file — open it in a split pane rather than pasting the content into the conversation.
One command discovers the terminal and whether glow is installed:
echo HERDR_ENV=$HERDR_ENV CMUX_WORKSPACE_ID=$CMUX_WORKSPACE_ID TERM_PROGRAM=$TERM_PROGRAM TERM_PROGRAM_VERSION=$TERM_PROGRAM_VERSION GLOW=$(command -v glow || echo missing)Check in priority order — multiplexers run inside a terminal, so they must win over TERM_PROGRAM:
HERDR_ENVset → herdrCMUX_WORKSPACE_IDset → cmuxTERM_PROGRAM=ghosttyand version ≥ 1.3.0 → Ghostty (AppleScript)TERM_PROGRAM=iTerm.app→ iTerm2 (AppleScript)- None of the above → no pane support; give the file path and summarise inline instead
Viewer (matters for every branch except cmux, which renders markdown itself):
GLOWis a path → viewer isglow -t(interactive TUI, live-reloads on file changes)missing→ viewer isless(plain text, no live-reload — tell the user to pressRin the pane after edits). Also advise the user that installing glow gives rendered, live-reloading previews, showing the command:brew install glow. Do not install it (or offer to) unless they explicitly ask you to
One command — opens the doc in a read-only markdown panel split to the right of the focused surface:
cmux markdown open <path>Then:
- The output is
OK surface=surface:N pane=pane:N path=...— capture the surface id for cleanup - The panel live-reloads when the file changes on disk (no viewer needed — cmux renders markdown itself)
- For routing options (
--workspace,--surface,--window) or troubleshooting, load thecmux-markdownskill
Split right of the focused terminal with the viewer as the new surface's command — returns the new terminal's id (a UUID):
osascript <<EOF
tell application "Ghostty"
set cfg to new surface configuration
set command of cfg to "<viewer> '$DOC_PATH'"
set newTerm to split (focused terminal of selected tab of front window) direction right with configuration cfg
return id of newTerm
end tell
EOF- First use triggers a macOS Automation (TCC) prompt — if it fails with a permissions error, tell the user to approve it (or it's disabled via
macos-applescript = falsein their Ghostty config) - The AppleScript API is a preview feature in 1.3 — expect breaking changes in 1.4
- There's no way to read the pane back to verify; trust a clean exit
Split side-by-side ("vertically" = vertical divider) with the viewer as the session command — returns the new session's id:
osascript <<EOF
tell application "iTerm2"
tell current session of current tab of current window
return id of (split vertically with default profile command "<viewer> '$DOC_PATH'")
end tell
end tell
EOF- Same one-time TCC prompt as Ghostty
- The pane may auto-close when the viewer exits (profile-dependent "close on exit")
In every branch, substitute <viewer> with the one detected in step 1 (glow -t or less), and report the captured pane/surface/terminal/session id so it can be closed later. With glow -t, file edits live-reload in the pane automatically — never re-open the pane after editing the doc.
Split a new pane to the right of the focused pane, capture its id, then run the viewer in it:
DOC_PATH=<path>
PANE_ID=$(herdr pane split "$(herdr pane list | jq -r '.result.panes[] | select(.focused) | .pane_id')" --direction right --no-focus | jq -r '.result.pane.pane_id')
herdr pane run "$PANE_ID" "<viewer> '$DOC_PATH'" && echo "$PANE_ID"- Run all three lines in one shell call (variables don't survive across calls)
- Capture the id from the split response as above — do not infer it from
herdr pane list; its ordering doesn't reflect creation order - Verify it rendered:
herdr pane read $PANE_ID | head
When the user is finished with the preview, close the pane (the tmp file can stay unless asked):
# herdr
herdr pane close <pane_id>
# cmux — use the surface id captured from `cmux markdown open`
cmux close-surface --surface <surface_id>
# Ghostty — use the terminal id (UUID) returned by the split; quotes required
osascript -e 'tell application "Ghostty" to close (first terminal whose id is "<terminal_id>")'
# iTerm2 — use the session id returned by the split
osascript -e 'tell application "iTerm2" to close (first session of current tab of current window whose id is "<session_id>")'