* 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.
149 lines
3.5 KiB
Python
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]
|