claude_agent/subagents/
provider.rs

1//! Subagent provider implementations using common infrastructure.
2
3use super::{SubagentDefinition, SubagentLoader};
4use crate::common::{
5    FileProvider, InMemoryProvider as GenericInMemoryProvider, SubagentLookupStrategy,
6};
7
8pub type InMemorySubagentProvider = GenericInMemoryProvider<SubagentDefinition>;
9
10pub type FileSubagentProvider =
11    FileProvider<SubagentDefinition, SubagentLoader, SubagentLookupStrategy>;
12
13pub fn file_subagent_provider() -> FileSubagentProvider {
14    FileSubagentProvider::new(SubagentLoader::new(), SubagentLookupStrategy)
15}
16
17pub type ChainSubagentProvider = crate::common::ChainProvider<SubagentDefinition>;
18
19#[cfg(test)]
20mod tests {
21    use super::*;
22    use crate::common::Provider;
23    use crate::subagents::builtin_subagents;
24
25    #[tokio::test]
26    async fn test_in_memory_provider() {
27        let subagent = SubagentDefinition::new("test", "Test agent", "Do something");
28        let provider = InMemorySubagentProvider::new().with_item(subagent);
29
30        let names = provider.list().await.unwrap();
31        assert_eq!(names, vec!["test"]);
32
33        let loaded = provider.get("test").await.unwrap();
34        assert!(loaded.is_some());
35        assert_eq!(loaded.unwrap().name, "test");
36    }
37
38    #[tokio::test]
39    async fn test_in_memory_with_builtins() {
40        let provider = InMemorySubagentProvider::new().with_items(builtin_subagents());
41        let names = provider.list().await.unwrap();
42
43        assert!(names.contains(&"explore".to_string()));
44        assert!(names.contains(&"plan".to_string()));
45        assert!(names.contains(&"general".to_string()));
46    }
47
48    #[tokio::test]
49    async fn test_chain_provider_priority() {
50        let low = InMemorySubagentProvider::new()
51            .with_item(SubagentDefinition::new("shared", "Low", "Low content"))
52            .with_priority(0);
53
54        let high = InMemorySubagentProvider::new()
55            .with_item(SubagentDefinition::new("shared", "High", "High content"))
56            .with_priority(10);
57
58        let chain = ChainSubagentProvider::new().with(low).with(high);
59
60        let subagent = chain.get("shared").await.unwrap().unwrap();
61        assert_eq!(subagent.description, "High");
62    }
63
64    #[tokio::test]
65    async fn test_file_provider() {
66        use crate::subagents::SubagentSourceType;
67
68        let temp = tempfile::tempdir().unwrap();
69        let provider = file_subagent_provider()
70            .with_path(temp.path())
71            .with_priority(5)
72            .with_source_type(SubagentSourceType::Project);
73
74        assert_eq!(provider.priority(), 5);
75        assert_eq!(provider.paths().len(), 1);
76    }
77
78    #[tokio::test]
79    async fn test_file_provider_load_subagent() {
80        let temp = tempfile::tempdir().unwrap();
81        let subagent_file = temp.path().join("test.md");
82        tokio::fs::write(
83            &subagent_file,
84            r#"---
85name: test-agent
86description: A test agent
87---
88
89Agent prompt here.
90"#,
91        )
92        .await
93        .unwrap();
94
95        let provider = file_subagent_provider().with_path(temp.path());
96
97        let subagent = provider.get("test").await.unwrap();
98        assert!(subagent.is_some());
99        assert_eq!(subagent.unwrap().name, "test-agent");
100    }
101}