agentshield/adapter/
mod.rs1pub mod crewai;
2pub mod cursor_rules;
3pub mod gpt_actions;
4pub mod hermes;
5pub mod langchain;
6pub mod mcp;
7pub(super) mod mcp_metadata;
8pub mod openclaw;
9
10use std::path::Path;
11
12use crate::config::ScanPathFilter;
13use crate::error::{Result, ShieldError};
14use crate::ir::{Framework, ScanTarget};
15
16pub trait Adapter: Send + Sync {
19 fn framework(&self) -> Framework;
21
22 fn detect(&self, root: &Path) -> bool;
24
25 fn load(&self, root: &Path, ignore_tests: bool) -> Result<Vec<ScanTarget>>;
28
29 fn load_with_filter(&self, root: &Path, filter: &ScanPathFilter) -> Result<Vec<ScanTarget>> {
30 self.load(root, filter.ignore_tests())
31 }
32}
33
34pub fn all_adapters() -> Vec<Box<dyn Adapter>> {
36 vec![
37 Box::new(mcp::McpAdapter),
38 Box::new(openclaw::OpenClawAdapter),
39 Box::new(hermes::HermesAgentAdapter),
40 Box::new(crewai::CrewAiAdapter),
41 Box::new(langchain::LangChainAdapter),
42 Box::new(gpt_actions::GptActionsAdapter),
43 Box::new(cursor_rules::CursorRulesAdapter),
44 ]
45}
46
47pub fn auto_detect_and_load(root: &Path, ignore_tests: bool) -> Result<Vec<ScanTarget>> {
52 let filter = ScanPathFilter::for_ignore_tests(ignore_tests);
53 auto_detect_and_load_with_filter(root, &filter)
54}
55
56pub fn auto_detect_and_load_with_filter(
57 root: &Path,
58 filter: &ScanPathFilter,
59) -> Result<Vec<ScanTarget>> {
60 let adapters = all_adapters();
61 let mut all_targets = Vec::new();
62
63 for adapter in &adapters {
64 if adapter.detect(root) {
65 match adapter.load_with_filter(root, filter) {
66 Ok(targets) => all_targets.extend(targets),
67 Err(e) => {
68 tracing::warn!(
69 framework = %adapter.framework(),
70 error = %e,
71 "adapter failed to load, skipping"
72 );
73 }
74 }
75 }
76 }
77
78 if all_targets.is_empty() {
79 return Err(ShieldError::NoAdapter(root.display().to_string()));
80 }
81
82 Ok(all_targets)
83}