11import asyncio
2+ import base64
23import os
34import platform
45import subprocess
@@ -54,8 +55,11 @@ def run_cmd_subprocess(
5455 if platform .system () == "Windows" :
5556 parent_process = get_windows_parent_process_name ()
5657 if parent_process == "powershell.exe" :
57- command = f"powershell -Command { command } "
58-
58+ # Silence progress/error streams at the source to prevent CLIXML
59+ silenced_command = f"$ProgressPreference='SilentlyContinue'; { command } "
60+ cmd_bytes = silenced_command .encode ("utf-16-le" )
61+ encoded = base64 .b64encode (cmd_bytes ).decode ()
62+ command = f"powershell -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand { encoded } "
5963 if verbose :
6064 print ("Running command:" , command )
6165 print ("SHELL:" , shell )
@@ -93,7 +97,7 @@ def run_cmd_subprocess(
9397 print (line , end = "" , flush = True )
9498
9599 process .wait ()
96- return process .returncode , "" .join (output )
100+ return process .returncode , _clean_output ( "" .join (output ) )
97101 except Exception as e :
98102 return 1 , str (e )
99103
@@ -116,7 +120,11 @@ async def run_cmd_async(
116120 if platform .system () == "Windows" :
117121 parent_process = get_windows_parent_process_name ()
118122 if parent_process == "powershell.exe" :
119- command = f"powershell -Command { command } "
123+ # Silence progress/error streams at the source to prevent CLIXML
124+ silenced_command = f"$ProgressPreference='SilentlyContinue'; { command } "
125+ cmd_bytes = silenced_command .encode ("utf-16-le" )
126+ encoded = base64 .b64encode (cmd_bytes ).decode ()
127+ command = f"powershell -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand { encoded } "
120128
121129 if verbose :
122130 print ("Running command:" , command )
@@ -131,6 +139,19 @@ async def run_cmd_async(
131139 stderr = asyncio .subprocess .STDOUT ,
132140 cwd = cwd ,
133141 )
142+ except NotImplementedError :
143+ # On Windows with SelectorEventLoop, asyncio does not support subprocesses.
144+ # Fall back to synchronous subprocess via loop.run_in_executor.
145+ loop = asyncio .get_running_loop ()
146+ return await loop .run_in_executor (
147+ None ,
148+ run_cmd_subprocess ,
149+ command ,
150+ verbose ,
151+ cwd ,
152+ encoding ,
153+ should_print ,
154+ )
134155 except FileNotFoundError :
135156 return 1 , f"Command not found: { command } "
136157
@@ -175,7 +196,7 @@ async def read_stream(stream):
175196 if not reader_task .done ():
176197 await reader_task
177198
178- return process .returncode , "" .join (output )
199+ return process .returncode , _clean_output ( "" .join (output ) )
179200
180201
181202def run_cmd_pexpect (command , verbose = False , cwd = None , should_print = True ):
@@ -222,3 +243,26 @@ def output_callback(b):
222243 except (pexpect .ExceptionPexpect , TypeError , ValueError ) as e :
223244 error_msg = f"Error running command { command } : { e } "
224245 return 1 , error_msg
246+
247+
248+ def _clean_output (output ):
249+ """Remove CLIXML progress output from PowerShell commands."""
250+ if platform .system () != "Windows" :
251+ return output
252+
253+ if output .startswith ("#< CLIXML" ):
254+ lines = output .splitlines ()
255+ filtered = []
256+ for line in lines :
257+ # Skip the CLIXML header line
258+ if line .startswith ("#< CLIXML" ):
259+ continue
260+ # Skip CLIXML XML object tags (progress messages)
261+ stripped = line .strip ()
262+ if stripped .startswith ("<Objs " ) or stripped == "</Objs>" :
263+ continue
264+ if stripped .startswith ("<Obj " ):
265+ continue
266+ filtered .append (line )
267+ return "\n " .join (filtered )
268+ return output
0 commit comments