44import logging
55import os
66import shutil
7- import tempfile
8- from collections import deque
97from collections .abc import AsyncIterable , AsyncIterator
108from contextlib import suppress
119from pathlib import Path
@@ -42,9 +40,7 @@ def __init__(
4240 self ._cwd = str (options .cwd ) if options .cwd else None
4341 self ._process : Process | None = None
4442 self ._stdout_stream : TextReceiveStream | None = None
45- self ._stderr_stream : TextReceiveStream | None = None
4643 self ._stdin_stream : TextSendStream | None = None
47- self ._stderr_file : Any = None # tempfile.NamedTemporaryFile
4844 self ._ready = False
4945 self ._exit_error : Exception | None = None # Track process exit errors
5046
@@ -174,12 +170,6 @@ async def connect(self) -> None:
174170
175171 cmd = self ._build_command ()
176172 try :
177- # Create a temp file for stderr to avoid pipe buffer deadlock
178- # We can't use context manager as we need it for the subprocess lifetime
179- self ._stderr_file = tempfile .NamedTemporaryFile ( # noqa: SIM115
180- mode = "w+" , prefix = "claude_stderr_" , suffix = ".log" , delete = False
181- )
182-
183173 # Merge environment variables: system -> user -> SDK required
184174 process_env = {
185175 ** os .environ ,
@@ -190,11 +180,19 @@ async def connect(self) -> None:
190180 if self ._cwd :
191181 process_env ["PWD" ] = self ._cwd
192182
183+ # Only output stderr if customer explicitly requested debug output and provided a file object
184+ stderr_dest = (
185+ self ._options .debug_stderr
186+ if "debug-to-stderr" in self ._options .extra_args
187+ and self ._options .debug_stderr
188+ else None
189+ )
190+
193191 self ._process = await anyio .open_process (
194192 cmd ,
195193 stdin = PIPE ,
196194 stdout = PIPE ,
197- stderr = self . _stderr_file ,
195+ stderr = stderr_dest ,
198196 cwd = self ._cwd ,
199197 env = process_env ,
200198 )
@@ -234,7 +232,7 @@ async def close(self) -> None:
234232 if not self ._process :
235233 return
236234
237- # Close stdin first if it's still open
235+ # Close streams
238236 if self ._stdin_stream :
239237 with suppress (Exception ):
240238 await self ._stdin_stream .aclose ()
@@ -253,18 +251,8 @@ async def close(self) -> None:
253251 # Just try to wait, but don't block if it fails
254252 await self ._process .wait ()
255253
256- # Clean up temp file
257- if self ._stderr_file :
258- try :
259- self ._stderr_file .close ()
260- Path (self ._stderr_file .name ).unlink ()
261- except Exception :
262- pass
263- self ._stderr_file = None
264-
265254 self ._process = None
266255 self ._stdout_stream = None
267- self ._stderr_stream = None
268256 self ._stdin_stream = None
269257 self ._exit_error = None
270258
@@ -358,46 +346,20 @@ async def _read_messages_impl(self) -> AsyncIterator[dict[str, Any]]:
358346 # Client disconnected
359347 pass
360348
361- # Read stderr from temp file (keep only last N lines for memory efficiency)
362- stderr_lines : deque [str ] = deque (maxlen = 100 ) # Keep last 100 lines
363- if self ._stderr_file :
364- try :
365- # Flush any pending writes
366- self ._stderr_file .flush ()
367- # Read from the beginning
368- self ._stderr_file .seek (0 )
369- for line in self ._stderr_file :
370- line_text = line .strip ()
371- if line_text :
372- stderr_lines .append (line_text )
373- except Exception :
374- pass
375-
376349 # Check process completion and handle errors
377350 try :
378351 returncode = await self ._process .wait ()
379352 except Exception :
380353 returncode = - 1
381354
382- # Convert deque to string for error reporting
383- stderr_output = "\n " .join (list (stderr_lines )) if stderr_lines else ""
384- if len (stderr_lines ) == stderr_lines .maxlen :
385- stderr_output = (
386- f"[stderr truncated, showing last { stderr_lines .maxlen } lines]\n "
387- + stderr_output
388- )
389-
390- # Use exit code for error detection, not string matching
355+ # Use exit code for error detection
391356 if returncode is not None and returncode != 0 :
392357 self ._exit_error = ProcessError (
393358 f"Command failed with exit code { returncode } " ,
394359 exit_code = returncode ,
395- stderr = stderr_output ,
360+ stderr = "Check stderr output for details" ,
396361 )
397362 raise self ._exit_error
398- elif stderr_output :
399- # Log stderr for debugging but don't fail on non-zero exit
400- logger .debug (f"Process stderr: { stderr_output } " )
401363
402364 def is_ready (self ) -> bool :
403365 """Check if transport is ready for communication."""
0 commit comments