Skip to main content

oxios_kernel/skill/
format.rs

1#![allow(missing_docs)]
2//! Skill format detection.
3
4use std::path::Path;
5
6use serde::{Deserialize, Serialize};
7use serde_yaml::Value;
8
9/// Detected skill format.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum SkillFormat {
13    Oxios,
14    OpenClaw,
15    ClaudeCode,
16    AgentSkills,
17}
18
19impl std::fmt::Display for SkillFormat {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        match self {
22            SkillFormat::Oxios => write!(f, "oxios"),
23            SkillFormat::OpenClaw => write!(f, "openclaw"),
24            SkillFormat::ClaudeCode => write!(f, "claude_code"),
25            SkillFormat::AgentSkills => write!(f, "agent_skills"),
26        }
27    }
28}
29
30pub fn detect_format(value: &Value) -> SkillFormat {
31    if let Some(meta) = value.get("metadata") {
32        if meta.get("openclaw").is_some()
33            || meta.get("clawdbot").is_some()
34            || meta.get("clawdis").is_some()
35        {
36            return SkillFormat::OpenClaw;
37        }
38    }
39    for key in &[
40        "allowed-tools",
41        "arguments",
42        "when_to_use",
43        "argument-hint",
44        "effort",
45        "hooks",
46        "paths",
47    ] {
48        if value.get(*key).is_some() {
49            return SkillFormat::ClaudeCode;
50        }
51    }
52    for key in &[
53        "requires",
54        "install",
55        "primaryEnv",
56        "primary-env",
57        "skillKey",
58        "skill-key",
59    ] {
60        if value.get(*key).is_some() {
61            return SkillFormat::Oxios;
62        }
63    }
64    SkillFormat::AgentSkills
65}
66
67pub fn resolve_format(value: &Value, skill_dir: &Path) -> SkillFormat {
68    if skill_dir.join(".clawhub").join("origin.json").exists() {
69        return SkillFormat::OpenClaw;
70    }
71    detect_format(value)
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77    #[test]
78    fn test_detect_oxios() {
79        let v: Value = serde_yaml::from_str("name: test\nrequires:\n  bins:\n    - git\n").unwrap();
80        assert_eq!(detect_format(&v), SkillFormat::Oxios);
81    }
82    #[test]
83    fn test_detect_openclaw() {
84        let v: Value = serde_yaml::from_str(
85            "name: test\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - KEY\n",
86        )
87        .unwrap();
88        assert_eq!(detect_format(&v), SkillFormat::OpenClaw);
89    }
90    #[test]
91    fn test_detect_claude() {
92        let v: Value = serde_yaml::from_str("name: test\nallowed-tools: Read Grep\n").unwrap();
93        assert_eq!(detect_format(&v), SkillFormat::ClaudeCode);
94    }
95    #[test]
96    fn test_detect_standard() {
97        let v: Value = serde_yaml::from_str("name: test\ndescription: A skill\n").unwrap();
98        assert_eq!(detect_format(&v), SkillFormat::AgentSkills);
99    }
100}