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        && (meta.get("openclaw").is_some()
33            || meta.get("clawdbot").is_some()
34            || meta.get("clawdis").is_some())
35    {
36        return SkillFormat::OpenClaw;
37    }
38    for key in &[
39        "allowed-tools",
40        "arguments",
41        "when_to_use",
42        "argument-hint",
43        "effort",
44        "hooks",
45        "paths",
46    ] {
47        if value.get(*key).is_some() {
48            return SkillFormat::ClaudeCode;
49        }
50    }
51    for key in &[
52        "requires",
53        "install",
54        "primaryEnv",
55        "primary-env",
56        "skillKey",
57        "skill-key",
58    ] {
59        if value.get(*key).is_some() {
60            return SkillFormat::Oxios;
61        }
62    }
63    SkillFormat::AgentSkills
64}
65
66pub fn resolve_format(value: &Value, skill_dir: &Path) -> SkillFormat {
67    if skill_dir.join(".clawhub").join("origin.json").exists() {
68        return SkillFormat::OpenClaw;
69    }
70    detect_format(value)
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    #[test]
77    fn test_detect_oxios() {
78        let v: Value = serde_yaml::from_str("name: test\nrequires:\n  bins:\n    - git\n").unwrap();
79        assert_eq!(detect_format(&v), SkillFormat::Oxios);
80    }
81    #[test]
82    fn test_detect_openclaw() {
83        let v: Value = serde_yaml::from_str(
84            "name: test\nmetadata:\n  openclaw:\n    requires:\n      env:\n        - KEY\n",
85        )
86        .unwrap();
87        assert_eq!(detect_format(&v), SkillFormat::OpenClaw);
88    }
89    #[test]
90    fn test_detect_claude() {
91        let v: Value = serde_yaml::from_str("name: test\nallowed-tools: Read Grep\n").unwrap();
92        assert_eq!(detect_format(&v), SkillFormat::ClaudeCode);
93    }
94    #[test]
95    fn test_detect_standard() {
96        let v: Value = serde_yaml::from_str("name: test\ndescription: A skill\n").unwrap();
97        assert_eq!(detect_format(&v), SkillFormat::AgentSkills);
98    }
99}