Skip to main content

oxios_kernel/kernel_handle/
extension_api.rs

1//! Extension API — skills (RFC-009 Final).
2//!
3//! `ExtensionApi` is the unified facade for the skill system.
4//! The legacy ProgramManager and HostToolValidator have been removed.
5
6use crate::skill::{RequirementsCheck, Skill, SkillEntry, SkillManager, SkillMeta};
7use std::sync::Arc;
8
9/// Extension system calls.
10pub struct ExtensionApi {
11    /// Unified skill manager.
12    pub(crate) skill_manager: Arc<SkillManager>,
13}
14
15impl ExtensionApi {
16    /// Create a new ExtensionApi with only a SkillManager.
17    pub fn new(skill_manager: Arc<SkillManager>) -> Self {
18        Self { skill_manager }
19    }
20
21    // ── Skill methods ───────────────────────────────────────────────
22
23    /// List installed skill entries.
24    pub async fn list_skills_entries(&self) -> Vec<SkillEntry> {
25        self.skill_manager.list_skills().await
26    }
27
28    /// Get skill details.
29    pub async fn get_skill_entry(&self, name: &str) -> Option<SkillEntry> {
30        self.skill_manager.get_skill(name).await
31    }
32
33    /// Enable a skill.
34    pub async fn enable_skill(&self, name: &str) -> anyhow::Result<()> {
35        self.skill_manager.set_enabled(name, true).await
36    }
37
38    /// Disable a skill.
39    pub async fn disable_skill(&self, name: &str) -> anyhow::Result<()> {
40        self.skill_manager.set_enabled(name, false).await
41    }
42
43    /// Check requirements for a skill.
44    pub async fn check_skill_requirements(&self, name: &str) -> Option<RequirementsCheck> {
45        self.skill_manager
46            .get_skill(name)
47            .await
48            .map(|e| e.eligibility)
49    }
50
51    /// List all skills (metadata only).
52    pub async fn list_skills(&self) -> anyhow::Result<Vec<SkillMeta>> {
53        Ok(self.skill_manager.list_skills_meta().await)
54    }
55
56    /// Load skill by name.
57    pub async fn load_skill(&self, name: &str) -> anyhow::Result<Option<Skill>> {
58        self.skill_manager.load_skill(name).await
59    }
60
61    /// Create a new skill.
62    pub async fn create_skill(
63        &self,
64        name: &str,
65        description: &str,
66        content: &str,
67    ) -> anyhow::Result<()> {
68        self.skill_manager
69            .create_skill(name, description, content)
70            .await
71    }
72
73    /// Delete a skill.
74    pub async fn delete_skill(&self, name: &str) -> anyhow::Result<()> {
75        self.skill_manager.delete_skill(name).await
76    }
77
78    /// Write a skill's raw `SKILL.md` verbatim (frontmatter preserved) and
79    /// reindex. For inline edits and `.md` text imports — never strips
80    /// rich frontmatter the way [`create_skill`] does.
81    pub async fn write_skill_raw(&self, name: &str, raw: &str) -> anyhow::Result<SkillEntry> {
82        self.skill_manager.write_skill_raw(name, raw).await
83    }
84
85    /// Import a skill from `.zip` / `.skill` archive bytes. Extracts, derives
86    /// the name from frontmatter, moves into `skills_dir`, records provenance.
87    pub async fn import_skill_zip(
88        &self,
89        name_hint: &str,
90        bytes: &[u8],
91    ) -> anyhow::Result<SkillEntry> {
92        self.skill_manager.import_skill_zip(name_hint, bytes).await
93    }
94
95    /// Import a skill from raw `SKILL.md` text (frontmatter preserved),
96    /// deriving the name from the frontmatter. For text-paste / URL imports.
97    pub async fn import_skill_text(
98        &self,
99        content: &str,
100        name_hint: Option<&str>,
101    ) -> anyhow::Result<SkillEntry> {
102        self.skill_manager
103            .import_skill_text(content, name_hint)
104            .await
105    }
106
107    /// Skill manager reference.
108    pub fn skill_manager(&self) -> &Arc<SkillManager> {
109        &self.skill_manager
110    }
111}