Skip to main content

mirror_log/
infer.rs

1use chrono::{Duration, Utc};
2use std::collections::HashMap;
3use std::path::Path;
4
5use crate::stage::StagedEvent;
6
7#[derive(Debug, Clone)]
8pub struct Pattern {
9    pub description: String,
10    pub source_events: Vec<String>, // list of event IDs that triggered this pattern
11}
12
13pub fn detect_patterns(staging_dir: &Path) -> Result<Vec<Pattern>, Box<dyn std::error::Error>> {
14    let mut patterns = Vec::new();
15
16    let events = StagedEvent::load_all(staging_dir)?;
17
18    if events.is_empty() {
19        return Ok(patterns);
20    }
21
22    let one_week_ago = Utc::now() - Duration::weeks(1);
23
24    // Pattern 1: Frequent shell commands (nushell-history)
25    let mut shell_command_counts: HashMap<String, (i32, Vec<String>)> = HashMap::new();
26    for event in &events {
27        if event.source == "nushell-history" && event.timestamp_utc() > one_week_ago {
28            let entry = shell_command_counts
29                .entry(event.content.clone())
30                .or_insert((0, Vec::new()));
31            entry.0 += 1;
32            entry.1.push(event.id.clone());
33        }
34    }
35
36    for (command, (count, source_ids)) in &shell_command_counts {
37        if *count >= 3 {
38            patterns.push(Pattern {
39                description: format!(
40                    "* You ran `{}` {} times in the last week — this suggests you rely on it for routine tasks.",
41                    command, count
42                ),
43                source_events: source_ids.clone(),
44            });
45        }
46    }
47
48    // Pattern 2: Repeated dotfile edits (e.g., .config, .bashrc, .rustfmt.toml)
49    let mut dotfile_edits: HashMap<String, (i32, Vec<String>)> = HashMap::new();
50    for event in &events {
51        if event.source.starts_with("dotfile") && event.timestamp_utc() > one_week_ago {
52            let entry = dotfile_edits
53                .entry(event.content.clone())
54                .or_insert((0, Vec::new()));
55            entry.0 += 1;
56            entry.1.push(event.id.clone());
57        }
58    }
59
60    for (content, (count, source_ids)) in &dotfile_edits {
61        if *count >= 2 {
62            patterns.push(Pattern {
63                description: format!(
64                    "* You edited a configuration file with content like \"{}\" {} times — this suggests iterative refinement of your workflow.",
65                    content, count
66                ),
67                source_events: source_ids.clone(),
68            });
69        }
70    }
71
72    // Pattern 3: Sensitive content (e.g., passwords, keys)
73    for event in &events {
74        if event.content.contains("password")
75            || event.content.contains("secret")
76            || event.content.contains("key=")
77        {
78            patterns.push(Pattern {
79                description: format!(
80                    "* You entered sensitive data: \"{}\" — consider using a password manager.",
81                    event.content
82                ),
83                source_events: vec![event.id.clone()],
84            });
85        }
86    }
87
88    Ok(patterns)
89}