"""Claude Code CLI: `claude -p --output-format json` with the prompt on stdin and cwd = repo; resume with `--resume `. The JSON result carries `result` (last message) and `session_id`.""" import json from .base import ExecResult, RunnerExecutor, parse_exit def parse_output(text): """Return the result object from `--output-format json` output (whole text or its last JSON line).""" text = (text or "").strip() candidates = [text] + list(reversed(text.splitlines())) for c in candidates: try: obj = json.loads(c) except ValueError: continue if isinstance(obj, dict): return obj if isinstance(obj, list): # stream-json style array: take the result event res = [o for o in obj if isinstance(o, dict) and o.get("type") == "result"] if res: return res[-1] return {} class ClaudeCodeExecutor(RunnerExecutor): stdout_name = "out.json" COLLECT = [("read", "exit.txt"), ("read", "out.json"), ("tail", "stderr.txt", 5)] def collect(self, handle): exit_text, out, tail = self.fetch(handle, self.COLLECT) # one remote call obj = parse_output(out) err = bool(obj.get("is_error")) or obj.get("subtype", "success") != "success" last = "" if err else (obj.get("result") or "") if err: tail = ("%s %s" % (obj.get("subtype", ""), str(obj.get("result", ""))[:400]) + "\n" + tail).strip() return ExecResult(last.strip(), parse_exit(exit_text), obj.get("session_id") or handle.get("thread_id"), self.logs(handle, "out.json", "stderr.txt"), tail)