Skip to main content

systemprompt_sync/export/
skills.rs

1//! Serialise a [`Skill`] into the disk layout expected by `SkillsLocalSync`.
2
3use super::escape_yaml;
4use crate::error::SyncResult;
5use std::fs;
6use std::path::Path;
7use systemprompt_agent::models::Skill;
8
9pub fn generate_skill_markdown(skill: &Skill) -> String {
10    format!(
11        "---\ndescription: \"{}\"\n---\n\n{}",
12        escape_yaml(&skill.description),
13        &skill.instructions
14    )
15}
16
17pub fn generate_skill_config(skill: &Skill) -> String {
18    let tags_yaml = if skill.tags.is_empty() {
19        "[]".to_string()
20    } else {
21        skill
22            .tags
23            .iter()
24            .map(|t| format!("  - {}", t))
25            .collect::<Vec<_>>()
26            .join("\n")
27    };
28
29    format!(
30        r#"id: {}
31name: "{}"
32description: "{}"
33enabled: {}
34version: "1.0.0"
35file: "SKILL.md"
36assigned_agents:
37  - content
38tags:
39{}"#,
40        skill.id.as_str(),
41        escape_yaml(&skill.name),
42        escape_yaml(&skill.description),
43        skill.enabled,
44        tags_yaml
45    )
46}
47
48pub fn export_skill_to_disk(skill: &Skill, base_path: &Path) -> SyncResult<()> {
49    let skill_dir_name = skill.id.as_str().replace('_', "-");
50    let skill_dir = base_path.join(&skill_dir_name);
51    fs::create_dir_all(&skill_dir)?;
52
53    let skill_content = generate_skill_markdown(skill);
54    fs::write(skill_dir.join("SKILL.md"), skill_content)?;
55
56    let config_content = generate_skill_config(skill);
57    fs::write(skill_dir.join("config.yaml"), config_content)?;
58
59    Ok(())
60}