1use crate::agent::McpServerEntry;
6use crate::deps::ProgramDep;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct Capability {
11 pub name: String,
12 pub version: String,
13 pub description: String,
14 #[serde(default)]
15 pub mcp_servers: Vec<McpServerEntry>,
16 #[serde(default)]
17 pub skills: Vec<String>,
18 #[serde(default)]
19 pub requires_programs: Vec<ProgramDep>,
20 #[serde(default)]
21 pub entitlements: CapabilityEntitlements,
22}
23
24#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27pub struct CapabilityEntitlements {
28 #[serde(default)]
29 pub spawn_programs: Vec<String>,
30 #[serde(default)]
31 pub network_hosts: Vec<String>,
32 #[serde(default)]
33 pub filesystem_read: Vec<String>,
34}
35
36#[cfg(test)]
37mod tests {
38 use super::*;
39
40 #[test]
41 fn capability_yaml_round_trips() {
42 let yaml = "\
43name: demo
44version: 1.0.0
45description: a demo capability
46skills:
47 - foo-skill
48requires_programs:
49 - name: vlc
50 detect:
51 command: vlc
52 reason: needed for playback
53entitlements:
54 network_hosts:
55 - 127.0.0.1
56";
57 let c: Capability = serde_yaml_ng::from_str(yaml).unwrap();
58 assert_eq!(c.name, "demo");
59 assert_eq!(c.skills, vec!["foo-skill"]);
60 assert_eq!(c.requires_programs.len(), 1);
61 assert_eq!(c.requires_programs[0].name, "vlc");
62 assert_eq!(c.entitlements.network_hosts, vec!["127.0.0.1"]);
63 let back = serde_yaml_ng::to_string(&c).unwrap();
64 let c2: Capability = serde_yaml_ng::from_str(&back).unwrap();
65 assert_eq!(c, c2);
66 }
67}