Skip to main content

fastskill_core/validation/
dir_structure.rs

1//! Skill directory structure validation (SKILL.md presence, scripts/references/assets).
2
3use crate::validation::result::{ErrorSeverity, ValidationResult};
4use std::path::Path;
5use tokio::fs;
6
7pub(crate) fn ensure_skill_md_exists(
8    skill_path: &Path,
9    mut result: ValidationResult,
10) -> ValidationResult {
11    let skill_file = skill_path.join("SKILL.md");
12    if !skill_file.exists() {
13        result = result.with_error(
14            "structure",
15            "Missing required SKILL.md file",
16            ErrorSeverity::Critical,
17        );
18    }
19    result
20}
21
22pub(crate) async fn scan_skill_directory_entries(
23    skill_path: &Path,
24    mut result: ValidationResult,
25) -> Result<(bool, bool, bool, ValidationResult), crate::core::service::ServiceError> {
26    let mut has_scripts = false;
27    let mut has_references = false;
28    let mut has_assets = false;
29    let mut entries = fs::read_dir(skill_path).await?;
30    while let Some(entry) = entries.next_entry().await? {
31        let path = entry.path();
32        let name = path
33            .file_name()
34            .map(|n| n.to_string_lossy().to_string())
35            .unwrap_or_else(|| "unknown".to_string());
36        match name.as_str() {
37            "SKILL.md" => {}
38            "scripts" => {
39                if path.is_dir() {
40                    has_scripts = true;
41                } else {
42                    result = result.with_warning("structure", "scripts should be a directory");
43                }
44            }
45            "references" => {
46                if path.is_dir() {
47                    has_references = true;
48                } else {
49                    result = result.with_warning("structure", "references should be a directory");
50                }
51            }
52            "assets" => {
53                if path.is_dir() {
54                    has_assets = true;
55                } else {
56                    result = result.with_warning("structure", "assets should be a directory");
57                }
58            }
59            _ => {
60                result = result
61                    .with_warning("structure", &format!("Unexpected file/directory: {}", name));
62            }
63        }
64    }
65    Ok((has_scripts, has_references, has_assets, result))
66}