agentshield/adapter/
mod.rs1pub mod crewai;
2pub mod cursor_rules;
3pub mod gpt_actions;
4pub mod hermes;
5pub mod langchain;
6pub mod mcp;
7pub mod openclaw;
8
9use std::path::Path;
10
11use crate::error::{Result, ShieldError};
12use crate::ir::{Framework, ScanTarget};
13
14pub trait Adapter: Send + Sync {
17 fn framework(&self) -> Framework;
19
20 fn detect(&self, root: &Path) -> bool;
22
23 fn load(&self, root: &Path, ignore_tests: bool) -> Result<Vec<ScanTarget>>;
26}
27
28pub fn all_adapters() -> Vec<Box<dyn Adapter>> {
30 vec![
31 Box::new(mcp::McpAdapter),
32 Box::new(openclaw::OpenClawAdapter),
33 Box::new(hermes::HermesAgentAdapter),
34 Box::new(crewai::CrewAiAdapter),
35 Box::new(langchain::LangChainAdapter),
36 Box::new(gpt_actions::GptActionsAdapter),
37 Box::new(cursor_rules::CursorRulesAdapter),
38 ]
39}
40
41pub fn auto_detect_and_load(root: &Path, ignore_tests: bool) -> Result<Vec<ScanTarget>> {
46 let adapters = all_adapters();
47 let mut all_targets = Vec::new();
48
49 for adapter in &adapters {
50 if adapter.detect(root) {
51 match adapter.load(root, ignore_tests) {
52 Ok(targets) => all_targets.extend(targets),
53 Err(e) => {
54 tracing::warn!(
55 framework = %adapter.framework(),
56 error = %e,
57 "adapter failed to load, skipping"
58 );
59 }
60 }
61 }
62 }
63
64 if all_targets.is_empty() {
65 return Err(ShieldError::NoAdapter(root.display().to_string()));
66 }
67
68 Ok(all_targets)
69}