use super::FsStorage;
use anyhow::Result;
use std::{collections::HashSet, fs};
use wcore::storage::Skill;
pub(super) fn list_skills(storage: &FsStorage) -> Result<Vec<Skill>> {
let mut skills = Vec::new();
let mut seen = HashSet::new();
for root in &storage.skill_roots {
if !root.exists() {
continue;
}
let entries = match fs::read_dir(root) {
Ok(e) => e,
Err(_) => continue,
};
for entry in entries {
let entry = match entry {
Ok(e) => e,
Err(_) => continue,
};
let path = entry.path();
if !path.is_dir() {
continue;
}
let name = match path.file_name().and_then(|n| n.to_str()) {
Some(n) if !n.starts_with('.') => n.to_owned(),
_ => continue,
};
if seen.contains(&name) {
continue;
}
let skill_path = path.join("SKILL.md");
if !skill_path.exists() {
continue;
}
let content = match fs::read_to_string(&skill_path) {
Ok(c) => c,
Err(e) => {
tracing::warn!("failed to read {}: {e}", skill_path.display());
continue;
}
};
match crate::hooks::skill::loader::parse_skill_md(&content) {
Ok(skill) => {
seen.insert(name);
skills.push(skill);
}
Err(e) => tracing::warn!("failed to parse {}: {e}", skill_path.display()),
}
}
}
Ok(skills)
}
pub(super) fn load_skill(storage: &FsStorage, name: &str) -> Result<Option<Skill>> {
for root in &storage.skill_roots {
let skill_path = root.join(name).join("SKILL.md");
if !skill_path.exists() {
continue;
}
let content = fs::read_to_string(&skill_path)?;
let skill = crate::hooks::skill::loader::parse_skill_md(&content)?;
return Ok(Some(skill));
}
Ok(None)
}