syncable_cli/agent/session/
plan_mode.rs1use regex::Regex;
9
10#[derive(Debug, Clone)]
12pub struct IncompletePlan {
13 pub path: String,
14 pub filename: String,
15 pub done: usize,
16 pub pending: usize,
17 pub total: usize,
18}
19
20pub fn find_incomplete_plans(project_path: &std::path::Path) -> Vec<IncompletePlan> {
22 let plans_dir = project_path.join("plans");
23 if !plans_dir.exists() {
24 return Vec::new();
25 }
26
27 let task_regex = Regex::new(r"^\s*-\s*\[([ x~!])\]").unwrap();
28 let mut incomplete = Vec::new();
29
30 if let Ok(entries) = std::fs::read_dir(&plans_dir) {
31 for entry in entries.flatten() {
32 let path = entry.path();
33 if path.extension().map(|e| e == "md").unwrap_or(false)
34 && let Ok(content) = std::fs::read_to_string(&path)
35 {
36 let mut done = 0;
37 let mut pending = 0;
38 let mut in_progress = 0;
39
40 for line in content.lines() {
41 if let Some(caps) = task_regex.captures(line) {
42 match caps.get(1).map(|m| m.as_str()) {
43 Some("x") => done += 1,
44 Some(" ") => pending += 1,
45 Some("~") => in_progress += 1,
46 Some("!") => done += 1, _ => {}
48 }
49 }
50 }
51
52 let total = done + pending + in_progress;
53 if total > 0 && (pending > 0 || in_progress > 0) {
54 let rel_path = path
55 .strip_prefix(project_path)
56 .map(|p| p.display().to_string())
57 .unwrap_or_else(|_| path.display().to_string());
58
59 incomplete.push(IncompletePlan {
60 path: rel_path,
61 filename: path
62 .file_name()
63 .map(|n| n.to_string_lossy().to_string())
64 .unwrap_or_default(),
65 done,
66 pending: pending + in_progress,
67 total,
68 });
69 }
70 }
71 }
72 }
73
74 incomplete.sort_by(|a, b| b.filename.cmp(&a.filename));
76 incomplete
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
81pub enum PlanMode {
82 #[default]
84 Standard,
85 Planning,
87}
88
89impl PlanMode {
90 pub fn toggle(&self) -> Self {
92 match self {
93 PlanMode::Standard => PlanMode::Planning,
94 PlanMode::Planning => PlanMode::Standard,
95 }
96 }
97
98 pub fn is_planning(&self) -> bool {
100 matches!(self, PlanMode::Planning)
101 }
102
103 pub fn display_name(&self) -> &'static str {
105 match self {
106 PlanMode::Standard => "standard mode",
107 PlanMode::Planning => "plan mode",
108 }
109 }
110}