diff --git a/scripts/review_pr.py b/scripts/review_pr.py index fca864a..ccb6aa6 100644 --- a/scripts/review_pr.py +++ b/scripts/review_pr.py @@ -19,6 +19,7 @@ import shutil import subprocess import sys import threading +import time import urllib.error import urllib.request @@ -327,7 +328,7 @@ def run_claude(claude_bin, project_root, prompt, extra_dir=None): cmd += ["--add-dir", extra_dir] cmd.append(prompt) - print(f"[review_pr] Running: {' '.join(cmd[:4])} ...", file=sys.stderr) + print(f"[review_pr] Command: {' '.join(cmd)}", file=sys.stderr) proc = subprocess.Popen( cmd, cwd=project_root, @@ -337,22 +338,37 @@ def run_claude(claude_bin, project_root, prompt, extra_dir=None): encoding="utf-8", ) - def _stream(pipe, label, dest_lines): + def _stream(pipe, prefix, dest_lines): for line in pipe: line = line.rstrip("\n") - if line: - print(f"[{label}] {line}", file=sys.stderr) dest_lines.append(line) - - stderr_lines = [] - t = threading.Thread(target=_stream, args=(proc.stderr, "claude:stderr", stderr_lines)) - t.start() + if line.strip(): + print(f"[{prefix}] {line}", file=sys.stderr) stdout_lines = [] - _stream(proc.stdout, "claude", stdout_lines) + stderr_lines = [] - t.join(timeout=10) - proc.wait(timeout=600) + # Heartbeat: print alive message every 30s so we know claude hasn't hung + heartbeat_stop = threading.Event() + def _heartbeat(start): + while not heartbeat_stop.is_set(): + heartbeat_stop.wait(30) + if not heartbeat_stop.is_set(): + elapsed = int(time.time() - start) + print(f"[review_pr] ... still running ({elapsed}s elapsed)", file=sys.stderr) + t_beat = threading.Thread(target=_heartbeat, args=(time.time(),)) + t_beat.start() + + t_out = threading.Thread(target=_stream, args=(proc.stdout, "claude", stdout_lines)) + t_err = threading.Thread(target=_stream, args=(proc.stderr, "claude:stderr", stderr_lines)) + t_out.start() + t_err.start() + + t_out.join(timeout=600) + heartbeat_stop.set() + t_err.join(timeout=10) + t_beat.join(timeout=5) + proc.wait(timeout=10) if proc.returncode != 0: print(f"Claude exited with code {proc.returncode}", file=sys.stderr) @@ -367,7 +383,7 @@ def extract_json_from_output(stdout): match = re.search(r"```json\s*([\s\S]*)\s*```", stdout) if not match: print("ERROR: No ```json block found in Claude output.", file=sys.stderr) - print(stdout[:3000], file=sys.stderr) + print(stdout[-3000:], file=sys.stderr) sys.exit(1) try: