devboy_core/agents/mod.rs
1//! Agent detection.
2//!
3//! Walks `$HOME/` looking for deterministic on-disk traces of supported AI
4//! coding agents (Claude Code, GitHub Copilot CLI, Codex CLI, Kimi Code CLI,
5//! Cursor, Gemini CLI, Antigravity), and reports a structured snapshot per
6//! agent — install status, session count, last-used timestamp, and a score
7//! used to pick a primary candidate for `devboy onboard`.
8//!
9//! Architecture: one [`AgentDetector`] implementation per agent file, plus
10//! a [`registry::detect_all`] entrypoint that runs every detector
11//! sequentially and returns a sorted [`AgentSnapshot`] vector. Each detector
12//! is bound on filesystem I/O (a handful of `read_dir` + `metadata` calls);
13//! the total wall-clock is well under a second on a real machine, so we
14//! don't add a thread pool today.
15//!
16//! See ADR-017.
17
18pub mod antigravity;
19pub mod bundles;
20pub mod claude;
21pub mod codex;
22pub mod copilot;
23pub mod cursor;
24pub mod gemini;
25pub mod kimi;
26pub mod registry;
27pub mod score;
28
29mod fs_util;
30
31use std::path::{Path, PathBuf};
32
33use chrono::{DateTime, Utc};
34use serde::Serialize;
35
36pub use registry::{detect_all, detect_all_with_home};
37pub use score::{compute_score, pick_primary};
38
39/// Whether an agent is installed on the machine.
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
41#[serde(rename_all = "lowercase")]
42pub enum InstallStatus {
43 /// The agent's install marker (config dir or binary) was found.
44 Yes,
45 /// No traces found.
46 No,
47 /// We couldn't determine — e.g. the marker exists but is empty/ambiguous.
48 Unknown,
49}
50
51/// One row in the detector's report.
52#[derive(Debug, Clone, Serialize)]
53pub struct AgentSnapshot {
54 pub id: &'static str,
55 pub display_name: &'static str,
56 pub status: InstallStatus,
57 pub sessions: Option<u64>,
58 pub last_used: Option<DateTime<Utc>>,
59 pub score: f64,
60 pub paths_checked: Vec<PathBuf>,
61}
62
63/// Trait every per-agent detector implements.
64pub trait AgentDetector: Send + Sync {
65 /// Fn.
66 fn id(&self) -> &'static str;
67 /// Fn.
68 fn display_name(&self) -> &'static str;
69
70 /// Inspect `home` (the user's home directory) and produce a snapshot.
71 /// Detectors must not panic on missing paths — `InstallStatus::No` is the
72 /// expected outcome for absent agents.
73 fn detect(&self, home: &Path) -> AgentSnapshot;
74}