garudust 0.2.5

Self-improving AI agent — TUI/CLI, tool hub, Ollama & OpenRouter support
use std::path::Path;

use anyhow::Result;
use garudust_tools::skill_hub;

pub async fn list(skills_dir: &Path) -> Result<()> {
    let skills = skill_hub::list_installed(skills_dir).await;

    if skills.is_empty() {
        println!(
            "No skills installed. Run `garudust skill install <source>` to install one.\n\
             Sources: owner/repo/path  |  https://…/SKILL.md  |  well-known:https://…"
        );
        return Ok(());
    }

    let name_w = skills
        .iter()
        .map(|s| s.name.len())
        .max()
        .unwrap_or(4)
        .max(4);
    let ver_w = 9usize;

    println!("{:<name_w$}  {:<ver_w$}  DESCRIPTION", "NAME", "VERSION");
    println!("{}", "-".repeat(name_w + ver_w + 16));

    for s in &skills {
        let desc = if s.description.chars().count() > 56 {
            format!("{}", s.description.chars().take(55).collect::<String>())
        } else {
            s.description.clone()
        };
        println!("{:<name_w$}  {:<ver_w$}  {desc}", s.name, s.version);
    }

    Ok(())
}

pub async fn install(source: &str, name: &str, hub: &str, skills_dir: &Path) -> Result<()> {
    let is_short_name = !source.contains('/')
        && !source.starts_with("https://")
        && !source.starts_with("http://")
        && !source.starts_with("well-known:");

    if is_short_name {
        println!("Installing skill '{source}' from hub {hub}...");
        garudust_tools::hub::install_skill_from_hub(hub, source, skills_dir).await?;
        println!("Installed skill '{source}'.");
    } else {
        println!("Installing skill from '{source}'...");
        let installed_name = skill_hub::install_skill(source, name, skills_dir).await?;
        println!("Installed skill '{installed_name}'.");
    }
    Ok(())
}

pub async fn uninstall(skill_name: &str, skills_dir: &Path) -> Result<()> {
    skill_hub::uninstall_skill(skill_name, skills_dir).await?;
    println!("Uninstalled skill '{skill_name}'.");
    Ok(())
}