Skip to content

fix: capture-pane CLI expands POSIX short flag clusters#326

Closed
quazardous wants to merge 1 commit into
psmux:masterfrom
quazardous:fix/cli-capture-pane-short-cluster
Closed

fix: capture-pane CLI expands POSIX short flag clusters#326
quazardous wants to merge 1 commit into
psmux:masterfrom
quazardous:fix/cli-capture-pane-short-cluster

Conversation

@quazardous

Copy link
Copy Markdown
Contributor

Summary

psmux capture-pane -ep -t <target> returned exit 0 with empty stdout, while psmux capture-pane -e -p -t <target> (same flags, separated) worked correctly.

Root cause

The CLI dispatcher for capture-pane in main.rs matched flag strings exactly ("-p", "-e", "-J"), so a POSIX short flag cluster like -ep or -pe fell through to _ => {} and was silently dropped. As a result print_stdout stayed false and the command was routed to the fire-and-forget send_control branch — no response was printed, hence the empty stdout and exit 0.

Fix

Add a match arm that detects an argument of the form -XYZ whose every character after the dash is one of the capture-pane boolean shorts (p, e, J), and emit each flag individually. Value-taking flags (-t, -S, -E, -b) take a following argument and are not eligible for clustering — they stay on their existing arms.

Smoke matrix (attached 29×120 session)

Form Before After
-p 1281 1281
-e -p 2312 2312
-ep 0 2312
-pe 0 2312

Zero regression on the separated forms; the cluster form now matches the separated-flag output and contains ANSI SGR escapes as expected for -e.

Test plan

  • cargo build --release clean
  • Repro confirmed pre-patch (-ep/-pe → 0 bytes ; -p/-e -p → text)
  • 4-form smoke matrix verified post-patch
  • Adding a Rust unit test for the cluster expansion would be a reasonable follow-up; current behavior validated by the integration smoke above

`psmux capture-pane -ep -t <target>` returned exit 0 with empty stdout,
while `psmux capture-pane -e -p -t <target>` (same flags, separated)
worked correctly.

Root cause: the CLI dispatcher for `capture-pane` in `main.rs` matched
flag strings exactly (`"-p"`, `"-e"`, `"-J"`), so a POSIX short flag
cluster like `-ep` or `-pe` fell through to `_ => {}` and was silently
dropped. As a result `print_stdout` stayed `false` and the command was
routed to the fire-and-forget `send_control` branch — no response was
printed, hence the empty stdout and exit 0.

Fix: add a `match` arm that detects an argument of the form `-XYZ`
whose every character after the dash is one of the capture-pane
boolean shorts (`p`, `e`, `J`), and emit each flag individually.
Value-taking flags (`-t`, `-S`, `-E`, `-b`) take a following argument
and are not eligible for clustering — they stay on their existing
arms.

Smoke matrix (attached 29x120 session):

  | Form    | Before | After |
  |---------|--------|-------|
  | -p      |  1281  |  1281 |
  | -e -p   |  2312  |  2312 |
  | -ep     |     0  |  2312 |
  | -pe     |     0  |  2312 |

Zero regression on the separated forms; the cluster form now matches
the separated-flag output and contains ANSI SGR escapes as expected
for -e.
psmux added a commit that referenced this pull request May 27, 2026
The CLI dispatcher for capture-pane matched flag strings exactly (-p,
-e, -J), so a POSIX short flag cluster like -ep or -pe fell through to
the wildcard arm and was silently dropped. As a result print_stdout
stayed false and capture-pane returned empty stdout with exit 0.

Add a match arm that detects arguments of the form -XYZ whose every
character after the dash is one of the capture-pane boolean shorts
(p, e, J), and emits each flag individually. Value-taking flags
(-t, -S, -E, -b) take a following argument and are not eligible for
clustering.

This is a tmux parity fix: tmux's args_parse_flags() uses a
character-by-character loop that naturally supports POSIX flag
clustering for all commands.

19 E2E tests pass, 2110 Rust tests pass (0 regressions).

Co-authored-by: David Berlioz <berliozdavid@gmail.com>
@psmux

psmux commented May 27, 2026

Copy link
Copy Markdown
Owner

Hey @quazardous, another solid catch. Applied in 72d08aa with your Co-authored-by credit.

Bug confirmed before patch:

-p=214 -ep=0 -pe=0 -e -p=432

POSIX flag clusters silently fell through to _ => {} in the CLI dispatcher, leaving print_stdout = false and routing to the fire-and-forget branch. Empty stdout, exit 0. Nasty because it looks like success.

After patch:

-p=130 -ep=330 -pe=330 -e -p=330 -pJ=130 -epJ=330

All cluster combinations now match their separated equivalents.

What I verified (19/19 E2E tests pass):

  • -ep, -pe, -pJ, -Jp, -epJ, -Jpe all produce identical output to their separated forms
  • Content integrity: marker text present in all cluster outputs
  • ANSI SGR escapes present when -e is in the cluster
  • -eJ without -p correctly produces no stdout (fire-and-forget)
  • Invalid cluster -px silently ignored (x not in {p,e,J})
  • Value-taking flag -t not affected by cluster logic
  • TCP server path works correctly (separated flags)
  • TUI visual verification: attached session, -ep matches -e -p
  • Full regression: 2110 Rust tests pass, 0 failures

tmux parity note: tmux natively supports POSIX flag clustering for ALL commands through its args_parse_flags() character-by-character loop in arguments.c. This fix brings capture-pane to parity. Other commands may have the same gap but that's a separate effort.

Thanks David, two for two this week.

@psmux

psmux commented May 27, 2026

Copy link
Copy Markdown
Owner

Applied in commit 72d08aa with Co-authored-by credit. Thanks @quazardous!

@psmux psmux closed this May 27, 2026
quazardous added a commit to quazardous/aiball that referenced this pull request May 28, 2026
…#37)

psmux <= v3.3.4 silently dropped POSIX short-flag clusters in the
capture-pane CLI dispatcher (returned exit 0 + empty stdout), so the
`-ep` form we used at both call sites lost ANSI on graphite. Upstream
fix landed in psmux commit 72d08aa via PR psmux/psmux#326.

Switch both call sites (src/pane.ts captureOnce, src/api/agents.ts SSE
tick) to the separated `-e -p` form — works on every psmux version,
not just post-72d08aa. With the cluster form gone, the b/w fallback
in captureOnce (text="" retry without -e) is dead code and removed.

Also updates the stale "Caveat psmux : capture-pane may handle -e
differently — to validate with #467" comment in agents.ts now that
the caveat is gone, and the matching reference in TerminalView.vue.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants