#![forbid(unsafe_code)]
mod env;
pub mod providers;
mod types;
use std::collections::HashMap;
pub use types::{AgentKind, DetectedAgent};
pub fn detect() -> Option<DetectedAgent> {
let env = env::Env::current();
providers::detect(&env)
}
pub fn detect_with_env(env: HashMap<String, String>) -> Option<DetectedAgent> {
let env = env::Env::from_map(env);
providers::detect(&env)
}
pub fn is_agent() -> bool {
detect()
.map(|a| matches!(a.kind, AgentKind::Agent | AgentKind::Hybrid))
.unwrap_or(false)
}
pub fn is_interactive() -> bool {
detect()
.map(|a| matches!(a.kind, AgentKind::Interactive | AgentKind::Hybrid))
.unwrap_or(false)
}
pub fn is_hybrid() -> bool {
detect()
.map(|a| a.kind == AgentKind::Hybrid)
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
fn env(pairs: &[(&str, &str)]) -> HashMap<String, String> {
pairs
.iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect()
}
#[test]
fn detect_returns_none_for_empty_env() {
assert!(detect_with_env(HashMap::new()).is_none());
}
#[test]
fn detect_with_env_finds_claude_code() {
let agent = detect_with_env(env(&[("CLAUDECODE", "1")])).unwrap();
assert_eq!(agent.id, "claude-code");
assert_eq!(agent.name, "Claude Code");
assert!(agent.is_agent());
assert!(!agent.is_interactive());
assert!(!agent.is_hybrid());
}
#[test]
fn detect_with_env_finds_codex() {
let agent = detect_with_env(env(&[("CODEX_THREAD_ID", "t-1")])).unwrap();
assert_eq!(agent.id, "codex");
}
#[test]
fn detect_with_env_finds_warp_as_hybrid() {
let agent = detect_with_env(env(&[("TERM_PROGRAM", "WarpTerminal")])).unwrap();
assert_eq!(agent.id, "warp");
assert!(agent.is_hybrid());
assert!(!agent.is_agent());
assert!(!agent.is_interactive());
}
#[test]
fn detect_with_env_finds_cursor_interactive() {
let agent = detect_with_env(env(&[("CURSOR_TRACE_ID", "t-1")])).unwrap();
assert_eq!(agent.id, "cursor");
assert!(agent.is_interactive());
}
#[test]
fn agent_kind_display() {
assert_eq!(AgentKind::Agent.to_string(), "agent");
assert_eq!(AgentKind::Interactive.to_string(), "interactive");
assert_eq!(AgentKind::Hybrid.to_string(), "hybrid");
}
#[test]
fn detected_agent_display() {
let agent = DetectedAgent {
id: "test",
name: "Test Agent",
kind: AgentKind::Agent,
};
assert_eq!(agent.to_string(), "Test Agent (agent)");
}
}