33from __future__ import annotations
44
55import logging
6+ import shlex
67import shutil
78import subprocess
89import threading
@@ -44,6 +45,7 @@ def _start_process(self, server_args: t.Sequence[str | int] | None) -> None:
4445 if server_args :
4546 cmd .extend (str (a ) for a in server_args )
4647 cmd .append ("-C" )
48+ cmd .extend (["new-session" , "-A" , "-s" , "libtmux_control_mode" ])
4749
4850 logger .debug (f"Starting Control Mode process: { cmd } " )
4951 self .process = subprocess .Popen (
@@ -57,6 +59,17 @@ def _start_process(self, server_args: t.Sequence[str | int] | None) -> None:
5759 )
5860 self ._server_args = server_args
5961
62+ # Consume startup command output
63+ assert self .process .stdout is not None
64+ while True :
65+ line = self .process .stdout .readline ()
66+ if not line :
67+ # EOF immediately?
68+ logger .warning ("Control Mode process exited immediately" )
69+ break
70+ if line .startswith ("%end" ) or line .startswith ("%error" ):
71+ break
72+
6073 def run (
6174 self ,
6275 cmd : str ,
@@ -82,13 +95,13 @@ def run(
8295 assert self .process .stdout is not None
8396
8497 # Construct the command line
85- # We use subprocess.list2cmdline for basic quoting, but we need to be
86- # careful. tmux control mode accepts a single line .
98+ # We use shlex.join for correct shell-like quoting, required by tmux control
99+ # mode.
87100 full_args = [cmd ]
88101 if cmd_args :
89102 full_args .extend (str (a ) for a in cmd_args )
90103
91- command_line = subprocess . list2cmdline (full_args )
104+ command_line = shlex . join (full_args )
92105
93106 logger .debug (f"Sending to Control Mode: { command_line } " )
94107 try :
@@ -122,10 +135,21 @@ def run(
122135
123136 if line .startswith ("%begin" ):
124137 # Start of response
138+ # %begin time id flags
139+ parts = line .split ()
140+ if len (parts ) > 3 :
141+ flags = int (parts [3 ])
142+ if flags & 1 :
143+ returncode = 1
125144 continue
126145 elif line .startswith ("%end" ):
127146 # End of success response
128- returncode = 0
147+ # %end time id flags
148+ parts = line .split ()
149+ if len (parts ) > 3 :
150+ flags = int (parts [3 ])
151+ if flags & 1 :
152+ returncode = 1
129153 break
130154 elif line .startswith ("%error" ):
131155 # End of error response
@@ -144,6 +168,10 @@ def run(
144168 # Tmux usually puts error message in stdout (captured above) for %error
145169 # But we moved it to stderr_lines if %error occurred.
146170
171+ # If we detected failure via flags but got %end, treat stdout as potentially
172+ # containing info?
173+ # For now, keep stdout as is.
174+
147175 # Mimic subprocess.communicate output structure
148176 return tmux_cmd (
149177 cmd = [cmd ] + (list (map (str , cmd_args )) if cmd_args else []),
0 commit comments