use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc;
use super::orchestrator::RallyEvent;
#[derive(Debug, Clone)]
pub struct Context {
pub repo: String,
pub pr_number: u32,
pub pr_title: String,
pub pr_body: Option<String>,
pub diff: String,
pub working_dir: Option<String>,
pub head_sha: String,
pub base_branch: String,
pub external_comments: Vec<ExternalComment>,
pub local_mode: bool,
pub file_patches: Vec<(String, String)>,
}
#[derive(Debug, Clone)]
pub struct ExternalComment {
pub source: String,
pub path: Option<String>,
pub line: Option<u32>,
pub body: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReviewAction {
Approve,
RequestChanges,
Comment,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewComment {
pub path: String,
pub line: u32,
pub body: String,
pub severity: CommentSeverity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CommentSeverity {
Critical,
Major,
Minor,
Suggestion,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewerOutput {
pub action: ReviewAction,
pub summary: String,
pub comments: Vec<ReviewComment>,
pub blocking_issues: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RevieweeStatus {
Completed,
NeedsClarification,
NeedsPermission,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionRequest {
pub action: String,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RevieweeOutput {
pub status: RevieweeStatus,
pub summary: String,
pub files_modified: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub question: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_request: Option<PermissionRequest>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_details: Option<String>,
}
#[async_trait]
pub trait AgentAdapter: Send + Sync {
#[allow(dead_code)]
fn name(&self) -> &str;
fn set_event_sender(&mut self, sender: mpsc::Sender<RallyEvent>);
async fn run_reviewer(&mut self, prompt: &str, context: &Context) -> Result<ReviewerOutput>;
async fn run_reviewee(&mut self, prompt: &str, context: &Context) -> Result<RevieweeOutput>;
#[allow(dead_code)]
async fn continue_reviewer(&mut self, message: &str) -> Result<ReviewerOutput>;
#[allow(dead_code)]
async fn continue_reviewee(&mut self, message: &str) -> Result<RevieweeOutput>;
fn add_reviewee_allowed_tool(&mut self, tool: &str);
fn set_local_mode(&mut self, local_mode: bool);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SupportedAgent {
Claude,
Codex,
}
impl SupportedAgent {
pub fn from_name(name: &str) -> Option<Self> {
match name.to_lowercase().as_str() {
"claude" => Some(Self::Claude),
"codex" => Some(Self::Codex),
_ => None,
}
}
#[allow(dead_code)]
pub fn name(&self) -> &'static str {
match self {
Self::Claude => "claude",
Self::Codex => "codex",
}
}
}