Skip to main content

codex_skills/
model.rs

1use codex_protocol::protocol::Product;
2use codex_protocol::protocol::SkillScope;
3use codex_utils_absolute_path::AbsolutePathBuf;
4use codex_utils_path_uri::PathUri;
5
6/// Metadata for one skill materialized on the host filesystem.
7#[derive(Debug, Clone, PartialEq)]
8pub struct SkillMetadata {
9    pub name: String,
10    pub description: String,
11    pub short_description: Option<String>,
12    pub interface: Option<SkillInterface>,
13    pub dependencies: Option<SkillDependencies>,
14    pub policy: Option<SkillPolicy>,
15    /// Path to the SKILL.md file that declares this skill.
16    pub path_to_skills_md: AbsolutePathBuf,
17    pub scope: SkillScope,
18    pub plugin_id: Option<String>,
19}
20
21impl SkillMetadata {
22    pub fn allows_implicit_invocation(&self) -> bool {
23        self.policy
24            .as_ref()
25            .and_then(|policy| policy.allow_implicit_invocation)
26            .unwrap_or(true)
27    }
28
29    pub fn matches_product_restriction_for_product(
30        &self,
31        restriction_product: Option<Product>,
32    ) -> bool {
33        matches_product_restriction(self.policy.as_ref(), restriction_product)
34    }
35}
36
37/// URI-native metadata for one skill owned by an execution environment.
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct EnvironmentSkillMetadata {
40    pub path_to_skills_md: PathUri,
41    pub name: String,
42    pub description: String,
43    pub short_description: Option<String>,
44    pub dependencies: Option<SkillDependencies>,
45    pub policy: Option<SkillPolicy>,
46}
47
48impl EnvironmentSkillMetadata {
49    pub fn allows_implicit_invocation(&self) -> bool {
50        self.policy
51            .as_ref()
52            .and_then(|policy| policy.allow_implicit_invocation)
53            .unwrap_or(true)
54    }
55
56    pub fn matches_product_restriction(&self, restriction_product: Option<Product>) -> bool {
57        matches_product_restriction(self.policy.as_ref(), restriction_product)
58    }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Default)]
62pub struct SkillPolicy {
63    pub allow_implicit_invocation: Option<bool>,
64    // TODO: Enforce product gating in Codex skill selection/injection instead of only parsing and
65    // storing this metadata.
66    pub products: Vec<Product>,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct SkillInterface {
71    pub display_name: Option<String>,
72    pub short_description: Option<String>,
73    pub icon_small: Option<AbsolutePathBuf>,
74    pub icon_large: Option<AbsolutePathBuf>,
75    pub brand_color: Option<String>,
76    pub default_prompt: Option<String>,
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct SkillDependencies {
81    pub tools: Vec<SkillToolDependency>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub struct SkillToolDependency {
86    pub r#type: String,
87    pub value: String,
88    pub description: Option<String>,
89    pub transport: Option<String>,
90    pub command: Option<String>,
91    pub url: Option<String>,
92}
93
94#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
95pub enum SkillConfigRuleSelector {
96    Name(String),
97    Path(AbsolutePathBuf),
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Hash)]
101pub struct SkillConfigRule {
102    pub selector: SkillConfigRuleSelector,
103    pub enabled: bool,
104}
105
106#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
107pub struct SkillConfigRules {
108    pub entries: Vec<SkillConfigRule>,
109}
110
111fn matches_product_restriction(
112    policy: Option<&SkillPolicy>,
113    restriction_product: Option<Product>,
114) -> bool {
115    match policy {
116        Some(policy) => {
117            policy.products.is_empty()
118                || restriction_product
119                    .is_some_and(|product| product.matches_product_restriction(&policy.products))
120        }
121        None => true,
122    }
123}
124
125#[cfg(test)]
126#[path = "model_tests.rs"]
127mod tests;