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 chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Complete developer profile
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DeveloperProfile {
    pub identity: DeveloperIdentity,
    pub github: GitHubProfile,
    pub coding_patterns: CodingPatterns,
    pub project_preferences: ProjectPreferences,
    pub work_history: WorkHistory,
    pub skills: SkillsProfile,
    pub last_updated: DateTime<Utc>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DeveloperIdentity {
    pub name: Option<String>,
    pub email: Option<String>,
    pub github_username: Option<String>,
    pub preferred_editor: Option<String>,
    pub shell: Option<String>,
    pub os: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct GitHubProfile {
    pub username: String,
    pub total_repositories: usize,
    pub total_contributions_30d: i64,
    pub primary_languages: Vec<(String, f64)>,
    pub most_active_repos: Vec<RepoActivity>,
    pub recent_prs: Vec<PullRequestSummary>,
    pub contribution_streak: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RepoActivity {
    pub name: String,
    pub commits_30d: i64,
    pub language: Option<String>,
    pub last_activity: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PullRequestSummary {
    pub repo: String,
    pub number: i64,
    pub title: String,
    pub state: String,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CodingPatterns {
    pub preferred_languages: Vec<String>,
    pub code_style: CodeStyle,
    pub commit_patterns: CommitPatterns,
    pub file_organization: FileOrganization,
    pub common_libraries: HashMap<String, Vec<String>>, // language -> libraries
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CodeStyle {
    pub indentation: Option<String>, // "tabs", "2_spaces", "4_spaces"
    pub line_length_preference: Option<i32>,
    pub naming_conventions: NamingConventions,
    pub documentation_style: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct NamingConventions {
    pub variables: Option<String>, // snake_case, camelCase, etc.
    pub functions: Option<String>,
    pub classes: Option<String>,
    pub constants: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CommitPatterns {
    pub message_style: Option<String>, // conventional, freeform, etc.
    pub average_message_length: i32,
    pub common_prefixes: Vec<String>,
    pub commit_frequency: CommitFrequency,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CommitFrequency {
    pub commits_per_day: f64,
    pub preferred_commit_times: Vec<i32>, // hours of day (0-23)
    pub most_active_day: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FileOrganization {
    pub preferred_project_structures: HashMap<String, Vec<String>>, // language -> common paths
    pub test_file_patterns: Vec<String>,
    pub documentation_locations: Vec<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProjectPreferences {
    pub preferred_frameworks: HashMap<String, Vec<String>>, // language -> frameworks
    pub build_tools: Vec<String>,
    pub ci_cd_preferences: Vec<String>,
    pub package_managers: Vec<String>,
    pub containerization: Vec<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct WorkHistory {
    pub current_focus: Vec<String>,
    pub recent_projects: Vec<ProjectSummary>,
    pub contribution_timeline: Vec<ContributionDay>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectSummary {
    pub name: String,
    pub description: Option<String>,
    pub language: Option<String>,
    pub role: String, // owner, contributor, maintainer
    pub start_date: Option<DateTime<Utc>>,
    pub last_activity: DateTime<Utc>,
    pub key_contributions: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContributionDay {
    pub date: DateTime<Utc>,
    pub commit_count: i64,
    pub repositories: Vec<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SkillsProfile {
    pub languages: Vec<LanguageSkill>,
    pub frameworks: Vec<FrameworkSkill>,
    pub tools: Vec<ToolSkill>,
    pub domains: Vec<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LanguageSkill {
    pub name: String,
    pub proficiency: Proficiency,
    pub proficiency_score: f64, // 0.0 to 1.0
    pub years_experience: f64,
    pub project_count: i64,
    pub lines_of_code: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrameworkSkill {
    pub name: String,
    pub language: String,
    pub proficiency: Proficiency,
    pub project_count: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolSkill {
    pub name: String,
    pub category: String, // editor, vcs, database, etc.
    pub proficiency: Proficiency,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Proficiency {
    Beginner,
    Intermediate,
    Advanced,
    Expert,
}

impl DeveloperProfile {
    pub fn update_timestamp(&mut self) {
        self.last_updated = Utc::now();
    }

    pub fn add_repo_activity(&mut self, name: String, commits: i64, language: Option<String>) {
        let activity = RepoActivity {
            name,
            commits_30d: commits,
            language,
            last_activity: Utc::now(),
        };
        
        // Remove if exists and add to front
        self.github.most_active_repos.retain(|r| r.name != activity.name);
        self.github.most_active_repos.insert(0, activity);
        self.github.most_active_repos.truncate(10);
    }
}