-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Labels
Description
Context
Currently have 3 separate multi-step modules that should be consolidated:
multi_step_analysis.rs
(515 lines)multi_step_integration.rs
(811 lines)simple_multi_step.rs
(129 lines)
These overlap significantly and should be organized as a single cohesive module.
Priority
🟡 HIGH - Major simplification of code organization
Steps
1. Create module structure
mkdir -p src/generation
touch src/generation/mod.rs
touch src/generation/multi_step.rs
2. Design the unified API in src/generation/multi_step.rs
//! Multi-step commit message generation.
//!
//! Implements a sophisticated analysis pipeline:
//! 1. Parse diff into individual files
//! 2. Analyze each file (lines changed, category, impact)
//! 3. Score files by impact
//! 4. Generate message candidates
//! 5. Select best candidate
use anyhow::Result;
use async_openai::config::OpenAIConfig;
use async_openai::Client;
use crate::config::AppConfig;
use crate::diff::parser::ParsedFile;
pub mod analysis;
pub mod scoring;
pub mod candidates;
pub mod local;
// Re-export commonly used types
pub use analysis::{FileAnalysis, analyze_file, analyze_file_via_api};
pub use scoring::{calculate_impact_scores, ImpactScore};
pub use candidates::{generate_candidates, select_best_candidate};
/// Main entry point for multi-step generation with API
pub async fn generate_with_api(
client: &Client<OpenAIConfig>,
model: &str,
diff: &str,
config: &AppConfig,
) -> Result<String> {
// High-level orchestration
// Move from multi_step_integration.rs generate_commit_message_multi_step
}
/// Main entry point for local multi-step generation (no API)
pub fn generate_local(
diff: &str,
config: &AppConfig,
) -> Result<String> {
// Move from multi_step_integration.rs generate_commit_message_local
}
3. Create src/generation/analysis.rs
//! File analysis for multi-step generation.
use anyhow::Result;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileAnalysis {
pub lines_added: u32,
pub lines_removed: u32,
pub category: FileCategory,
pub summary: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum FileCategory {
Source, Test, Config, Docs, Binary, Build,
}
/// Analyze a file locally without API
pub fn analyze_file(
path: &str,
diff_content: &str,
operation: &str,
) -> FileAnalysis {
// Move from multi_step_analysis.rs analyze_file function
}
/// Analyze a file using OpenAI API
pub async fn analyze_file_via_api(
client: &Client<OpenAIConfig>,
model: &str,
file: &ParsedFile,
) -> Result<FileAnalysis> {
// Move from multi_step_integration.rs call_analyze_function
}
/// Helper: Categorize file by path
fn categorize_file(path: &str) -> FileCategory {
// Move from multi_step_analysis.rs
}
#[cfg(test)]
mod tests {
// Move from multi_step_analysis.rs tests
}
4. Create src/generation/scoring.rs
//! Impact scoring for analyzed files.
use super::analysis::FileAnalysis;
pub struct ImpactScore {
pub file_path: String,
pub operation: String,
pub analysis: FileAnalysis,
pub score: f32,
}
pub fn calculate_impact_scores(
files: Vec<(String, String, FileAnalysis)>,
) -> Vec<ImpactScore> {
// Move from multi_step_analysis.rs calculate_impact_scores
}
fn calculate_single_score(
operation: &str,
analysis: &FileAnalysis,
) -> f32 {
// Move from multi_step_analysis.rs calculate_single_impact_score
}
5. Create src/generation/candidates.rs
//! Commit message candidate generation and selection.
use super::scoring::ImpactScore;
pub struct Candidate {
pub message: String,
pub style: CandidateStyle,
}
pub enum CandidateStyle {
Action, // "Add authentication"
Component, // "auth: implementation"
Impact, // "New feature for authentication"
}
pub fn generate_candidates(
scored_files: &[ImpactScore],
max_length: usize,
) -> Vec<Candidate> {
// Move from multi_step_analysis.rs generate_commit_messages
}
pub fn select_best_candidate(
candidates: &[Candidate],
) -> Option<String> {
// Selection logic
}
6. Create src/generation/local.rs
//! Local generation fallback (no API required).
use anyhow::Result;
use super::analysis;
use super::scoring;
use super::candidates;
pub fn generate_simple(
diff: &str,
max_length: usize,
) -> Result<String> {
// Move from simple_multi_step.rs generate_commit_message_simple_local
}
7. Create src/generation/mod.rs
//! Commit message generation strategies.
//!
//! Provides multiple approaches for generating commit messages:
//! - Multi-step analysis with API
//! - Local multi-step analysis
//! - Simple local generation
pub mod multi_step;
pub use multi_step::{generate_with_api, generate_local};
8. Update src/lib.rs
pub mod generation;
9. Update imports throughout codebase
Find and update all imports:
rg "multi_step_analysis" src/ -l
rg "multi_step_integration" src/ -l
rg "simple_multi_step" src/ -l
Change to:
use crate::generation::multi_step;
10. Delete old files
git rm src/multi_step_analysis.rs
git rm src/multi_step_integration.rs
git rm src/simple_multi_step.rs
Verification Criteria
✅ Pass:
-
src/generation/
module created with submodules - All multi-step logic consolidated (no duplication)
- Old files deleted
- All imports updated
-
cargo build
succeeds -
cargo test
passes all tests - Multi-step generation still works (test with commit)
- Local fallback still works (test without API key)
-
cargo clippy
shows no warnings - Module organization is clearer and more logical
Test manually
# Test API generation
export OPENAI_API_KEY=your-key
cd test-repo
echo "test" > file.txt
git add file.txt
git commit --no-edit
# Test local fallback
unset OPENAI_API_KEY
echo "test2" > file2.txt
git add file2.txt
git commit --no-edit
Estimated Time
6-8 hours (complex refactor)
Dependencies
- Issue Remove unused code #6 (Extract diff parsing) - needs diff::parser module
- Issue Allow for
git-ai hook reinstall
#3 (Standardize type names) - uses consistent types
Labels
- refactor
- module-structure
- generation
- breaking-change
Copilot