use clap::{ArgGroup, Args, Parser, Subcommand};
use std::io::IsTerminal;
#[derive(Debug, Parser)]
#[command(
name = "agents-skills",
about = "A minimal skill installer and manager for AI agents",
long_about = None,
disable_version_flag = true
)]
pub struct Cli {
#[arg(short = 'v', long = "version")]
pub version: bool,
#[command(subcommand)]
pub command: Option<Command>,
}
#[derive(Debug, Subcommand)]
pub enum Command {
Add(AddArgs),
Remove(SelectionArgs),
List(ListArgs),
Disable(SelectionArgs),
Enable(SelectionArgs),
Agent(AgentArgs),
}
#[derive(Debug, Args)]
pub struct AddArgs {
#[arg(required = true)]
pub source: String,
#[arg(long = "ref", value_name = "ref")]
pub reference: Option<String>,
#[arg(short = 'f', long = "force")]
pub force: bool,
}
#[derive(Debug, Args)]
pub struct SelectionArgs {
pub skills: Vec<String>,
#[arg(short = 's', long = "skill", num_args = 1..)]
pub skill: Vec<String>,
#[arg(long = "all")]
pub all: bool,
}
#[derive(Debug, Args)]
pub struct ListArgs {
#[arg(long = "json")]
pub json: bool,
}
#[derive(Debug, Args)]
#[command(group(
ArgGroup::new("mode")
.required(true)
.args(["link", "unlink", "status"])
))]
pub struct AgentArgs {
pub agents: Vec<String>,
#[arg(long = "link")]
pub link: bool,
#[arg(long = "unlink")]
pub unlink: bool,
#[arg(long = "status")]
pub status: bool,
}
pub struct Style(&'static str);
impl std::fmt::Display for Style {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if colors_enabled() {
f.write_str(self.0)
} else {
Ok(())
}
}
}
fn colors_enabled() -> bool {
use std::sync::OnceLock;
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| {
std::io::stdout().is_terminal()
&& std::env::var_os("NO_COLOR").is_none()
&& console_supports_ansi()
})
}
#[cfg(windows)]
fn console_supports_ansi() -> bool {
use windows_sys::Win32::System::Console::{
ENABLE_VIRTUAL_TERMINAL_PROCESSING, GetConsoleMode, GetStdHandle, STD_OUTPUT_HANDLE,
SetConsoleMode,
};
unsafe {
let mut mode = 0u32;
let console = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleMode(console, &mut mode) != 0
&& SetConsoleMode(console, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0
}
}
#[cfg(not(windows))]
fn console_supports_ansi() -> bool {
true
}
pub static RESET: Style = Style("\x1b[0m");
pub static DIM: Style = Style("\x1b[38;5;102m");
pub static TEXT: Style = Style("\x1b[38;5;145m");
pub static BOLD: Style = Style("\x1b[1m");
pub static CYAN: Style = Style("\x1b[36m");
pub static GREEN: Style = Style("\x1b[32m");
pub static YELLOW: Style = Style("\x1b[33m");
pub static RED: Style = Style("\x1b[31m");
pub fn show_banner() {
println!();
println!("{DIM}Agents skills installer and manager{RESET}");
println!();
println!(
" {DIM}${RESET} {TEXT}agents-skills add {DIM}owner/repo/slug{RESET} {DIM}Add a skill{RESET}"
);
println!(
" {DIM}${RESET} {TEXT}agents-skills remove{RESET} {DIM}Remove installed skills{RESET}"
);
println!(
" {DIM}${RESET} {TEXT}agents-skills list{RESET} {DIM}List installed skills{RESET}"
);
println!();
println!(
" {DIM}${RESET} {TEXT}agents-skills agent --link{RESET} {DIM}Link agents to the skills dir{RESET}"
);
println!(
" {DIM}${RESET} {TEXT}agents-skills agent --status{RESET} {DIM}Show agent link status{RESET}"
);
println!(
" {DIM}${RESET} {TEXT}agents-skills agent --unlink{RESET} {DIM}Unlink agents{RESET}"
);
println!();
println!(
" {DIM}${RESET} {TEXT}agents-skills disable{RESET} {DIM}Disable installed skills{RESET}"
);
println!(
" {DIM}${RESET} {TEXT}agents-skills enable{RESET} {DIM}Re-enable disabled skills{RESET}"
);
println!();
println!("{DIM}try:{RESET} agents-skills add anthropics/skills/pdf");
println!();
}