git_paw/mcp/query/
session.rs1use rmcp::schemars;
9use serde::Serialize;
10
11use crate::coordination::inventory::fetch_status_agents_over_http;
12use crate::mcp::RepoContext;
13use crate::session::{self, SessionStatus};
14
15#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
17pub struct AgentRow {
18 pub branch: String,
20 pub cli: String,
22 pub status: String,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub last_seen_seconds: Option<u64>,
27}
28
29#[derive(Debug, Clone, Serialize, schemars::JsonSchema)]
31pub struct SessionSnapshot {
32 pub name: String,
34 pub mode: String,
36 pub status: String,
38 pub paused: bool,
40 pub agent_count: usize,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub broker_url: Option<String>,
45 pub agents: Vec<AgentRow>,
47}
48
49fn status_label(status: &SessionStatus) -> &'static str {
50 match status {
51 SessionStatus::Active => "active",
52 SessionStatus::Paused => "paused",
53 SessionStatus::Stopped => "stopped",
54 }
55}
56
57#[must_use]
60pub fn session_status(ctx: &RepoContext) -> Option<SessionSnapshot> {
61 let session = session::find_session_for_repo(&ctx.root).ok().flatten()?;
62
63 let live = ctx
65 .broker_url
66 .as_deref()
67 .and_then(|url| fetch_status_agents_over_http(url).ok())
68 .unwrap_or_default();
69
70 let agents = session
71 .worktrees
72 .iter()
73 .map(|w| {
74 let row = live.iter().find(|a| a.agent_id == w.branch);
75 AgentRow {
76 branch: w.branch.clone(),
77 cli: w.cli.clone(),
78 status: row.map(|r| r.status.clone()).unwrap_or_default(),
79 last_seen_seconds: row.map(|r| r.last_seen_seconds),
80 }
81 })
82 .collect::<Vec<_>>();
83
84 let mode = match session.mode {
85 crate::session::SessionMode::Bare => "bare",
86 crate::session::SessionMode::Supervisor => "supervisor",
87 };
88
89 let paused = session.status == SessionStatus::Paused;
90 Some(SessionSnapshot {
91 name: session.session_name.clone(),
92 mode: mode.to_string(),
93 status: status_label(&session.status).to_string(),
94 paused,
95 agent_count: session.worktrees.len(),
96 broker_url: ctx.broker_url.clone(),
97 agents,
98 })
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn no_session_yields_none() {
107 let tmp = tempfile::tempdir().unwrap();
109 let ctx = RepoContext {
110 root: tmp.path().to_path_buf(),
111 git_paw_dir: None,
112 broker_url: None,
113 server_name: "git-paw".to_string(),
114 };
115 assert!(session_status(&ctx).is_none());
116 }
117}