1use crate::types::SkillManifest;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use std::path::PathBuf;
5use vtcode_commons::MultiErrors;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default, Hash)]
9#[serde(rename_all = "snake_case")]
10pub enum SkillScope {
11 #[default]
13 User,
14 Repo,
16 System,
18 Admin,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct SkillMetadata {
24 pub name: String,
25 pub description: String,
26 #[serde(default)]
27 pub short_description: Option<String>,
28 pub path: PathBuf,
29 pub scope: SkillScope,
30 #[serde(default)]
31 pub manifest: Option<Box<SkillManifest>>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct SkillErrorInfo {
36 pub path: PathBuf,
37 pub message: String,
38}
39
40impl fmt::Display for SkillErrorInfo {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 write!(f, "{}: {}", self.path.display(), self.message)
43 }
44}
45
46impl std::error::Error for SkillErrorInfo {}
47
48#[derive(Debug, Clone, Default)]
49pub struct SkillLoadOutcome {
50 pub skills: Vec<SkillMetadata>,
51 pub errors: MultiErrors<SkillErrorInfo>,
52}
53
54#[cfg(test)]
55mod tests {
56 use super::SkillMetadata;
57 use crate::types::SkillManifest;
58
59 #[test]
60 fn boxed_manifest_is_smaller_than_inline_option() {
61 use std::mem::size_of;
62
63 assert!(size_of::<Option<Box<SkillManifest>>>() < size_of::<Option<SkillManifest>>());
64 assert!(size_of::<SkillMetadata>() < size_of::<SkillMetadataInlineManifest>());
65 }
66
67 #[expect(
68 dead_code,
69 reason = "Intentional compatibility, platform, test, or API-shape suppression."
70 )]
71 struct SkillMetadataInlineManifest {
72 name: String,
73 description: String,
74 short_description: Option<String>,
75 path: std::path::PathBuf,
76 scope: super::SkillScope,
77 manifest: Option<SkillManifest>,
78 }
79}