Skip to content

Commit b5bd00d

Browse files
committed
fix(tools): Use ␞ (U+241E) as snapshot_pane delimiter, matching libtmux's FORMAT_SEPARATOR
why: The earlier delimiter fix picked `\t` on the theory that tmux always passes tabs through verbatim. That's true but incomplete: tabs are also legal (if rare) in Linux paths, so a pane_current_path containing a tab would still silently shift the parsed fields. And while researching which tmux versions escape ASCII control chars like 0x1f, the clean answer emerged from libtmux itself: since commit f88d28f2 (Jan 2023) libtmux has used the printable Unicode glyph `␞` (U+241E, "SYMBOL FOR RECORD SEPARATOR") as FORMAT_SEPARATOR for exactly this class of parsing. Two independent reasons ␞ is strictly safer than `\t`: 1. tmux's utf8_strvis (utf8.c:663-675) explicitly copies valid UTF-8 multi-byte sequences verbatim, bypassing the vis() escape that turns ASCII control chars into octal strings. `␞` is a valid 3-byte UTF-8 sequence (0xe2 0x90 0x9e), so no tmux version — stable, alpha, or master — can escape it through the control-char path that broke `\x1f` on CI. 2. `␞` is a printable Unicode symbol that realistically cannot appear in a pane title, a running command name, or a filesystem path. That gives us immunity to the tab-in-path vulnerability that `\t` doesn't have. This is the same delimiter libtmux uses internally for every object's format parsing, tested across every tmux version in the libtmux CI matrix for multiple years. We gain the same coverage for free. what: - Switch _SEP in snapshot_pane from "\t" to "␞". - Update the inline comment to cite utf8_strvis + libtmux's FORMAT_SEPARATOR as the rationale. - Update the monkeypatched fake_cmd in test_snapshot_pane_pads_short_display_message_output to split / rejoin on "␞". - Defensive padding retained unchanged. This supersedes the `\t` choice from the immediately-prior fix commit; that commit's defensive-padding contribution remains the load-bearing anti-IndexError guarantee. Verified locally against tmux 3.6a. Expected to clear the CI matrix failures on 3.2a, 3.3a, 3.4, 3.5, 3.6 stable, and master.
1 parent cff037c commit b5bd00d

2 files changed

Lines changed: 13 additions & 13 deletions

File tree

src/libtmux_mcp/tools/pane_tools.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -670,17 +670,17 @@ def snapshot_pane(
670670
window_id=window_id,
671671
)
672672

673-
# Fetch all metadata in a single display-message call. Use a tab as
674-
# the delimiter: tmux passes tabs through verbatim in
675-
# display-message output, whereas other ASCII control characters
676-
# (e.g. 0x1f / Unit Separator) get C-escaped to literal "\037"
677-
# strings on tmux >=3.2 / <3.6-rc, which corrupts parsing. Tabs in
678-
# pane_title are silently rejected by tmux's `select-pane -T`
679-
# input sanitizer, so the `\t` delimiter is safe against that
680-
# vector. Tabs in pane_current_path are legal on Linux but
681-
# vanishingly rare; the defensive padding below limits the blast
682-
# radius to a single truncated field rather than an IndexError.
683-
_SEP = "\t"
673+
# Fetch all metadata in a single display-message call. Use the
674+
# printable Unicode glyph ␞ (U+241E, "SYMBOL FOR RECORD SEPARATOR")
675+
# as the delimiter — the same choice libtmux itself uses for
676+
# FORMAT_SEPARATOR. tmux's utf8_strvis (tmux/utf8.c) copies any
677+
# valid UTF-8 multi-byte sequence verbatim, bypassing the vis()
678+
# escape that turns ASCII control chars like 0x1f into literal
679+
# "\037" in display-message output on some tmux builds. And ␞ is
680+
# safe against the false-positive path that a tab delimiter has:
681+
# tabs are legal (if rare) in Linux paths and could realistically
682+
# appear in pane_current_path.
683+
_SEP = ""
684684
fmt = _SEP.join(
685685
[
686686
"#{cursor_x}",

tests/test_pane_tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,8 +584,8 @@ def fake_cmd(self, cmd_name, *args, **kwargs): # type: ignore[no-untyped-def]
584584
# simulate an old tmux that dropped several unknown format
585585
# variables. Without defensive padding, parts[2..10] would
586586
# IndexError.
587-
parts = result.stdout[0].split("\t") if result.stdout else [""]
588-
result.stdout = ["\t".join(parts[:2])]
587+
parts = result.stdout[0].split("") if result.stdout else [""]
588+
result.stdout = ["".join(parts[:2])]
589589
return result
590590

591591
monkeypatch.setattr(mcp_pane.__class__, "cmd", fake_cmd)

0 commit comments

Comments
 (0)