Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions parallel_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,14 +439,20 @@ def _spawn_coding_agent(self, feature_id: int) -> tuple[bool, str]:
cmd.append("--yolo")

try:
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
cwd=str(AUTOCODER_ROOT),
env={**os.environ, "PYTHONUNBUFFERED": "1"},
)
# CREATE_NO_WINDOW on Windows prevents console window pop-ups
# stdin=DEVNULL prevents blocking on stdin reads
popen_kwargs = {
"stdin": subprocess.DEVNULL,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
"text": True,
"cwd": str(AUTOCODER_ROOT), # Run from autocoder root for proper imports
"env": {**os.environ, "PYTHONUNBUFFERED": "1"},
}
if sys.platform == "win32":
popen_kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW

proc = subprocess.Popen(cmd, **popen_kwargs)
except Exception as e:
# Reset in_progress on failure
session = self.get_session()
Expand Down Expand Up @@ -626,6 +632,12 @@ def _read_output(
print(f"[Feature #{feature_id}] {line}", flush=True)
proc.wait()
finally:
# CRITICAL: Kill the process tree to clean up any child processes (e.g., Claude CLI)
# This prevents zombie processes from accumulating
try:
_kill_process_tree(proc, timeout=2.0)
except Exception as e:
debug_log.log("CLEANUP", f"Error killing process tree for {agent_type} agent", error=str(e))
self._on_agent_complete(feature_id, proc.returncode, agent_type, proc)

def _on_agent_complete(
Expand Down
22 changes: 14 additions & 8 deletions server/services/process_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,14 +354,20 @@ async def start(
try:
# Start subprocess with piped stdout/stderr
# Use project_dir as cwd so Claude SDK sandbox allows access to project files
# IMPORTANT: Set PYTHONUNBUFFERED to ensure output isn't delayed
self.process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=str(self.project_dir),
env={**os.environ, "PYTHONUNBUFFERED": "1"},
)
# stdin=DEVNULL prevents blocking if Claude CLI or child process tries to read stdin
# CREATE_NO_WINDOW on Windows prevents console window pop-ups
# PYTHONUNBUFFERED ensures output isn't delayed
popen_kwargs = {
"stdin": subprocess.DEVNULL,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
"cwd": str(self.project_dir),
"env": {**os.environ, "PYTHONUNBUFFERED": "1"},
}
if sys.platform == "win32":
popen_kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW

self.process = subprocess.Popen(cmd, **popen_kwargs)

# Atomic lock creation - if it fails, another process beat us
if not self._create_lock():
Expand Down
2 changes: 0 additions & 2 deletions start_ui.bat
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,3 @@ pip install -r requirements.txt --quiet

REM Run the Python launcher
python "%~dp0start_ui.py" %*

pause
Loading