devboy_core/agents/
antigravity.rs1use std::path::Path;
18
19use super::fs_util::{dir_nonempty, max_mtime_in};
20use super::{AgentDetector, AgentSnapshot, InstallStatus};
21
22const ID: &str = "antigravity";
23const DISPLAY_NAME: &str = "Antigravity";
24
25pub struct AntigravityDetector;
26
27impl AgentDetector for AntigravityDetector {
28 fn id(&self) -> &'static str {
29 ID
30 }
31 fn display_name(&self) -> &'static str {
32 DISPLAY_NAME
33 }
34
35 fn detect(&self, home: &Path) -> AgentSnapshot {
36 let dir = home.join(".gemini/antigravity");
37 let conversations = dir.join("conversations");
38 let paths_checked = vec![dir.clone(), conversations.clone()];
39
40 let installed = dir.is_dir() && dir_nonempty(&dir);
41
42 if !installed {
43 return AgentSnapshot {
44 id: ID,
45 display_name: DISPLAY_NAME,
46 status: InstallStatus::No,
47 sessions: None,
48 last_used: None,
49 score: 0.0,
50 paths_checked,
51 };
52 }
53
54 let count = count_pb_files(&conversations);
55 let last_used = max_mtime_in(&conversations, |p| p.extension().is_some_and(|e| e == "pb"));
56
57 AgentSnapshot {
58 id: ID,
59 display_name: DISPLAY_NAME,
60 status: InstallStatus::Yes,
61 sessions: (count > 0).then_some(count),
62 last_used,
63 score: 0.0,
64 paths_checked,
65 }
66 }
67}
68
69fn count_pb_files(dir: &Path) -> u64 {
70 let Ok(entries) = std::fs::read_dir(dir) else {
71 return 0;
72 };
73 entries
74 .flatten()
75 .filter(|e| e.path().extension().is_some_and(|e| e == "pb"))
76 .count() as u64
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use std::fs;
83 use tempfile::tempdir;
84
85 #[test]
86 fn counts_pb_conversations() {
87 let home = tempdir().unwrap();
88 let conv = home.path().join(".gemini/antigravity/conversations");
89 fs::create_dir_all(&conv).unwrap();
90 fs::write(conv.join("aaaa.pb"), b"\x00").unwrap();
91 fs::write(conv.join("bbbb.pb"), b"\x00").unwrap();
92 fs::write(conv.join("readme.txt"), b"x").unwrap();
94
95 let snap = AntigravityDetector.detect(home.path());
96 assert_eq!(snap.status, InstallStatus::Yes);
97 assert_eq!(snap.sessions, Some(2));
98 }
99
100 #[test]
101 fn empty_antigravity_dir_means_not_installed() {
102 let home = tempdir().unwrap();
103 let snap = AntigravityDetector.detect(home.path());
105 assert_eq!(snap.status, InstallStatus::No);
106 }
107
108 #[test]
109 fn antigravity_empty_present_dir_means_not_installed() {
110 let home = tempdir().unwrap();
111 fs::create_dir_all(home.path().join(".gemini/antigravity")).unwrap();
112 let snap = AntigravityDetector.detect(home.path());
113 assert_eq!(snap.status, InstallStatus::No);
114 }
115
116 #[test]
117 fn antigravity_dir_with_only_marker_file_still_installed_no_sessions() {
118 let home = tempdir().unwrap();
119 let dir = home.path().join(".gemini/antigravity");
120 fs::create_dir_all(&dir).unwrap();
121 fs::write(dir.join("installation_id"), b"abc").unwrap();
122 let snap = AntigravityDetector.detect(home.path());
123 assert_eq!(snap.status, InstallStatus::Yes);
124 assert!(snap.sessions.is_none());
125 }
126}