agentstatedeveloper_cli/commands/
workflow.rs1use anyhow::Result;
14use clap::Args;
15use serde_json::Value;
16
17use crate::config::Config;
18
19#[derive(Debug, Args)]
20pub struct WorkflowArgs {
21 #[arg(long, default_value = "20")]
23 pub last: usize,
24
25 #[arg(long)]
27 pub json: bool,
28}
29
30pub fn run(cfg: &Config, args: WorkflowArgs) -> Result<()> {
31 let dot_asd = cfg
32 .db_path
33 .parent()
34 .filter(|p| !p.as_os_str().is_empty())
35 .map(|p| p.join(".asd"))
36 .unwrap_or_else(|| std::path::PathBuf::from(".asd"));
37
38 let sessions_path = dot_asd.join("workflow-sessions.jsonl");
39
40 let raw = std::fs::read_to_string(&sessions_path).unwrap_or_default();
41 let mut sessions: Vec<Value> = raw
42 .lines()
43 .filter(|l| !l.trim().is_empty())
44 .filter_map(|l| serde_json::from_str(l).ok())
45 .collect();
46
47 sessions.reverse();
49 sessions.truncate(args.last);
50
51 if args.json {
52 let out = serde_json::json!({
53 "sessions": sessions,
54 "count": sessions.len(),
55 });
56 println!("{}", serde_json::to_string_pretty(&out)?);
57 return Ok(());
58 }
59
60 if sessions.is_empty() {
61 eprintln!("asd: no workflow sessions recorded yet — run `asd task-close` first");
62 return Ok(());
63 }
64
65 println!(
67 "{:<28} {:<22} {:<8} {:<6} {}",
68 "closed_at", "workflow_type", "ev_score", "syms", "missing_steps"
69 );
70 println!("{}", "-".repeat(90));
71 for s in &sessions {
72 let closed_at = s.get("closed_at").and_then(Value::as_str).unwrap_or("—");
73 let wf_type = s
74 .get("workflow_type")
75 .and_then(Value::as_str)
76 .unwrap_or("—");
77 let ev_score = s
78 .get("evidence_score")
79 .and_then(Value::as_f64)
80 .map(|f| format!("{:.2}", f))
81 .unwrap_or_else(|| "—".to_string());
82 let syms = s
83 .get("symbols_annotated")
84 .and_then(Value::as_u64)
85 .map(|n| n.to_string())
86 .unwrap_or_else(|| "—".to_string());
87 let missing = s
88 .get("missing_steps")
89 .and_then(Value::as_array)
90 .map(|arr| {
91 arr.iter()
92 .filter_map(Value::as_str)
93 .collect::<Vec<_>>()
94 .join(", ")
95 })
96 .unwrap_or_default();
97 let missing_display = if missing.is_empty() {
98 "—".to_string()
99 } else {
100 missing
101 };
102
103 let ts = if closed_at.len() > 27 {
105 &closed_at[..27]
106 } else {
107 closed_at
108 };
109 println!(
110 "{:<28} {:<22} {:<8} {:<6} {}",
111 ts, wf_type, ev_score, syms, missing_display
112 );
113 }
114
115 Ok(())
116}