pub(crate) struct Pattern {
pub(crate) basename: &'static str,
pub(crate) tool_name: &'static str,
}
pub struct AgentDiscoveryClassifier {
patterns: Vec<Pattern>,
}
impl Default for AgentDiscoveryClassifier {
fn default() -> Self {
Self::new()
}
}
impl AgentDiscoveryClassifier {
pub fn new() -> Self {
Self::with_patterns(vec![
Pattern {
basename: "claude",
tool_name: "ClaudeCode",
},
Pattern {
basename: "codex",
tool_name: "Codex",
},
Pattern {
basename: "copilot",
tool_name: "GitHubCopilot",
},
Pattern {
basename: "windsurf",
tool_name: "WindsurfCascade",
},
Pattern {
basename: "cascade",
tool_name: "WindsurfCascade",
},
])
}
pub(crate) fn with_patterns(patterns: Vec<Pattern>) -> Self {
Self { patterns }
}
pub fn classify<'a>(&'a self, path: &'a str) -> Option<&'a str> {
let basename = path.rsplit('/').next().unwrap_or(path);
self.patterns
.iter()
.find(|p| p.basename == basename)
.map(|p| p.tool_name)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn classify_claude_by_basename() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify("/usr/local/bin/claude"), Some("ClaudeCode"));
}
#[test]
fn classify_codex_bare_name() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify("codex"), Some("Codex"));
}
#[test]
fn classify_windsurf_returns_tool_name() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify("/opt/windsurf/windsurf"), Some("WindsurfCascade"));
}
#[test]
fn classify_cascade_alias_returns_windsurf_cascade() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify("/usr/bin/cascade"), Some("WindsurfCascade"));
}
#[test]
fn classify_unknown_binary_returns_none() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify("/usr/bin/ls"), None);
assert_eq!(clf.classify("/usr/bin/python3"), None);
}
#[test]
fn classify_empty_path_returns_none() {
let clf = AgentDiscoveryClassifier::new();
assert_eq!(clf.classify(""), None);
}
#[test]
fn custom_pattern_is_matched() {
let clf = AgentDiscoveryClassifier::with_patterns(vec![Pattern {
basename: "my-agent",
tool_name: "MyCustomTool",
}]);
assert_eq!(clf.classify("/opt/my-agent"), Some("MyCustomTool"));
assert_eq!(clf.classify("/usr/bin/claude"), None);
}
}