File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -32,6 +32,12 @@ scrollback of the new session's panes before a long job starts.
3232
3333### Fixes
3434
35+ **Server instructions and wait errors name the right next tool**
36+
37+ The server instructions route a pane's own process to `wait_for_pane_exit`,
38+ `split_window` points to `split_window_many`, and a `wait_for_channel` timeout
39+ or lost server says what to call next.
40+
3541**A socket tmux cannot open is an error, not an empty list**
3642
3743`list_sessions`, `list_windows` and `list_panes` used to answer `[]` when tmux
Original file line number Diff line number Diff line change 9090
9191_INSTR_READ_TOOLS = (
9292 "Prefer snapshot_pane over capture_pane + get_pane_info; capture_since "
93- "for repeated observation/ tailing; display_message for tmux variables."
93+ "for tailing; display_message for tmux variables."
9494)
9595
9696_INSTR_WAIT_NOT_POLL = (
97- "WAIT, DON'T POLL: run_command for authored commands needing "
98- "status; wait_for_channel for custom tmux wait-for; capture_since "
97+ "WAIT, DON'T POLL: run_command for authored commands (status); "
98+ "wait_for_pane_exit for shell= jobs; "
99+ "wait_for_channel for custom wait-for; capture_since "
99100 "for tailing; wait_for_text for output you don't author "
100101 "(patterns=null=any output; stop=[] bails); "
101102 "send_keys_batch for raw input."
102103)
103104
105+
104106#: Gap-explainer: write-hook tools are intentionally absent. See module
105107#: comment above for when to add another ``_GAP`` segment vs. push the
106108#: explanation into a tool description.
Original file line number Diff line number Diff line change @@ -223,7 +223,13 @@ async def wait_for_channel(
223223 f"wait-for timeout: channel { cname !r} was not signalled within "
224224 f"{ effective_timeout } s"
225225 )
226- raise ExpectedToolError (msg ) from e
226+ raise ExpectedToolError (
227+ msg ,
228+ suggestion = (
229+ "The channel is still usable. Check the command that should "
230+ "signal it with capture_since, then wait again or raise timeout."
231+ ),
232+ ) from e
227233 if returncode != 0 :
228234 detail = stderr .decode (errors = "replace" ).strip ()
229235 msg = f"wait-for failed for channel { cname !r} : { detail or f'exit { returncode } ' } "
@@ -253,7 +259,10 @@ async def wait_for_channel(
253259 "no longer running, so the channel was probably never signalled "
254260 "— tmux exits 0 for both. Re-check the work you were waiting on."
255261 )
256- raise ExpectedToolError (msg )
262+ raise ExpectedToolError (
263+ msg ,
264+ suggestion = "Call get_server_info; create_session starts a new server." ,
265+ )
257266 return f"Channel { cname !r} was signalled (timeout { effective_timeout } s)"
258267
259268
Original file line number Diff line number Diff line change @@ -172,6 +172,7 @@ def split_window(
172172
173173 Creates a new pane by splitting an existing one. Use direction to choose
174174 above/below/left/right. Returns the new pane's info including its pane_id.
175+ To add several panes at once under one layout, use split_window_many.
175176
176177 Parameters
177178 ----------
Original file line number Diff line number Diff line change @@ -413,6 +413,7 @@ def test_base_instructions_prefer_typed_completion_over_polling() -> None:
413413 """_BASE_INSTRUCTIONS names typed completion and observation primitives."""
414414 assert "run_command" in _BASE_INSTRUCTIONS
415415 assert "wait_for_channel" in _BASE_INSTRUCTIONS
416+ assert "wait_for_pane_exit" in _BASE_INSTRUCTIONS
416417 assert "capture_since" in _BASE_INSTRUCTIONS
417418 assert "wait_for_text" in _BASE_INSTRUCTIONS
418419 # The catch-all form replaced the separate wait_for_content_change
Original file line number Diff line number Diff line change @@ -544,3 +544,23 @@ async def _drive() -> str:
544544
545545 assert "was signalled" in result
546546 assert time .monotonic () - started < 1.9
547+
548+
549+ @pytest .mark .usefixtures ("mcp_session" )
550+ def test_wait_for_channel_timeout_error_carries_a_next_step (
551+ mcp_server : Server ,
552+ ) -> None :
553+ """A timed-out wait tells the agent the channel is reusable and what to read."""
554+ from libtmux_mcp ._utils import ExpectedToolError
555+
556+ with pytest .raises (ExpectedToolError ) as excinfo :
557+ asyncio .run (
558+ wait_for_channel (
559+ channel = "wf_hint_test" ,
560+ timeout = 0.3 ,
561+ socket_name = mcp_server .socket_name ,
562+ )
563+ )
564+
565+ assert "still usable" in (excinfo .value .suggestion or "" )
566+ assert "capture_since" in (excinfo .value .suggestion or "" )
You can’t perform that action at this time.
0 commit comments