use std::collections::{VecDeque, HashMap};
use std::time::Instant;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum DeveloperPersona {
Expert,
Maintainer,
Learner,
AutomationSpecialist,
DebuggingSpecialist,
CodeReviewSpecialist,
DevOpsEngineer,
Architect,
}
#[derive(Clone, Debug)]
pub struct PersonaProfile {
pub persona: DeveloperPersona,
pub greeting: String,
pub guidance_level: GuidanceLevel,
pub preferences: DeveloperPreferences,
pub confidence: f64, pub detected_at: Instant,
}
#[derive(Clone, Debug)]
pub enum GuidanceLevel {
Minimal,
Suggestive,
Detailed,
}
#[derive(Clone, Debug, Default)]
pub struct DeveloperPreferences {
pub prefers_shortcut_commands: bool,
pub prefers_verbose_output: bool,
pub prefers_automated_tasks: bool,
pub prefers_code_generation: bool,
pub prefers_documentation: bool,
pub prefers_visual_feedback: bool,
pub command_patterns: Vec<String>,
pub project_focus: ProjectFocus,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub enum ProjectFocus {
Frontend,
Backend,
FullStack,
Infrastructure,
Testing,
#[default]
Unknown,
}
impl DeveloperPersona {
pub fn detect(history: &VecDeque<String>) -> (DeveloperPersona, f64) {
if history.is_empty() {
return (DeveloperPersona::Learner, 0.6); }
let shell_ratio = ratio(history, |cmd| !cmd.trim().starts_with('/'));
let ai_ratio = ratio(history, |cmd| cmd.trim().starts_with("/ask"));
let refactor_ratio = ratio(history, |cmd| cmd.trim().starts_with("/refactor"));
let test_ratio = ratio(history, |cmd| cmd.trim().starts_with("/test"));
let fix_ratio = ratio(history, |cmd| cmd.trim().starts_with("/fix"));
let review_ratio = ratio(history, |cmd| cmd.trim().starts_with("/review"));
let git_ratio = ratio(history, |cmd| cmd.contains("git") || cmd.contains("commit") || cmd.starts_with("git "));
let build_ratio = ratio(history, |cmd| cmd.contains("build") || cmd.contains("run") || cmd.contains("cargo") || cmd.contains("npm"));
let docker_ratio = ratio(history, |cmd| cmd.contains("docker") || cmd.contains("container"));
let mut scores: HashMap<DeveloperPersona, f64> = HashMap::new();
let expert_score = {
let command_diversity = calculate_command_diversity(history);
let complexity_score = calculate_command_complexity(history);
(command_diversity * 0.6 + complexity_score * 0.4).min(1.0)
};
let maintainer_score = {
let git_and_build = (git_ratio + build_ratio) * 0.4;
let testing_and_review = (test_ratio + review_ratio) * 0.6;
(git_and_build + testing_and_review).min(1.0)
};
let learner_score = {
let ai_interaction = ai_ratio;
let asking_questions = ai_ratio * 0.7 + (if history.len() < 5 { 0.3 } else { 0.0 });
(ai_interaction + asking_questions).min(1.0) / 2.0
};
let automation_score = shell_ratio.max(ratio(history, |cmd| cmd.contains("|") || cmd.contains("&&") || cmd.contains(">")));
let debugging_score = fix_ratio;
let review_score = review_ratio.max(refactor_ratio * 0.7);
let devops_score = (docker_ratio + build_ratio + git_ratio) / 3.0;
let architect_score = (ai_ratio * 0.4 + review_ratio * 0.3 + refactor_ratio * 0.3).min(1.0);
scores.insert(DeveloperPersona::Expert, expert_score as f64);
scores.insert(DeveloperPersona::Maintainer, maintainer_score as f64);
scores.insert(DeveloperPersona::Learner, learner_score as f64);
scores.insert(DeveloperPersona::AutomationSpecialist, automation_score as f64);
scores.insert(DeveloperPersona::DebuggingSpecialist, debugging_score as f64);
scores.insert(DeveloperPersona::CodeReviewSpecialist, review_score as f64);
scores.insert(DeveloperPersona::DevOpsEngineer, devops_score as f64);
scores.insert(DeveloperPersona::Architect, architect_score as f64);
let (highest_persona, highest_score) = scores
.into_iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
.unwrap_or((DeveloperPersona::Learner, 0.5));
(highest_persona, highest_score)
}
}
impl PersonaProfile {
pub fn from_history(history: &VecDeque<String>) -> Self {
let (persona, confidence) = DeveloperPersona::detect(history);
let greeting = match persona {
DeveloperPersona::Expert => {
if confidence > 0.8 {
"🔧 Expert mode engaged. Minimal guidance, maximum efficiency.".to_string()
} else {
"🔧 Developer patterns detected. Adjusting assistance level.".to_string()
}
},
DeveloperPersona::Maintainer => {
if confidence > 0.8 {
"🔄 Maintainer workflow detected. Optimizing for codebase stability.".to_string()
} else {
"🔄 Development workflow identified. Maintaining code quality focus.".to_string()
}
},
DeveloperPersona::Learner => {
if confidence > 0.8 {
"📚 Learner mode activated. Enhanced explanations enabled.".to_string()
} else {
"📚 Assisting with development questions and guidance.".to_string()
}
},
DeveloperPersona::AutomationSpecialist => {
if confidence > 0.8 {
"⚙️ Automation specialist detected. Streamlining repetitive tasks.".to_string()
} else {
"⚙️ Task automation workflows identified. Optimizing pipelines.".to_string()
}
},
DeveloperPersona::DebuggingSpecialist => {
if confidence > 0.8 {
"🪲 Debugging specialist engaged. Focused on error resolution.".to_string()
} else {
"🪲 Error investigation mode activated. Troubleshooting assistance ready.".to_string()
}
},
DeveloperPersona::CodeReviewSpecialist => {
if confidence > 0.8 {
"🔍 Code review expert detected. Quality analysis focused.".to_string()
} else {
"🔍 Review and refactoring assistance ready.".to_string()
}
},
DeveloperPersona::DevOpsEngineer => {
if confidence > 0.8 {
"🏗️ DevOps engineer detected. Infrastructure and deployment focused.".to_string()
} else {
"🏗️ Deployment and build optimization workflows identified.".to_string()
}
},
DeveloperPersona::Architect => {
if confidence > 0.8 {
"🏗️ Architect mode engaged. System design and architectural guidance.".to_string()
} else {
"🏗️ High-level design and planning assistance enabled.".to_string()
}
},
};
let guidance_level = match &persona {
DeveloperPersona::Expert | DeveloperPersona::AutomationSpecialist => GuidanceLevel::Minimal,
DeveloperPersona::Maintainer | DeveloperPersona::DevOpsEngineer | DeveloperPersona::DebuggingSpecialist => GuidanceLevel::Suggestive,
DeveloperPersona::Learner | DeveloperPersona::CodeReviewSpecialist | DeveloperPersona::Architect => GuidanceLevel::Detailed,
};
let preferences = DeveloperPreferences::from_persona(&persona, history);
Self {
persona,
greeting,
guidance_level,
preferences,
confidence,
detected_at: Instant::now(),
}
}
pub fn update_with_command(&mut self, command: &str) {
self.preferences.update_from_command(command);
self.confidence = self.confidence * 0.95; }
}
impl DeveloperPreferences {
fn from_persona(persona: &DeveloperPersona, history: &VecDeque<String>) -> Self {
let mut prefs = DeveloperPreferences::default();
prefs.prefers_shortcut_commands = matches!(persona,
DeveloperPersona::Expert | DeveloperPersona::AutomationSpecialist);
prefs.prefers_verbose_output = matches!(persona,
DeveloperPersona::Learner | DeveloperPersona::DebuggingSpecialist);
prefs.prefers_automated_tasks = matches!(persona,
DeveloperPersona::AutomationSpecialist | DeveloperPersona::DevOpsEngineer);
prefs.prefers_code_generation = matches!(persona,
DeveloperPersona::Learner | DeveloperPersona::Architect);
prefs.prefers_documentation = matches!(persona,
DeveloperPersona::Maintainer | DeveloperPersona::Architect);
prefs.prefers_visual_feedback = matches!(persona,
DeveloperPersona::Learner | DeveloperPersona::CodeReviewSpecialist);
let mut project_focus_counts = HashMap::new();
for cmd in history {
if cmd.contains("css") || cmd.contains("html") || cmd.contains("frontend") ||
cmd.contains("ui") || cmd.contains("react") || cmd.contains("vue") {
*project_focus_counts.entry(ProjectFocus::Frontend).or_insert(0) += 1;
} else if cmd.contains("api") || cmd.contains("backend") || cmd.contains("server") ||
cmd.contains("database") || cmd.contains("sql") || cmd.contains("rest") {
*project_focus_counts.entry(ProjectFocus::Backend).or_insert(0) += 1;
} else if cmd.contains("docker") || cmd.contains("k8s") || cmd.contains("deploy") ||
cmd.contains("devops") || cmd.contains("ci") {
*project_focus_counts.entry(ProjectFocus::Infrastructure).or_insert(0) += 1;
} else if cmd.contains("test") || cmd.contains("spec") || cmd.contains("unit") {
*project_focus_counts.entry(ProjectFocus::Testing).or_insert(0) += 1;
}
}
prefs.project_focus = project_focus_counts
.into_iter()
.max_by_key(|(_, count)| *count)
.map(|(focus, _)| focus)
.unwrap_or(ProjectFocus::Unknown);
prefs
}
fn update_from_command(&mut self, command: &str) {
if !self.command_patterns.contains(&command.to_string()) {
self.command_patterns.push(command.to_string());
if self.command_patterns.len() > 10 { self.command_patterns.remove(0);
}
}
}
}
fn ratio(history: &VecDeque<String>, predicate: impl Fn(&String) -> bool) -> f32 {
if history.is_empty() {
return 0.0;
}
let count = history.iter().filter(|cmd| predicate(cmd)).count() as f32;
count / history.len() as f32
}
fn calculate_command_diversity(history: &VecDeque<String>) -> f64 {
if history.is_empty() {
return 0.0;
}
let mut unique_commands = std::collections::HashSet::new();
for cmd in history {
let base_cmd = cmd.split_whitespace().next().unwrap_or("").to_string();
unique_commands.insert(base_cmd);
}
(unique_commands.len() as f64 / history.len() as f64).min(1.0)
}
fn calculate_command_complexity(history: &VecDeque<String>) -> f64 {
if history.is_empty() {
return 0.0;
}
let avg_complexity: f64 = history
.iter()
.map(|cmd| {
let word_count = cmd.split_whitespace().count();
let special_chars = cmd.chars().filter(|c| matches!(c, '|' | '&' | '>' | '<' | ';')).count();
((word_count + special_chars) as f64 / 10.0).min(1.0)
})
.sum::<f64>() / history.len() as f64;
avg_complexity
}