offline-cli 0.3.3

The Offline Protocol developer CLI
//! `offline skills`: show integration skill documents.

use anyhow::{bail, Result};
use clap::{Args, Subcommand};
use offline_registry::Registry;
use serde_json::json;

use crate::output;

#[derive(Subcommand)]
pub enum SkillsCommand {
    /// List all skills
    List(ListArgs),
    /// Print a skill document
    Show(ShowArgs),
}

#[derive(Args)]
pub struct ListArgs {
    /// Output JSON
    #[arg(long)]
    pub json: bool,
}

#[derive(Args)]
pub struct ShowArgs {
    /// Skill name (e.g. mesh-networking)
    pub name: String,
    /// Output JSON (frontmatter + body)
    #[arg(long)]
    pub json: bool,
}

pub fn run(cmd: SkillsCommand) -> Result<()> {
    let registry = Registry::load_default()?;
    match cmd {
        SkillsCommand::List(args) => {
            if args.json {
                let skills: Vec<_> = registry
                    .skills()
                    .map(|s| {
                        json!({
                            "name": s.name,
                            "title": s.title,
                            "description": s.description,
                            "useWhen": s.use_when,
                            "packages": s.packages,
                        })
                    })
                    .collect();
                return output::json(&skills);
            }
            output::heading("Skills");
            for s in registry.skills() {
                output::item(&s.name, &s.description);
                output::note(&format!("use when: {}", s.use_when.join("; ")));
            }
            Ok(())
        }
        SkillsCommand::Show(args) => {
            let Some(skill) = registry.skill(&args.name) else {
                let hits = registry.search_skills(&args.name);
                let hint = hits
                    .first()
                    .map(|h| format!(". Did you mean `{}`?", h.name))
                    .unwrap_or_default();
                bail!("skill `{}` not found{hint}", args.name);
            };
            if args.json {
                return output::json(skill);
            }
            println!("{}", skill.body);
            Ok(())
        }
    }
}