"""Agent runner for CI mode. Invokes the CLI backend (Claude, Codex, ...) with the codeflash-ci plugin agent in the cloned repo directory. The agent reads ``.codeflash/ci-context.json`` and handles everything autonomously. """ from __future__ import annotations import asyncio import logging import os from typing import TYPE_CHECKING from .backends import get_backend if TYPE_CHECKING: from pathlib import Path from .config import Config log = logging.getLogger(__name__) async def run_agent( config: Config, repo_dir: Path, token: str, *, agent: str = "codeflash-ci", prompt: str = "CI: process .codeflash/ci-context.json", timeout: int = 3600, ) -> str: """Run a plugin agent in the cloned repo. Sets ``GITHUB_TOKEN`` and ``GH_TOKEN`` so the agent can use ``gh`` CLI for all GitHub interactions. Returns the agent's stdout output. """ spec = get_backend(config.lead_backend) cmd, cwd = spec.build_edit_cmd( cli=config.cli_for_backend(config.lead_backend), model=config.model_for_backend(config.lead_backend), prompt=prompt, repo_dir=repo_dir, plugin_dir=config.plugin_dir, agent=agent, ) env = {**os.environ, "GITHUB_TOKEN": token, "GH_TOKEN": token} log.info("Running agent in %s: %s", repo_dir, " ".join(cmd[:6])) proc = await asyncio.create_subprocess_exec( *cmd, cwd=cwd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, env=env, ) try: stdout, stderr = await asyncio.wait_for( proc.communicate(), timeout=timeout, ) except TimeoutError: proc.kill() msg = f"Agent timed out after {timeout}s" raise TimeoutError(msg) from None if proc.returncode != 0: log.error( "Agent failed (rc=%d): %s", proc.returncode, stderr.decode(), ) msg = f"Agent exited with code {proc.returncode}" raise RuntimeError(msg) return stdout.decode()