use owo_colors::OwoColorize;
use crate::{
catalog::Catalog,
commands::{ColorChoice, init},
config::Config,
diagnostics::Diagnostics,
error::Result,
frontmatter::parse_frontmatter,
skill::render_template,
tool::Tool,
};
pub async fn run(
color: ColorChoice,
verbose: bool,
skill_name: Option<String>,
) -> Result<()> {
init::ensure().await?;
let mut diagnostics = Diagnostics::new(verbose);
let config = Config::load()?;
let catalog = Catalog::load(&config, &mut diagnostics);
let use_color = color.enabled();
let skills_to_validate: Vec<_> = if let Some(name) = skill_name {
if let Some(skill) = catalog.sources.get(&name) {
vec![(name, skill)]
} else {
println!("Skill '{}' not found in sources.", name);
return Ok(());
}
} else {
catalog.sources.iter().map(|(n, s)| (n.clone(), s)).collect()
};
if skills_to_validate.is_empty() {
println!("No skills to validate.");
return Ok(());
}
let mut valid_count = 0;
let mut invalid_count = 0;
for (name, skill) in skills_to_validate {
let mut errors = Vec::new();
match parse_frontmatter(&skill.contents) {
Ok(fm) => {
if fm.name != name {
errors.push(format!(
"frontmatter name '{}' does not match directory name '{}'",
fm.name, name
));
}
if fm.description.is_empty() {
errors.push("description is empty".to_string());
}
}
Err(e) => {
errors.push(format!("frontmatter: {}", e.message));
}
}
for tool in Tool::all() {
if let Err(e) = render_template(&skill.contents, tool) {
errors.push(format!("template ({} render): {}", tool.id(), e));
}
}
if errors.is_empty() {
valid_count += 1;
if use_color {
println!("{} {}", "✓".green(), name);
} else {
println!("✓ {}", name);
}
} else {
invalid_count += 1;
if use_color {
println!("{} {}", "✗".red(), name);
} else {
println!("✗ {}", name);
}
for error in errors {
println!(" - {}", error);
}
}
}
println!();
println!(
"{} valid, {} invalid",
valid_count,
invalid_count
);
Ok(())
}