use std::sync::Arc; use compliance_core::models::Finding; use compliance_core::traits::issue_tracker::ReviewComment; use crate::error::AgentError; use crate::llm::LlmClient; const PR_REVIEW_SYSTEM_PROMPT: &str = r#"You are a security-focused code reviewer. Given a list of security findings in a PR diff, generate concise review comments. Each comment should: 1. Briefly explain the issue 2. Suggest a specific fix 3. Reference the relevant security standard (CWE, OWASP) if applicable Be constructive and professional. Return JSON array: [{"path": "file.rs", "line": 42, "body": "..."}]"#; pub async fn generate_pr_review( llm: &Arc, findings: &[Finding], ) -> Result<(String, Vec), AgentError> { if findings.is_empty() { return Ok(( "No security issues found in this PR.".to_string(), Vec::new(), )); } let findings_text: Vec = findings .iter() .map(|f| { format!( "- [{severity}] {title} in {file}:{line}\n Code: {code}\n Rule: {rule}", severity = f.severity, title = f.title, file = f.file_path.as_deref().unwrap_or("unknown"), line = f .line_number .map(|n| n.to_string()) .unwrap_or_else(|| "?".to_string()), code = f.code_snippet.as_deref().unwrap_or("N/A"), rule = f.rule_id.as_deref().unwrap_or("N/A"), ) }) .collect(); let user_prompt = format!( "Generate review comments for these {} findings:\n{}", findings.len(), findings_text.join("\n"), ); let response = llm .chat(PR_REVIEW_SYSTEM_PROMPT, &user_prompt, Some(0.3)) .await?; // Parse comments from LLM response let comments: Vec = serde_json::from_str::>(&response) .unwrap_or_default() .into_iter() .map(|c| ReviewComment { path: c.path, line: c.line, body: c.body, }) .collect(); let summary = format!( "## Security Review\n\nFound **{}** potential security issue(s) in this PR.\n\n{}", findings.len(), findings .iter() .map(|f| format!( "- **[{}]** {} in `{}`", f.severity, f.title, f.file_path.as_deref().unwrap_or("unknown") )) .collect::>() .join("\n"), ); Ok((summary, comments)) } #[derive(serde::Deserialize)] struct PrComment { path: String, line: u32, body: String, }