codeflash-agent/packages/github-app/github_app/backends.py
Kevin Turcios 2caaf6af7c
Fix CI: mypy errors, ruff formatting, switch to prek (#22)
* Fix mypy errors and apply ruff formatting across packages

Fix ast.FunctionDef calls missing type_params for Python 3.12+,
correct type: ignore error codes in _comparator and _plugin, and
run ruff format on all package source and test files.

* Switch CI to prek for lint/typecheck checks

Use j178/prek-action for consistent lint+typecheck (ruff check,
ruff format, interrogate, mypy) matching local pre-commit config.
Keep test as a separate parallel job for test-env support.
2026-04-15 02:52:47 -05:00

149 lines
3.5 KiB
Python

"""CLI backend registry.
Each backend knows how to build a command-line invocation for a
specific AI CLI tool. To add a new backend (gemini, opencode,
tarmina, …), subclass ``BackendSpec``, implement ``build_cmd``,
and register an instance in ``BACKENDS``.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from pathlib import Path
@dataclass(frozen=True, slots=True)
class BackendSpec(ABC):
"""How to invoke a specific CLI backend."""
name: str
@abstractmethod
def build_cmd(
self,
*,
cli: str,
model: str,
prompt: str,
repo_dir: Path,
plugin_dir: Path | None = None,
) -> tuple[list[str], str | None]:
"""Return ``(argv, cwd_or_None)``."""
def build_edit_cmd(
self,
*,
cli: str,
model: str,
prompt: str,
repo_dir: Path,
plugin_dir: Path | None = None,
agent: str = "codeflash-ci",
) -> tuple[list[str], str | None]:
"""Return ``(argv, cwd_or_None)`` with autonomous edit permissions.
Default falls back to ``build_cmd``; override for backends
that need extra flags to enable file editing.
"""
return self.build_cmd(
cli=cli,
model=model,
prompt=prompt,
repo_dir=repo_dir,
plugin_dir=plugin_dir,
)
@dataclass(frozen=True, slots=True)
class ClaudeBackend(BackendSpec):
"""Claude Code CLI backend."""
def build_cmd(
self,
*,
cli: str,
model: str,
prompt: str,
repo_dir: Path,
plugin_dir: Path | None = None,
) -> tuple[list[str], str | None]:
cmd = [cli, "-p", prompt, "--model", model]
if plugin_dir:
cmd += ["--plugin-dir", str(plugin_dir)]
return cmd, str(repo_dir)
def build_edit_cmd(
self,
*,
cli: str,
model: str,
prompt: str,
repo_dir: Path,
plugin_dir: Path | None = None,
agent: str = "codeflash-ci",
) -> tuple[list[str], str | None]:
cmd = [
cli,
"-p",
prompt,
"--model",
model,
"--agent",
agent,
"--max-turns",
"200",
"--dangerously-skip-permissions",
]
if plugin_dir:
cmd += ["--plugin-dir", str(plugin_dir)]
return cmd, str(repo_dir)
@dataclass(frozen=True, slots=True)
class CodexBackend(BackendSpec):
"""OpenAI Codex CLI backend."""
def build_cmd(
self,
*,
cli: str,
model: str,
prompt: str,
repo_dir: Path,
plugin_dir: Path | None = None,
) -> tuple[list[str], str | None]:
cmd = [
cli,
"exec",
"--model",
model,
"--full-auto",
"-C",
str(repo_dir),
"-o",
"/dev/stdout",
prompt,
]
return cmd, None
BACKENDS: dict[str, BackendSpec] = {
"claude": ClaudeBackend(name="claude"),
"codex": CodexBackend(name="codex"),
}
def get_backend(name: str) -> BackendSpec:
"""Look up a backend by name.
Raises ``ValueError`` for unknown backends.
"""
if name not in BACKENDS:
known = ", ".join(sorted(BACKENDS))
msg = f"Unknown backend {name!r}. Known: {known}"
raise ValueError(msg)
return BACKENDS[name]