Skip to main content

stynx_code_skills/domain/
skill.rs

1use std::path::PathBuf;
2use serde::Deserialize;
3
4/// Newtype wrapper for a skill identifier.
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct SkillId(pub String);
7
8impl SkillId {
9    pub fn new(id: impl Into<String>) -> Self {
10        Self(id.into())
11    }
12
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16}
17
18impl std::fmt::Display for SkillId {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        write!(f, "{}", self.0)
21    }
22}
23
24/// Metadata parsed from the YAML frontmatter of a skill file.
25#[derive(Debug, Clone, Deserialize)]
26pub struct SkillMetadata {
27    pub name: String,
28    #[serde(default)]
29    pub description: String,
30    #[serde(default)]
31    pub triggers: Vec<String>,
32    pub model: Option<String>,
33    #[serde(default)]
34    pub is_hidden: bool,
35}
36
37/// Where a skill was loaded from.
38#[derive(Debug, Clone)]
39pub enum SkillSource {
40    UserSkill(PathBuf),
41    ProjectSkill(PathBuf),
42    Bundled,
43    Plugin(String),
44}
45
46/// A fully loaded skill with metadata, markdown body, and its origin.
47#[derive(Debug, Clone)]
48pub struct Skill {
49    pub metadata: SkillMetadata,
50    /// The full markdown body (everything after the frontmatter).
51    pub content: String,
52    pub source: SkillSource,
53}
54
55impl Skill {
56    /// Replace `{{ARGUMENTS}}` in the content with the given arguments string.
57    pub fn expand_template(&self, arguments: &str) -> String {
58        self.content.replace("{{ARGUMENTS}}", arguments)
59    }
60}