Skip to content

Commit 83e6105

Browse files
committed
fix(tools): Surface tmux errors from select_window directional navigation
why: session.cmd("next-window" / "previous-window" / "last-window") discarded the tmux_cmd return value. When tmux emitted an error — most obviously "last-window" against a session with no previously- active window, or "next-window" on a single-window session with wrapping off — stderr was non-empty but the tool silently returned session.active_window as if the navigation had succeeded. Agents observed a success response and never noticed the no-op. what: - Capture the tmux_cmd from session.cmd(subcommand) and raise ToolError if proc.stderr is non-empty, using the subcommand name in the message so the agent knows which operation failed. - Add test_select_window_last_on_single_window_session_raises: a freshly-created session has no prior window, so "last-window" emits "no last window" on stderr. Verified to FAIL against the previous code (silent success) and PASS with the fix. Scope is deliberately narrow: only the one new call site flagged by review. Other .cmd() sites in pre-existing tools follow the same silent pattern and are out of scope for this PR (separate hygiene follow-up).
1 parent 3e38208 commit 83e6105

2 files changed

Lines changed: 26 additions & 1 deletion

File tree

src/libtmux_mcp/tools/session_tools.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,11 @@ def select_window(
289289
if subcommand is None:
290290
msg = f"Invalid direction: {direction!r}. Valid: next, previous, last"
291291
raise ToolError(msg)
292-
session.cmd(subcommand)
292+
proc = session.cmd(subcommand)
293+
if proc.stderr:
294+
stderr = " ".join(proc.stderr).strip()
295+
msg = f"tmux {subcommand} failed: {stderr}"
296+
raise ToolError(msg)
293297

294298
active_window = session.active_window
295299
return _serialize_window(active_window)

tests/test_session_tools.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,27 @@ def test_select_window_requires_target(mcp_server: Server) -> None:
244244
select_window(socket_name=mcp_server.socket_name)
245245

246246

247+
def test_select_window_last_on_single_window_session_raises(
248+
mcp_server: Server, mcp_session: Session
249+
) -> None:
250+
"""select_window last with no prior window must surface the tmux error.
251+
252+
Regression guard: session.cmd("last-window") on a session that has
253+
never had a previously-active window emits "no last window" on
254+
stderr, but the tool previously discarded the return value and
255+
returned the unchanged active window as if the navigation had
256+
worked.
257+
"""
258+
# The fixture session is freshly created: there is no previously-
259+
# active window for last-window to jump back to.
260+
with pytest.raises(ToolError, match="last-window"):
261+
select_window(
262+
direction="last",
263+
session_name=mcp_session.session_name,
264+
socket_name=mcp_server.socket_name,
265+
)
266+
267+
247268
def test_kill_session_requires_target(mcp_server: Server) -> None:
248269
"""kill_session refuses to kill without an explicit target."""
249270
with pytest.raises(ToolError, match="Refusing to kill"):

0 commit comments

Comments
 (0)