i-self 0.4.3

Personal developer-companion CLI: scans your repos, indexes code semantically, watches your activity, and moves AI-agent sessions between tools (Claude Code, Aider, Goose, OpenAI Codex CLI, Continue.dev, OpenCode).
use crate::learning::{LearningPathGenerator, LearningPlan};
use crate::skills::{SkillGapAnalyzer, Skill, SkillLevel, get_common_job_profiles};
use anyhow::Result;
use clap::Parser;

#[derive(Parser)]
pub struct LearningCommand {
    #[arg(short, long)]
    pub job: Option<String>,

    #[arg(short, long)]
    pub skill: Option<String>,

    #[arg(short, long)]
    pub target_level: Option<String>,
}

impl LearningCommand {
    pub async fn run(&self) -> Result<()> {
        let mut analyzer = SkillGapAnalyzer::new();
        let skills = load_current_skills()?;
        analyzer.load_from_profile(skills);

        let gaps = if let Some(ref job_title) = self.job {
            let jobs = get_common_job_profiles();
            let job = jobs.iter().find(|j| j.title.to_lowercase().contains(&job_title.to_lowercase()));
            
            if let Some(job) = job {
                let analysis = analyzer.analyze_job(job);
                analysis.gaps
            } else {
                println!("Job profile '{}' not found.", job_title);
                return Ok(());
            }
        } else if let Some(ref skill_name) = self.skill {
            let skill_key = skill_name.to_lowercase();
            if let Some(skill) = analyzer.current_skills.get(&skill_key) {
                let target = self.target_level
                    .as_ref()
                    .and_then(|l| match l.to_lowercase().as_str() {
                        "beginner" => Some(SkillLevel::Beginner),
                        "intermediate" => Some(SkillLevel::Intermediate),
                        "advanced" => Some(SkillLevel::Advanced),
                        "expert" => Some(SkillLevel::Expert),
                        "master" => Some(SkillLevel::Master),
                        _ => None,
                    })
                    .unwrap_or(SkillLevel::Advanced);

                vec![crate::skills::SkillGap {
                    skill_name: skill_name.clone(),
                    current_level: skill.level,
                    required_level: target,
                    gap_score: target.to_score() as i32 - skill.level.to_score() as i32,
                    is_critical: true,
                    recommendation: format!("Learn {} to {} level", skill_name, target.label()),
                }]
            } else {
                println!("Skill '{}' not found in your profile.", skill_name);
                return Ok(());
            }
        } else {
            println!("Please specify --job or --skill");
            return Ok(());
        };

        let plan = LearningPathGenerator::generate_plan(&gaps, &analyzer.current_skills);
        print_learning_plan(&plan);

        Ok(())
    }
}

fn load_current_skills() -> Result<Vec<Skill>> {
    let mut skills = Vec::new();

    skills.push(Skill {
        name: "Rust".to_string(),
        level: SkillLevel::Advanced,
        years_experience: Some(3.0),
        last_used: Some("2024-01".to_string()),
        projects_count: 15,
    });

    skills.push(Skill {
        name: "Python".to_string(),
        level: SkillLevel::Advanced,
        years_experience: Some(5.0),
        last_used: Some("2024-01".to_string()),
        projects_count: 25,
    });

    skills.push(Skill {
        name: "JavaScript".to_string(),
        level: SkillLevel::Advanced,
        years_experience: Some(6.0),
        last_used: Some("2024-01".to_string()),
        projects_count: 20,
    });

    skills.push(Skill {
        name: "TypeScript".to_string(),
        level: SkillLevel::Intermediate,
        years_experience: Some(2.0),
        last_used: Some("2024-01".to_string()),
        projects_count: 8,
    });

    skills.push(Skill {
        name: "Docker".to_string(),
        level: SkillLevel::Intermediate,
        years_experience: Some(2.0),
        last_used: Some("2024-01".to_string()),
        projects_count: 10,
    });

    Ok(skills)
}

fn print_learning_plan(plan: &LearningPlan) {
    println!("\n=== Learning Plan ===\n");
    println!("Total Estimated Time: {:.1} hours", plan.total_estimated_hours);
    println!("Focus Areas: {}\n", plan.focus_areas.join(", "));

    for (idx, rec) in plan.recommendations.iter().enumerate() {
        let priority_label = match rec.priority {
            1 => "🔴 High",
            2 => "🟡 Medium",
            _ => "🟢 Low",
        };
        
        println!("{}. {} [{}]", idx + 1, rec.skill_name, priority_label);
        println!("   Current: {} → Target: {}", rec.current_level, rec.target_level);
        println!("   Reason: {}", rec.reason);
        
        if let Some(ref path) = rec.path {
            println!("   Estimated Time: {:.1} hours", path.estimated_total_hours);
            println!("   Resources:");
            for (ridx, resource) in path.resources.iter().enumerate() {
                println!("     {}. {} ({})", ridx + 1, resource.title, resource.difficulty);
                println!("        {}", resource.url);
            }
        }
        println!();
    }
}