Skip to content

Commit 76e57a5

Browse files
committed
mcp(fix[pane]): Filter run_command sync line by UUID only
why: joining wrapped capture rows (tmux -J, as search and wait already do) keeps the private sync line one logical row carrying the full per-call channel and status option, so the over-broad substring markers are unnecessary and only caused silent false drops of real output. what: - Capture with join_wrapped=True - Match only the per-call channel and status option - Drop the xfail and assert the sync line is dropped
1 parent 14ca45f commit 76e57a5

2 files changed

Lines changed: 29 additions & 15 deletions

File tree

src/libtmux_mcp/tools/pane_tools/io.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ async def run_command(
204204
with contextlib.suppress(Exception):
205205
pane.cmd("set-option", "-p", "-u", status_option)
206206

207-
raw_lines = await asyncio.to_thread(pane.capture_pane)
207+
# join_wrapped keeps the private sync line a single logical row, so
208+
# _filter_run_command_internal_lines can match it by the per-call
209+
# channel/status option even when a wide prompt wraps it.
210+
raw_lines = await asyncio.to_thread(pane.capture_pane, join_wrapped=True)
208211
visible_lines = _filter_run_command_internal_lines(
209212
raw_lines,
210213
channel=channel,
@@ -271,16 +274,14 @@ def _truncate_lines_tail(
271274
def _filter_run_command_internal_lines(
272275
lines: list[str], channel: str, status_option: str
273276
) -> list[str]:
274-
"""Drop private synchronisation commands from captured command output."""
275-
internal_markers = (
276-
channel,
277-
status_option,
278-
"__libtmux_mcp_status",
279-
"libtmux_mcp_",
280-
"mcp_status",
281-
"tmux wait-for -S",
282-
"tmux set-option -p",
283-
)
277+
"""Drop the private synchronisation line from captured output.
278+
279+
Matches only the per-call ``channel`` and ``status_option`` (random
280+
hex that never collides with real output). ``run_command`` captures
281+
with ``join_wrapped`` so the line stays one logical row even under a
282+
wide prompt, keeping both markers intact.
283+
"""
284+
internal_markers = (channel, status_option)
284285
return [
285286
line for line in lines if not any(marker in line for marker in internal_markers)
286287
]

tests/test_pane_tools.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -245,10 +245,6 @@ class FilterInternalLinesFixture(t.NamedTuple):
245245
]
246246

247247

248-
@pytest.mark.xfail(
249-
strict=True,
250-
reason="#76: over-broad markers drop legitimate run_command output",
251-
)
252248
@pytest.mark.parametrize(
253249
FilterInternalLinesFixture._fields,
254250
FILTER_KEEP_FIXTURES,
@@ -269,6 +265,23 @@ def test_filter_run_command_keeps_legitimate_output(test_id: str, line: str) ->
269265
assert kept == [line]
270266

271267

268+
def test_filter_run_command_drops_sync_line() -> None:
269+
"""The joined private synchronisation line is removed from output."""
270+
from libtmux_mcp.tools.pane_tools.io import _filter_run_command_internal_lines
271+
272+
command_id = "deadbeefdeadbeefdeadbeefdeadbeef"
273+
channel = f"libtmux_mcp_run_{command_id}"
274+
status_option = f"@libtmux_mcp_status_{command_id}"
275+
sync_line = (
276+
f"}}; __libtmux_mcp_status=$?; tmux set-option -p {status_option} "
277+
f'"$__libtmux_mcp_status"; tmux wait-for -S {channel}'
278+
)
279+
kept = _filter_run_command_internal_lines(
280+
["RUN_OK", sync_line], channel=channel, status_option=status_option
281+
)
282+
assert kept == ["RUN_OK"]
283+
284+
272285
def test_capture_pane(mcp_server: Server, mcp_pane: Pane) -> None:
273286
"""capture_pane returns pane content."""
274287
result = capture_pane(

0 commit comments

Comments
 (0)