Skip to main content

vtcode_core/skills/loader/
resources.rs

1use std::fs;
2use std::path::Path;
3
4use anyhow::Result;
5
6pub fn load_skill_resources(skill_path: &Path) -> Result<Vec<crate::skills::types::SkillResource>> {
7    let mut resources = Vec::new();
8    let resource_dir = skill_path.join("scripts");
9
10    if resource_dir.exists() {
11        for entry in fs::read_dir(&resource_dir)? {
12            let entry = entry?;
13            let path = entry.path();
14
15            if path.is_file() {
16                let rel_path = path
17                    .strip_prefix(skill_path)
18                    .map(|p| p.to_string_lossy().to_string())
19                    .unwrap_or_default();
20
21                let resource_type = match path.extension().and_then(|e| e.to_str()) {
22                    Some("py") | Some("sh") | Some("bash") => crate::skills::types::ResourceType::Script,
23                    Some("md") => crate::skills::types::ResourceType::Markdown,
24                    Some("json") | Some("yaml") | Some("yml") => crate::skills::types::ResourceType::Reference,
25                    _ => crate::skills::types::ResourceType::Other(format!("{:?}", path.extension())),
26                };
27
28                resources.push(crate::skills::types::SkillResource { path: rel_path, resource_type, content: None });
29            }
30        }
31    }
32
33    // Check for references/ directory
34    let references_dir = skill_path.join("references");
35    if references_dir.exists() {
36        for entry in fs::read_dir(&references_dir)? {
37            let entry = entry?;
38            let path = entry.path();
39
40            if path.is_file() {
41                let rel_path = path
42                    .strip_prefix(skill_path)
43                    .map(|p| p.to_string_lossy().to_string())
44                    .unwrap_or_default();
45
46                let resource_type = match path.extension().and_then(|e| e.to_str()) {
47                    Some("md") => crate::skills::types::ResourceType::Reference,
48                    Some("json") | Some("yaml") | Some("yml") | Some("txt") | Some("csv") => {
49                        crate::skills::types::ResourceType::Reference
50                    }
51                    _ => crate::skills::types::ResourceType::Other(format!("{:?}", path.extension())),
52                };
53
54                resources.push(crate::skills::types::SkillResource { path: rel_path, resource_type, content: None });
55            }
56        }
57    }
58
59    // Check for assets/ directory
60    let assets_dir = skill_path.join("assets");
61    if assets_dir.exists() {
62        for entry in fs::read_dir(&assets_dir)? {
63            let entry = entry?;
64            let path = entry.path();
65
66            if path.is_file() {
67                let rel_path = path
68                    .strip_prefix(skill_path)
69                    .map(|p| p.to_string_lossy().to_string())
70                    .unwrap_or_default();
71
72                let resource_type = match path.extension().and_then(|e| e.to_str()) {
73                    Some("png") | Some("jpg") | Some("jpeg") | Some("gif") | Some("svg") => {
74                        crate::skills::types::ResourceType::Asset
75                    }
76                    Some("json") | Some("yaml") | Some("yml") | Some("txt") | Some("csv") => {
77                        crate::skills::types::ResourceType::Asset
78                    }
79                    _ => crate::skills::types::ResourceType::Asset,
80                };
81
82                resources.push(crate::skills::types::SkillResource { path: rel_path, resource_type, content: None });
83            }
84        }
85    }
86
87    Ok(resources)
88}
89
90pub(crate) fn is_executable_file(path: &Path) -> bool {
91    #[cfg(unix)]
92    {
93        use std::os::unix::fs::PermissionsExt;
94        if let Ok(meta) = path.metadata() {
95            return meta.permissions().mode() & 0o111 != 0;
96        }
97    }
98    #[cfg(windows)]
99    {
100        if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
101            return matches!(ext.to_lowercase().as_str(), "exe" | "bat" | "cmd");
102        }
103    }
104    false
105}