Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .claude-plugin/skills/revdiff/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: revdiff
description: Review diffs, files, and documents with inline annotations in a TUI overlay, or answer questions about revdiff usage, configuration, themes, and keybindings. Opens revdiff in tmux/kitty/wezterm/ghostty/iterm2/emacs-vterm, captures annotations, and addresses them. Activates on "revdiff", "review diff", "annotate diff", "git review with revdiff", "interactive diff review", "revdiff all files", "review all files", "browse all files", "revdiff config", "revdiff themes", "revdiff keybindings", "how to configure revdiff", "what themes does revdiff have".
description: Review diffs, files, and documents with inline annotations in a TUI overlay, or answer questions about revdiff usage, configuration, themes, and keybindings. Opens revdiff in tmux/kitty/wezterm/cmux/ghostty/iterm2/emacs-vterm, captures annotations, and addresses them. Activates on "revdiff", "review diff", "annotate diff", "git review with revdiff", "interactive diff review", "revdiff all files", "review all files", "browse all files", "revdiff config", "revdiff themes", "revdiff keybindings", "how to configure revdiff", "what themes does revdiff have".
argument-hint: 'optional: git ref(s), "all files", or file path'
allowed-tools: [Bash, Read, Edit, Write, Grep, Glob]
---
Expand All @@ -26,7 +26,7 @@ If the user asks a question about revdiff (configuration, themes, keybindings, i

## How It Works

1. Launch revdiff in a terminal overlay (tmux popup, kitty overlay, wezterm split-pane, ghostty split+zoom, iTerm2 split pane, or Emacs vterm frame)
1. Launch revdiff in a terminal overlay (tmux popup, kitty overlay, wezterm split-pane, cmux split, ghostty split+zoom, iTerm2 split pane, or Emacs vterm frame)
2. User navigates the diff, adds annotations on specific lines
3. On quit, annotations are captured from stdout
4. Claude reads annotations and addresses each one
Expand Down Expand Up @@ -88,7 +88,7 @@ ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/skills/revdiff/scripts/launch-revdiff.sh [b
```

The script:
- Detects available terminal (tmux → kitty → wezterm → ghostty → iTerm2 → Emacs vterm)
- Detects available terminal (tmux → kitty → wezterm → cmux → ghostty → iTerm2 → Emacs vterm)
- Launches revdiff in an overlay
- Captures annotation output to a temp file
- Prints captured annotations to stdout
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/skills/revdiff/references/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ go install github.com/umputun/revdiff/cmd/revdiff@latest
/plugin install revdiff@umputun-revdiff
```

Use: `/revdiff [base] [against]` — opens review session in a terminal overlay (tmux, kitty, wezterm, ghostty, iTerm2, or Emacs vterm).
Use: `/revdiff [base] [against]` — opens review session in a terminal overlay (tmux, kitty, wezterm, cmux, ghostty, iTerm2, or Emacs vterm).

### Plan Review Plugin

Expand Down
41 changes: 39 additions & 2 deletions .claude-plugin/skills/revdiff/scripts/launch-revdiff.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# launch revdiff in a terminal overlay (tmux/kitty/wezterm/ghostty/iterm2) and capture annotations.
# launch revdiff in a terminal overlay (tmux/kitty/wezterm/cmux/ghostty/iterm2) and capture annotations.
# usage: launch-revdiff.sh [ref] [--staged] [--only=file1 ...]
# output: annotation text from revdiff stdout (empty if no annotations)

Expand Down Expand Up @@ -90,6 +90,43 @@ if [ -n "${WEZTERM_PANE:-}" ] && command -v wezterm >/dev/null 2>&1; then
exit 0
fi

# cmux: split pane via cmux CLI (must precede ghostty — cmux also sets TERM_PROGRAM=ghostty)
if [ -n "${CMUX_SURFACE_ID:-}" ] && command -v cmux >/dev/null 2>&1; then
SENTINEL=$(mktemp /tmp/revdiff-done-XXXXXX)
rm -f "$SENTINEL"

LAUNCH_SCRIPT=$(mktemp /tmp/revdiff-launch-XXXXXX.sh)
trap 'rm -f "$OUTPUT_FILE" "$SENTINEL" "$LAUNCH_SCRIPT"' EXIT
cat > "$LAUNCH_SCRIPT" <<LAUNCHER
#!/bin/sh
$REVDIFF_CMD; touch '$SENTINEL'
LAUNCHER
chmod +x "$LAUNCH_SCRIPT"

# capture new surface ref from "OK surface:N ..." output
CMUX_NEW=$(cmux new-split down 2>&1) || true
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)

# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if [ -n "$CMUX_SURF" ]; then
cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"
else
cmux send "exec $LAUNCH_SCRIPT\n"
fi

while [ ! -f "$SENTINEL" ]; do
sleep 0.3
done
# close the split pane
if [ -n "$CMUX_SURF" ]; then
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
fi
Comment on lines +107 to +124

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cmux path ignores failures from cmux new-split (due to || true) and falls back to untargeted cmux send when the surface ref can't be parsed. This can end up sending exec ... to the wrong surface (potentially the invoking pane) and then blocking forever in the sentinel wait. Consider failing fast if new-split fails or if CMUX_SURF is empty, and add a bounded timeout/cleanup path for the sentinel loop (closing the created surface on error when possible).

Suggested change
CMUX_NEW=$(cmux new-split down 2>&1) || true
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)
# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if [ -n "$CMUX_SURF" ]; then
cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"
else
cmux send "exec $LAUNCH_SCRIPT\n"
fi
while [ ! -f "$SENTINEL" ]; do
sleep 0.3
done
# close the split pane
if [ -n "$CMUX_SURF" ]; then
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
fi
if ! CMUX_NEW=$(cmux new-split down 2>&1); then
echo "error: cmux new-split failed" >&2
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
exit 1
fi
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)
if [ -z "$CMUX_SURF" ]; then
echo "error: failed to parse cmux surface from new-split output: $CMUX_NEW" >&2
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
exit 1
fi
# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if ! cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"; then
echo "error: failed to send launch command to cmux surface $CMUX_SURF" >&2
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
exit 1
fi
CMUX_WAIT_TIMEOUT=60
CMUX_WAIT_ELAPSED=0
while [ ! -f "$SENTINEL" ]; do
if [ "$CMUX_WAIT_ELAPSED" -ge "$CMUX_WAIT_TIMEOUT" ]; then
echo "error: timed out waiting for revdiff to finish in cmux surface $CMUX_SURF" >&2
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
exit 1
fi
sleep 1
CMUX_WAIT_ELAPSED=$((CMUX_WAIT_ELAPSED + 1))
done
# close the split pane
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true

Copilot uses AI. Check for mistakes.
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
cat "$OUTPUT_FILE"
exit 0
fi

# ghostty: split pane via AppleScript (macOS only, requires Ghostty 1.3.0+)
if [ "${TERM_PROGRAM:-}" = "ghostty" ] && command -v osascript >/dev/null 2>&1; then

Expand Down Expand Up @@ -280,5 +317,5 @@ LAUNCHER
exit 0
fi

echo "error: no overlay terminal available (requires tmux, kitty, wezterm, ghostty, iTerm2, or emacs vterm)" >&2
echo "error: no overlay terminal available (requires tmux, kitty, wezterm, cmux, ghostty, iTerm2, or emacs vterm)" >&2
exit 1
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,14 @@ The plugin requires one of the following terminals since Claude Code itself cann
| **tmux** | `display-popup` (blocks until quit) | `$TMUX` env var |
| **kitty** | `kitty @ launch --type=overlay` | `$KITTY_LISTEN_ON` env var |
| **wezterm** | `wezterm cli split-pane` | `$WEZTERM_PANE` env var |
| **cmux** | `cmux new-split` + `cmux send` | `$CMUX_SURFACE_ID` env var |
| **ghostty** | AppleScript split + zoom (macOS only) | `$TERM_PROGRAM` + AppleScript probe |
| **iTerm2** | `osascript` split pane (macOS only) | `$ITERM_SESSION_ID` env var |
| **Emacs vterm** | New frame via `emacsclient` | `$INSIDE_EMACS` env var |

Priority: tmux → kitty → wezterm → ghostty → iTerm2 → Emacs vterm (first detected wins). If none are available, the plugin exits with an error.
Priority: tmux → kitty → wezterm → cmux → ghostty → iTerm2 → Emacs vterm (first detected wins). If none are available, the plugin exits with an error.

> **Note:** cmux is detected before ghostty because cmux also sets `$TERM_PROGRAM=ghostty`. The cmux block uses the cmux CLI (`new-split` + `send --surface`) instead of Ghostty's AppleScript API.

> **Note:** iTerm2 uses a split pane (vertical or horizontal, auto-detected from terminal dimensions) rather than a full-screen overlay. The iTerm2 AppleScript API does not expose a zoom command, so the split view shares screen space with the invoking session.

Expand Down
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ revdiff is a terminal-based UI tool for code review and document annotation. It

Install: `/plugin marketplace add umputun/revdiff && /plugin install revdiff@umputun-revdiff`

Supports tmux, kitty, wezterm, ghostty, and iTerm2 terminal overlays.
Supports tmux, kitty, wezterm, cmux, ghostty, and iTerm2 terminal overlays.

Usage: `/revdiff HEAD~1` or natural language like "review diff against main"

Expand Down
37 changes: 36 additions & 1 deletion plugins/revdiff-planning/scripts/launch-plan-review.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,41 @@ if [ -n "${WEZTERM_PANE:-}" ] && command -v wezterm >/dev/null 2>&1; then
exit 0
fi

# cmux: split pane via cmux CLI (must precede ghostty — cmux also sets TERM_PROGRAM=ghostty)
if [ -n "${CMUX_SURFACE_ID:-}" ] && command -v cmux >/dev/null 2>&1; then
SENTINEL=$(mktemp /tmp/plan-review-done-XXXXXX)
rm -f "$SENTINEL"

LAUNCH_SCRIPT=$(mktemp /tmp/plan-review-launch-XXXXXX.sh)
trap 'rm -f "$OUTPUT_FILE" "$SENTINEL" "$LAUNCH_SCRIPT"' EXIT
cat > "$LAUNCH_SCRIPT" <<LAUNCHER
#!/bin/sh
$REVDIFF_CMD; touch '$SENTINEL'
LAUNCHER
chmod +x "$LAUNCH_SCRIPT"

CMUX_NEW=$(cmux new-split down 2>&1) || true
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)

# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if [ -n "$CMUX_SURF" ]; then
cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"
else
cmux send "exec $LAUNCH_SCRIPT\n"
fi

while [ ! -f "$SENTINEL" ]; do
sleep 0.3
done
if [ -n "$CMUX_SURF" ]; then
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
fi
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
Comment on lines +81 to +111

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cmux path ignores failures from cmux new-split (due to || true) and falls back to untargeted cmux send when the surface ref can't be parsed. This can send exec ... to the wrong pane and then block forever in the sentinel wait. Consider checking new-split exit status / expected output, requiring a parsed CMUX_SURF before sending, and adding a timeout + cleanup path for the sentinel loop (including close-surface if a surface was created).

Suggested change
# cmux: split pane via cmux CLI (must precede ghostty — cmux also sets TERM_PROGRAM=ghostty)
if [ -n "${CMUX_SURFACE_ID:-}" ] && command -v cmux >/dev/null 2>&1; then
SENTINEL=$(mktemp /tmp/plan-review-done-XXXXXX)
rm -f "$SENTINEL"
LAUNCH_SCRIPT=$(mktemp /tmp/plan-review-launch-XXXXXX.sh)
trap 'rm -f "$OUTPUT_FILE" "$SENTINEL" "$LAUNCH_SCRIPT"' EXIT
cat > "$LAUNCH_SCRIPT" <<LAUNCHER
#!/bin/sh
$REVDIFF_CMD; touch '$SENTINEL'
LAUNCHER
chmod +x "$LAUNCH_SCRIPT"
CMUX_NEW=$(cmux new-split down 2>&1) || true
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)
# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if [ -n "$CMUX_SURF" ]; then
cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"
else
cmux send "exec $LAUNCH_SCRIPT\n"
fi
while [ ! -f "$SENTINEL" ]; do
sleep 0.3
done
if [ -n "$CMUX_SURF" ]; then
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
fi
rm -f "$SENTINEL" "$LAUNCH_SCRIPT"
cleanup_cmux_launch() {
rm -f "${OUTPUT_FILE:-}" "${SENTINEL:-}" "${LAUNCH_SCRIPT:-}"
if [ -n "${CMUX_SURF:-}" ]; then
cmux close-surface --surface "$CMUX_SURF" 2>/dev/null || true
CMUX_SURF=""
fi
}
# cmux: split pane via cmux CLI (must precede ghostty — cmux also sets TERM_PROGRAM=ghostty)
if [ -n "${CMUX_SURFACE_ID:-}" ] && command -v cmux >/dev/null 2>&1; then
SENTINEL=$(mktemp /tmp/plan-review-done-XXXXXX)
rm -f "$SENTINEL"
LAUNCH_SCRIPT=$(mktemp /tmp/plan-review-launch-XXXXXX.sh)
CMUX_SURF=""
trap 'cleanup_cmux_launch' EXIT
cat > "$LAUNCH_SCRIPT" <<LAUNCHER
#!/bin/sh
$REVDIFF_CMD; touch '$SENTINEL'
LAUNCHER
chmod +x "$LAUNCH_SCRIPT"
if ! CMUX_NEW=$(cmux new-split down 2>&1); then
echo "error: cmux new-split failed: $CMUX_NEW" >&2
exit 1
fi
CMUX_SURF=$(echo "$CMUX_NEW" | grep -o 'surface:[0-9]*' | head -1)
if [ -z "$CMUX_SURF" ]; then
echo "error: cmux new-split did not return a surface reference: $CMUX_NEW" >&2
exit 1
fi
# send exec command immediately — the pty input buffer holds the text
# until the new pane's shell finishes initializing and reads it
if ! cmux send --surface "$CMUX_SURF" "exec $LAUNCH_SCRIPT\n"; then
echo "error: failed to send launch command to cmux surface $CMUX_SURF" >&2
exit 1
fi
CMUX_WAIT_TIMEOUT=60
CMUX_WAIT_START=$SECONDS
while [ ! -f "$SENTINEL" ]; do
if [ $((SECONDS - CMUX_WAIT_START)) -ge "$CMUX_WAIT_TIMEOUT" ]; then
echo "error: timed out waiting for cmux launch to finish" >&2
exit 1
fi
sleep 0.3
done
cleanup_cmux_launch
trap - EXIT

Copilot uses AI. Check for mistakes.
cat "$OUTPUT_FILE"
exit 0
fi

# ghostty: split pane via AppleScript (macOS only, requires Ghostty 1.3.0+)
if [ "${TERM_PROGRAM:-}" = "ghostty" ] && command -v osascript >/dev/null 2>&1; then

Expand Down Expand Up @@ -257,5 +292,5 @@ LAUNCHER
exit 0
fi

echo "error: no overlay terminal available (requires tmux, kitty, wezterm, ghostty, iTerm2, or emacs vterm)" >&2
echo "error: no overlay terminal available (requires tmux, kitty, wezterm, cmux, ghostty, iTerm2, or emacs vterm)" >&2
exit 1
2 changes: 1 addition & 1 deletion plugins/revdiff-planning/scripts/plan-review-hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

requirements:
- revdiff binary in PATH
- tmux, kitty, wezterm, or ghostty (macOS) terminal
- tmux, kitty, wezterm, cmux, or ghostty (macOS) terminal
"""

import json
Expand Down
3 changes: 2 additions & 1 deletion site/docs.html
Original file line number Diff line number Diff line change
Expand Up @@ -351,12 +351,13 @@ <h2 id="plugin-terminals">Terminal support</h2>
<tr><td><strong>tmux</strong></td><td><code>display-popup</code> (blocks until quit)</td><td><code>$TMUX</code></td></tr>
<tr><td><strong>kitty</strong></td><td><code>kitty @ launch --type=overlay</code></td><td><code>$KITTY_LISTEN_ON</code></td></tr>
<tr><td><strong>wezterm</strong></td><td><code>wezterm cli split-pane</code></td><td><code>$WEZTERM_PANE</code></td></tr>
<tr><td><strong>cmux</strong></td><td><code>cmux new-split</code> + <code>cmux send</code></td><td><code>$CMUX_SURFACE_ID</code></td></tr>
<tr><td><strong>ghostty</strong></td><td>AppleScript split + zoom (macOS only)</td><td><code>$TERM_PROGRAM</code></td></tr>
<tr><td><strong>iTerm2</strong></td><td>AppleScript split pane (macOS only)</td><td><code>$ITERM_SESSION_ID</code></td></tr>
<tr><td><strong>Emacs vterm</strong></td><td>New frame via <code>emacsclient</code></td><td><code>$INSIDE_EMACS</code></td></tr>
</tbody>
</table>
<p>Priority: tmux &rarr; kitty &rarr; wezterm &rarr; ghostty &rarr; iTerm2 &rarr; Emacs vterm (first detected wins). iTerm2 uses a split pane rather than a full-screen overlay.</p>
<p>Priority: tmux &rarr; kitty &rarr; wezterm &rarr; cmux &rarr; ghostty &rarr; iTerm2 &rarr; Emacs vterm (first detected wins). cmux is checked before ghostty because it also sets <code>$TERM_PROGRAM=ghostty</code>. iTerm2 uses a split pane rather than a full-screen overlay.</p>

<h2 id="plugin-usage">Plugin usage</h2>
<h3>Slash commands</h3>
Expand Down
7 changes: 6 additions & 1 deletion site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ <h2>Works with your terminal</h2>
<div class="terminal-method"><code>wezterm cli split-pane</code></div>
<div class="terminal-detect"><code>$WEZTERM_PANE</code></div>
</div>
<div class="terminal-card">
<div class="terminal-name">cmux</div>
<div class="terminal-method"><code>cmux new-split</code> + <code>cmux send</code></div>
<div class="terminal-detect"><code>$CMUX_SURFACE_ID</code></div>
</div>
<div class="terminal-card">
<div class="terminal-name">ghostty</div>
<div class="terminal-method">AppleScript split + zoom</div>
Expand All @@ -212,7 +217,7 @@ <h2>Works with your terminal</h2>
<div class="terminal-detect"><code>$INSIDE_EMACS</code></div>
</div>
</div>
<p class="terminal-priority">Priority: tmux &rarr; kitty &rarr; wezterm &rarr; ghostty &rarr; iTerm2 &rarr; Emacs vterm</p>
<p class="terminal-priority">Priority: tmux &rarr; kitty &rarr; wezterm &rarr; cmux &rarr; ghostty &rarr; iTerm2 &rarr; Emacs vterm</p>
</div>
</section>

Expand Down
2 changes: 1 addition & 1 deletion site/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ revdiff is a terminal-based UI tool for code review and document annotation. It

Install: `/plugin marketplace add umputun/revdiff && /plugin install revdiff@umputun-revdiff`

Supports tmux, kitty, wezterm, ghostty, and iTerm2 terminal overlays.
Supports tmux, kitty, wezterm, cmux, ghostty, and iTerm2 terminal overlays.

Usage: `/revdiff HEAD~1` or natural language like "review diff against main"

Expand Down
Loading