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>, }
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 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 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 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}