Skip to main content

crabtalk_runtime/skill/
handler.rs

1//! Crabtalk skill handler — initial load from disk.
2
3use crate::skill::{SkillRegistry, loader};
4use anyhow::Result;
5use std::path::PathBuf;
6use tokio::sync::Mutex;
7
8/// Skill registry owner.
9pub struct SkillHandler {
10    /// The skill registry (Mutex for interior-mutability from `dispatch_load_skill`).
11    pub registry: Mutex<SkillRegistry>,
12    /// Skill directories to search (local first, then packages).
13    pub skill_dirs: Vec<PathBuf>,
14}
15
16impl Default for SkillHandler {
17    fn default() -> Self {
18        Self {
19            registry: Mutex::new(SkillRegistry::new()),
20            skill_dirs: Vec::new(),
21        }
22    }
23}
24
25impl SkillHandler {
26    /// Load skills from multiple directories. Tolerates missing directories
27    /// by skipping them. Skills whose names appear in `disabled` are excluded.
28    pub fn load(skill_dirs: Vec<PathBuf>, disabled: &[String]) -> Result<Self> {
29        let mut registry = SkillRegistry::new();
30        for dir in &skill_dirs {
31            if !dir.exists() {
32                continue;
33            }
34            match loader::load_skills_dir(dir) {
35                Ok(r) => {
36                    let count = r.skills.len();
37                    for skill in &r.skills {
38                        if disabled.contains(&skill.name) {
39                            tracing::info!("skill '{}' disabled, skipping", skill.name);
40                        } else if registry.contains(&skill.name) {
41                            tracing::warn!(
42                                "skill '{}' from {} conflicts with already-loaded skill, skipping",
43                                skill.name,
44                                dir.display()
45                            );
46                        } else {
47                            registry.add(skill.clone());
48                        }
49                    }
50                    tracing::info!("loaded {count} skill(s) from {}", dir.display());
51                }
52                Err(e) => {
53                    tracing::warn!("could not load skills from {}: {e}", dir.display());
54                }
55            }
56        }
57        tracing::info!("total {} skill(s) loaded", registry.skills.len());
58        Ok(Self {
59            registry: Mutex::new(registry),
60            skill_dirs,
61        })
62    }
63}