Skip to content

Commit 76bdf31

Browse files
committed
✨ Add AI-powered code review functionality
Implement a new 'review' command that provides AI-powered code reviews for staged changes. This adds the ability to get professional, constructive feedback on code changes before committing. The implementation includes: - New review command in CLI with options to print or display results - GeneratedReview model for structured code review feedback - Code review prompt generation and token optimization - Colorized terminal output with categorized feedback - Unit tests for review formatting The review output includes summary, code quality assessment, positive aspects, issues identified, and suggestions for improvement - all formatted with helpful colors and emojis for better readability.
1 parent 9098e9f commit 76bdf31

File tree

8 files changed

+467
-43
lines changed

8 files changed

+467
-43
lines changed

src/cli.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::common::CommonParams;
55
use crate::llm::get_available_provider_names;
66
use crate::log_debug;
77
use crate::ui;
8-
use clap::builder::{styling::AnsiColor, Styles};
9-
use clap::{crate_version, Parser, Subcommand};
8+
use clap::builder::{Styles, styling::AnsiColor};
9+
use clap::{Parser, Subcommand, crate_version};
1010

1111
const LOG_FILE: &str = "git-iris-debug.log";
1212

@@ -74,6 +74,19 @@ pub enum Commands {
7474
#[arg(long, help = "Skip verification steps (pre/post commit hooks)")]
7575
no_verify: bool,
7676
},
77+
/// Review staged changes and provide feedback
78+
#[command(
79+
about = "Review staged changes using AI",
80+
long_about = "Generate a detailed code review of staged changes using AI based on the current Git context."
81+
)]
82+
Review {
83+
#[command(flatten)]
84+
common: CommonParams,
85+
86+
/// Print the generated review to stdout and exit
87+
#[arg(short, long, help = "Print the generated review to stdout and exit")]
88+
print: bool,
89+
},
7790
/// Configure the AI-assisted Git commit message generator
7891
#[command(about = "Configure the AI-assisted Git commit message generator")]
7992
Config {
@@ -200,7 +213,11 @@ pub async fn handle_command(command: Commands) -> anyhow::Result<()> {
200213
} => {
201214
log_debug!(
202215
"Handling 'gen' command with common: {:?}, auto_commit: {}, no_gitmoji: {}, print: {}, no_verify: {}",
203-
common, auto_commit, no_gitmoji, print, no_verify
216+
common,
217+
auto_commit,
218+
no_gitmoji,
219+
print,
220+
no_verify
204221
);
205222

206223
ui::print_version(crate_version!());
@@ -217,7 +234,11 @@ pub async fn handle_command(command: Commands) -> anyhow::Result<()> {
217234
} => {
218235
log_debug!(
219236
"Handling 'config' command with common: {:?}, api_key: {:?}, model: {:?}, token_limit: {:?}, param: {:?}",
220-
common, api_key, model, token_limit, param
237+
common,
238+
api_key,
239+
model,
240+
token_limit,
241+
param
221242
);
222243
commands::handle_config_command(common, api_key, model, token_limit, param)?;
223244
}
@@ -243,6 +264,16 @@ pub async fn handle_command(command: Commands) -> anyhow::Result<()> {
243264
);
244265
changes::handle_release_notes_command(common, from, to).await?;
245266
}
267+
Commands::Review { common, print } => {
268+
log_debug!(
269+
"Handling 'review' command with common: {:?}, print: {}",
270+
common,
271+
print
272+
);
273+
ui::print_version(crate_version!());
274+
println!();
275+
commit::review::handle_review_command(common, print).await?;
276+
}
246277
}
247278

248279
Ok(())

src/commit/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod cli;
22
mod relevance;
3+
pub mod review;
34

45
pub mod prompt;
56
pub mod service;
67

78
pub use cli::handle_gen_command;
9+
pub use review::handle_review_command;
810
use git2::FileMode;
911
pub use service::IrisCommitService;
1012

src/commit/prompt.rs

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::common::get_combined_instructions;
22
use crate::config::Config;
33
use crate::context::{
4-
ChangeType, CommitContext, GeneratedMessage, ProjectMetadata, RecentCommit, StagedFile,
4+
ChangeType, CommitContext, GeneratedMessage, GeneratedReview, ProjectMetadata, RecentCommit,
5+
StagedFile,
56
};
67
use crate::gitmoji::{apply_gitmoji, get_gitmoji_list};
78

@@ -193,3 +194,108 @@ pub fn process_commit_message(message: String, use_gitmoji: bool) -> String {
193194
message
194195
}
195196
}
197+
198+
/// Creates a system prompt for code review generation
199+
pub fn create_review_system_prompt(config: &Config) -> anyhow::Result<String> {
200+
let review_schema = schemars::schema_for!(GeneratedReview);
201+
let review_schema_str = serde_json::to_string_pretty(&review_schema)?;
202+
203+
let mut prompt = String::from(
204+
"You are an AI assistant specializing in code reviews. \
205+
Your task is to provide a comprehensive, professional, and constructive review of the code changes provided.
206+
207+
Work step-by-step and follow these guidelines exactly:
208+
209+
1. Analyze the code changes carefully, focusing on:
210+
- Code quality and readability
211+
- Potential bugs or errors
212+
- Architecture and design patterns
213+
- Performance implications
214+
- Security considerations
215+
- Maintainability and testability
216+
217+
2. Provide constructive feedback:
218+
- Be specific and actionable in your suggestions
219+
- Point out both strengths and areas for improvement
220+
- Explain why certain patterns or practices are problematic
221+
- Suggest alternative approaches when appropriate
222+
223+
3. Focus on substantive issues:
224+
- Prioritize significant issues over minor stylistic concerns
225+
- Consider the context of the codebase and changes
226+
- Note potential edge cases or scenarios that might not be handled
227+
228+
4. Be professional and constructive:
229+
- Frame feedback positively and constructively
230+
- Focus on the code, not the developer
231+
- Acknowledge good practices and improvements
232+
233+
Your review should be based entirely on the information provided in the context, without any speculation or assumptions.
234+
");
235+
236+
prompt.push_str(get_combined_instructions(config).as_str());
237+
238+
prompt.push_str("
239+
Your response must be a valid JSON object with the following structure:
240+
241+
{
242+
\"summary\": \"A brief summary of the changes and their quality\",
243+
\"code_quality\": \"An assessment of the overall code quality\",
244+
\"suggestions\": [\"Suggestion 1\", \"Suggestion 2\", ...],
245+
\"issues\": [\"Issue 1\", \"Issue 2\", ...],
246+
\"positive_aspects\": [\"Positive aspect 1\", \"Positive aspect 2\", ...]
247+
}
248+
249+
Follow these steps to generate the code review:
250+
251+
1. Analyze the provided context, including staged changes, recent commits, and project metadata.
252+
2. Evaluate the code quality, looking for potential issues, improvements, and good practices.
253+
3. Create a concise summary of the changes and their quality.
254+
4. List specific issues found in the code.
255+
5. Provide actionable suggestions for improvements.
256+
6. Acknowledge positive aspects and good practices in the code.
257+
7. Construct the final JSON object with all components.
258+
259+
Here's a minimal example of the expected output format:
260+
261+
{
262+
\"summary\": \"The changes implement a new authentication system with good separation of concerns, but lacks proper error handling in several places.\",
263+
\"code_quality\": \"The code is generally well-structured with clear naming conventions. The architecture follows established patterns, but there are some inconsistencies in error handling approaches.\",
264+
\"suggestions\": [\"Consider implementing a consistent error handling strategy across all authentication operations\", \"Add unit tests for edge cases in the token validation logic\"],
265+
\"issues\": [\"Missing error handling in the user registration flow\", \"Potential race condition in token refresh mechanism\"],
266+
\"positive_aspects\": [\"Good separation of concerns with clear service boundaries\", \"Consistent naming conventions throughout the added components\"]
267+
}
268+
269+
Ensure that your response is a valid JSON object matching this structure.
270+
"
271+
);
272+
273+
prompt.push_str(&review_schema_str);
274+
275+
Ok(prompt)
276+
}
277+
278+
/// Creates a user prompt for code review generation
279+
pub fn create_review_user_prompt(context: &CommitContext) -> String {
280+
let scorer = RelevanceScorer::new();
281+
let relevance_scores = scorer.score(context);
282+
let detailed_changes = format_detailed_changes(&context.staged_files, &relevance_scores);
283+
284+
let prompt = format!(
285+
"Based on the following context, generate a code review:\n\n\
286+
Branch: {}\n\n\
287+
Recent commits:\n{}\n\n\
288+
Staged changes:\n{}\n\n\
289+
Project metadata:\n{}\n\n\
290+
Detailed changes:\n{}",
291+
context.branch,
292+
format_recent_commits(&context.recent_commits),
293+
format_staged_files(&context.staged_files, &relevance_scores),
294+
format_project_metadata(&context.project_metadata),
295+
detailed_changes
296+
);
297+
298+
log_debug!("Review context details:\n{}", detailed_changes);
299+
300+
prompt
301+
}

src/commit/review.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use super::service::IrisCommitService;
2+
use crate::common::CommonParams;
3+
use crate::config::Config;
4+
use crate::llm_providers::LLMProviderType;
5+
use crate::messages;
6+
use crate::ui;
7+
use anyhow::{Context, Result};
8+
use std::str::FromStr;
9+
use std::sync::Arc;
10+
use colored::Colorize;
11+
12+
/// Handles the review command which generates an AI code review of staged changes
13+
pub async fn handle_review_command(
14+
common: CommonParams,
15+
print: bool,
16+
) -> Result<()> {
17+
let mut config = Config::load()?;
18+
common.apply_to_config(&mut config)?;
19+
let current_dir = std::env::current_dir()?;
20+
21+
let provider_type = LLMProviderType::from_str(&config.default_provider)?;
22+
23+
let service = Arc::new(
24+
IrisCommitService::new(
25+
config.clone(),
26+
&current_dir.clone(),
27+
provider_type,
28+
false, // gitmoji not needed for review
29+
false, // verification not needed for review
30+
)
31+
.context("Failed to create IrisCommitService")?,
32+
);
33+
34+
// Check environment prerequisites
35+
if let Err(e) = service.check_environment() {
36+
ui::print_error(&format!("Error: {e}"));
37+
ui::print_info("\nPlease ensure the following:");
38+
ui::print_info("1. Git is installed and accessible from the command line.");
39+
ui::print_info("2. You are running this command from within a Git repository.");
40+
ui::print_info("3. You have set up your configuration using 'git-iris config'.");
41+
return Err(e);
42+
}
43+
44+
let git_info = service.get_git_info().await?;
45+
46+
if git_info.staged_files.is_empty() {
47+
ui::print_warning(
48+
"No staged changes. Please stage your changes before generating a review.",
49+
);
50+
ui::print_info("You can stage changes using 'git add <file>' or 'git add .'");
51+
return Ok(());
52+
}
53+
54+
let effective_instructions = common
55+
.instructions
56+
.unwrap_or_else(|| config.instructions.clone());
57+
let preset_str = common.preset.as_deref().unwrap_or("");
58+
59+
// Create and start the spinner
60+
let spinner = ui::create_spinner("");
61+
let random_message = messages::get_waiting_message();
62+
spinner.set_message(format!("{} {}", "🔮".cyan(), random_message.text));
63+
64+
// Generate the code review
65+
let review = service
66+
.generate_review(preset_str, &effective_instructions)
67+
.await?;
68+
69+
// Stop the spinner
70+
spinner.finish_and_clear();
71+
72+
// Print the review to stdout or save to file if requested
73+
if print {
74+
// Just print to stdout
75+
println!("\n{}", review.format());
76+
} else {
77+
// Add a fancy version header
78+
println!("\n{} {}\n", "🔮".cyan(), "Git-Iris Code Review".bright_magenta().bold());
79+
println!("{}", review.format());
80+
}
81+
82+
Ok(())
83+
}

0 commit comments

Comments
 (0)