Skip to main content

git_iris/companion/
branch_memory.rs

1//! Branch memory for Iris Companion
2//!
3//! Remembers context per branch across sessions.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8
9/// Focus state - where the user was last working
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct FileFocus {
12    /// Path to the focused file
13    pub path: PathBuf,
14    /// Line number in the file
15    pub line: usize,
16    /// When this focus was recorded
17    pub timestamp: DateTime<Utc>,
18}
19
20impl FileFocus {
21    /// Create a new file focus
22    #[must_use]
23    pub fn new(path: PathBuf, line: usize) -> Self {
24        Self {
25            path,
26            line,
27            timestamp: Utc::now(),
28        }
29    }
30}
31
32/// Per-branch persistent memory
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct BranchMemory {
35    /// Branch name
36    pub branch_name: String,
37    /// When this branch was first visited
38    pub created_at: DateTime<Utc>,
39    /// When this branch was last visited
40    pub last_visited: DateTime<Utc>,
41    /// Last focused file and line
42    pub last_focus: Option<FileFocus>,
43    /// User notes for this branch
44    pub notes: Vec<String>,
45    /// Number of sessions on this branch
46    pub session_count: u32,
47    /// Number of commits made on this branch (across sessions)
48    pub total_commits: u32,
49}
50
51impl BranchMemory {
52    /// Create new branch memory
53    #[must_use]
54    pub fn new(branch_name: String) -> Self {
55        let now = Utc::now();
56        Self {
57            branch_name,
58            created_at: now,
59            last_visited: now,
60            last_focus: None,
61            notes: Vec::new(),
62            session_count: 0,
63            total_commits: 0,
64        }
65    }
66
67    /// Record a new session visit
68    pub fn record_visit(&mut self) {
69        self.last_visited = Utc::now();
70        self.session_count += 1;
71    }
72
73    /// Update last focus
74    pub fn set_focus(&mut self, path: PathBuf, line: usize) {
75        self.last_focus = Some(FileFocus::new(path, line));
76    }
77
78    /// Clear focus
79    pub fn clear_focus(&mut self) {
80        self.last_focus = None;
81    }
82
83    /// Add a note
84    pub fn add_note(&mut self, note: String) {
85        self.notes.push(note);
86    }
87
88    /// Record a commit
89    pub fn record_commit(&mut self) {
90        self.total_commits += 1;
91    }
92
93    /// Time since last visit
94    #[must_use]
95    pub fn time_since_last_visit(&self) -> chrono::Duration {
96        Utc::now() - self.last_visited
97    }
98
99    /// Check if this is a returning visit (visited before more than 5 minutes ago)
100    #[must_use]
101    pub fn is_returning_visit(&self) -> bool {
102        self.session_count > 0 && self.time_since_last_visit() > chrono::Duration::minutes(5)
103    }
104
105    /// Generate a welcome message if returning
106    #[must_use]
107    pub fn welcome_message(&self) -> Option<String> {
108        if !self.is_returning_visit() {
109            return None;
110        }
111
112        let duration = self.time_since_last_visit();
113        let time_str = if duration.num_days() > 0 {
114            format!("{} days ago", duration.num_days())
115        } else if duration.num_hours() > 0 {
116            format!("{} hours ago", duration.num_hours())
117        } else {
118            format!("{} minutes ago", duration.num_minutes())
119        };
120
121        Some(format!(
122            "Welcome back to {}! Last here {}",
123            self.branch_name, time_str
124        ))
125    }
126}