use anyhow::{Result, anyhow};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use systemprompt_database::JsonRow;
use systemprompt_identifiers::{CategoryId, SkillId, SourceId};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Skill {
#[serde(rename = "skill_id")]
pub id: SkillId,
pub file_path: String,
pub name: String,
pub description: String,
pub instructions: String,
pub enabled: bool,
pub tags: Vec<String>,
pub category_id: Option<CategoryId>,
pub source_id: SourceId,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl Skill {
pub fn from_json_row(row: &JsonRow) -> Result<Self> {
let id = SkillId::new(
row.get("skill_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing skill_id"))?,
);
let file_path = row
.get("file_path")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing file_path"))?
.to_string();
let name = row
.get("name")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing name"))?
.to_string();
let description = row
.get("description")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing description"))?
.to_string();
let instructions = row
.get("instructions")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing instructions"))?
.to_string();
let enabled = row
.get("enabled")
.and_then(serde_json::Value::as_bool)
.ok_or_else(|| anyhow!("Missing enabled"))?;
let tags = row
.get("tags")
.and_then(|v| v.as_array())
.map_or_else(Vec::new, |arr| {
arr.iter()
.filter_map(|v| v.as_str().map(String::from))
.collect()
});
let category_id = row
.get("category_id")
.and_then(|v| v.as_str())
.map(CategoryId::new);
let source_id = SourceId::new(
row.get("source_id")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("Missing source_id"))?,
);
let created_at = row
.get("created_at")
.and_then(systemprompt_database::parse_database_datetime)
.ok_or_else(|| anyhow!("Missing or invalid created_at"))?;
let updated_at = row
.get("updated_at")
.and_then(systemprompt_database::parse_database_datetime)
.ok_or_else(|| anyhow!("Missing or invalid updated_at"))?;
Ok(Self {
id,
file_path,
name,
description,
instructions,
enabled,
tags,
category_id,
source_id,
created_at,
updated_at,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillMetadata {
pub id: SkillId,
pub name: String,
pub description: String,
pub enabled: bool,
pub file: String,
pub assigned_agents: Vec<String>,
pub tags: Vec<String>,
}