1use std::path::Path;
6
7#[derive(Debug, Clone)]
9pub struct BeadsTask {
10 pub id: String,
12 pub title: String,
14 pub description: Option<String>,
16}
17
18pub fn find_in_progress_tasks(working_dir: &str) -> Vec<BeadsTask> {
23 let jsonl_path = Path::new(working_dir).join(".beads/issues.jsonl");
24 let content = match std::fs::read_to_string(&jsonl_path) {
25 Ok(c) => c,
26 Err(_) => return Vec::new(),
27 };
28
29 let mut tasks: Vec<(String, BeadsTask)> = Vec::new(); for line in content.lines() {
32 if !line.contains("\"in_progress\"") {
34 continue;
35 }
36 if let Ok(val) = serde_json::from_str::<serde_json::Value>(line) {
37 let status = val
38 .get("status")
39 .and_then(|v| v.as_str())
40 .unwrap_or_default();
41 if status != "in_progress" {
42 continue;
43 }
44 let id = val
45 .get("id")
46 .and_then(|v| v.as_str())
47 .unwrap_or_default()
48 .to_string();
49 let title = val
50 .get("title")
51 .and_then(|v| v.as_str())
52 .unwrap_or_default()
53 .to_string();
54 let updated_at = val
55 .get("updated_at")
56 .and_then(|v| v.as_str())
57 .unwrap_or_default()
58 .to_string();
59
60 let description = val
61 .get("description")
62 .and_then(|v| v.as_str())
63 .filter(|s| !s.is_empty())
64 .map(|s| s.to_string());
65
66 if !id.is_empty() && !title.is_empty() {
67 tasks.push((
68 updated_at,
69 BeadsTask {
70 id,
71 title,
72 description,
73 },
74 ));
75 }
76 }
77 }
78
79 tasks.sort_by(|a, b| b.0.cmp(&a.0));
81 tasks.into_iter().map(|(_, task)| task).collect()
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use std::fs;
88
89 #[test]
90 fn test_find_in_progress_tasks_empty() {
91 let dir = tempfile::tempdir().unwrap();
92 let result = find_in_progress_tasks(dir.path().to_str().unwrap());
93 assert!(result.is_empty());
94 }
95
96 #[test]
97 fn test_find_in_progress_tasks() {
98 let dir = tempfile::tempdir().unwrap();
99 let beads_dir = dir.path().join(".beads");
100 fs::create_dir_all(&beads_dir).unwrap();
101 fs::write(
102 beads_dir.join("issues.jsonl"),
103 r#"{"id":"test-1","title":"Open task","status":"open","updated_at":"2026-01-01T00:00:00Z"}
104{"id":"test-2","title":"Active task","status":"in_progress","updated_at":"2026-01-02T00:00:00Z"}
105{"id":"test-3","title":"Done task","status":"closed","updated_at":"2026-01-03T00:00:00Z"}
106{"id":"test-4","title":"Another active","status":"in_progress","updated_at":"2026-01-04T00:00:00Z"}
107"#,
108 )
109 .unwrap();
110
111 let result = find_in_progress_tasks(dir.path().to_str().unwrap());
112 assert_eq!(result.len(), 2);
113 assert_eq!(result[0].title, "Another active"); assert_eq!(result[1].title, "Active task");
115 }
116}