vtcode_config/core/
skills.rs1use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
24#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
25#[serde(rename_all = "kebab-case")]
26pub struct BundledSkillsConfig {
27 #[serde(default = "default_bundled_skills_enabled")]
29 pub enabled: bool,
30}
31
32impl Default for BundledSkillsConfig {
33 fn default() -> Self {
34 Self { enabled: default_bundled_skills_enabled() }
35 }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
40#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
41#[serde(rename_all = "kebab-case")]
42pub struct SkillsConfig {
43 #[serde(default)]
45 pub bundled: BundledSkillsConfig,
46
47 #[serde(default = "default_render_mode")]
51 render_mode: SkillsRenderMode,
52
53 #[serde(default = "default_prompt_format")]
57 prompt_format: PromptFormat,
58
59 #[serde(default = "default_max_skills_in_prompt")]
61 max_skills_in_prompt: usize,
62
63 #[serde(default = "default_enable_auto_trigger")]
65 pub enable_auto_trigger: bool,
66
67 #[serde(default = "default_enable_description_matching")]
69 pub enable_description_matching: bool,
70
71 #[serde(default = "default_min_keyword_matches")]
73 pub min_keyword_matches: usize,
74}
75
76impl Default for SkillsConfig {
77 fn default() -> Self {
78 Self {
79 bundled: BundledSkillsConfig::default(),
80 render_mode: default_render_mode(),
81 prompt_format: default_prompt_format(),
82 max_skills_in_prompt: default_max_skills_in_prompt(),
83 enable_auto_trigger: default_enable_auto_trigger(),
84 enable_description_matching: default_enable_description_matching(),
85 min_keyword_matches: default_min_keyword_matches(),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
92#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
93#[serde(rename_all = "lowercase")]
94pub enum SkillsRenderMode {
95 #[default]
97 Lean,
98 Full,
100}
101
102#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
104#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
105#[serde(rename_all = "lowercase")]
106pub enum PromptFormat {
107 #[default]
109 Xml,
110 Markdown,
112}
113
114fn default_render_mode() -> SkillsRenderMode {
115 SkillsRenderMode::Lean
116}
117
118fn default_bundled_skills_enabled() -> bool {
119 true
120}
121
122fn default_prompt_format() -> PromptFormat {
123 PromptFormat::Xml
124}
125
126fn default_max_skills_in_prompt() -> usize {
127 10
128}
129
130fn default_enable_auto_trigger() -> bool {
131 true
132}
133
134fn default_enable_description_matching() -> bool {
135 true
136}
137
138fn default_min_keyword_matches() -> usize {
139 2
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_default_skills_config() {
148 let config = SkillsConfig::default();
149 assert!(config.bundled.enabled);
150 assert_eq!(config.render_mode, SkillsRenderMode::Lean);
151 assert_eq!(config.prompt_format, PromptFormat::Xml);
152 assert_eq!(config.max_skills_in_prompt, 10);
153 assert!(config.enable_auto_trigger);
154 assert!(config.enable_description_matching);
155 assert_eq!(config.min_keyword_matches, 2);
156 }
157
158 #[test]
159 fn test_skills_render_mode_serde() {
160 let lean = SkillsRenderMode::Lean;
162 let lean_json = serde_json::to_string(&lean).unwrap();
163 assert_eq!(lean_json, r#""lean""#);
164
165 let full = SkillsRenderMode::Full;
166 let full_json = serde_json::to_string(&full).unwrap();
167 assert_eq!(full_json, r#""full""#);
168
169 let lean_de: SkillsRenderMode = serde_json::from_str(r#""lean""#).unwrap();
171 assert_eq!(lean_de, SkillsRenderMode::Lean);
172
173 let full_de: SkillsRenderMode = serde_json::from_str(r#""full""#).unwrap();
174 assert_eq!(full_de, SkillsRenderMode::Full);
175 }
176
177 #[test]
178 fn test_prompt_format_serde() {
179 let xml = PromptFormat::Xml;
181 let xml_json = serde_json::to_string(&xml).unwrap();
182 assert_eq!(xml_json, r#""xml""#);
183
184 let markdown = PromptFormat::Markdown;
185 let markdown_json = serde_json::to_string(&markdown).unwrap();
186 assert_eq!(markdown_json, r#""markdown""#);
187
188 let xml_de: PromptFormat = serde_json::from_str(r#""xml""#).unwrap();
190 assert_eq!(xml_de, PromptFormat::Xml);
191
192 let markdown_de: PromptFormat = serde_json::from_str(r#""markdown""#).unwrap();
193 assert_eq!(markdown_de, PromptFormat::Markdown);
194 }
195
196 #[test]
197 fn test_skills_config_serde() {
198 let config = SkillsConfig {
199 bundled: BundledSkillsConfig { enabled: false },
200 render_mode: SkillsRenderMode::Full,
201 prompt_format: PromptFormat::Markdown,
202 max_skills_in_prompt: 15,
203 enable_auto_trigger: false,
204 enable_description_matching: false,
205 min_keyword_matches: 3,
206 };
207
208 let json = serde_json::to_string_pretty(&config).unwrap();
209 let deserialized: SkillsConfig = serde_json::from_str(&json).unwrap();
210 assert_eq!(config, deserialized);
211 }
212
213 #[test]
214 fn test_skills_config_toml_parses_bundled_settings() {
215 let config: SkillsConfig = toml::from_str(
216 r#"
217 render-mode = "full"
218 prompt-format = "markdown"
219 max-skills-in-prompt = 15
220 enable-auto-trigger = false
221 enable-description-matching = false
222 min-keyword-matches = 3
223
224 [bundled]
225 enabled = false
226 "#,
227 )
228 .unwrap();
229
230 assert!(!config.bundled.enabled);
231 assert_eq!(config.render_mode, SkillsRenderMode::Full);
232 assert_eq!(config.prompt_format, PromptFormat::Markdown);
233 assert_eq!(config.max_skills_in_prompt, 15);
234 assert!(!config.enable_auto_trigger);
235 assert!(!config.enable_description_matching);
236 assert_eq!(config.min_keyword_matches, 3);
237 }
238}