use crate::cli::{BOLD, CYAN, DIM, GREEN, RESET, SelectionArgs, YELLOW};
use crate::commands::fail_agents;
use agents_skills::error::Result;
use agents_skills::{Manager, SelectionOutcome, SelectionRequest};
#[derive(Debug, Clone, Copy)]
pub enum Action {
Remove,
Disable,
Enable,
}
impl Action {
fn apply(&self, manager: &Manager, req: &SelectionRequest) -> Result<SelectionOutcome> {
match self {
Action::Remove => manager.remove(req),
Action::Disable => manager.disable(req),
Action::Enable => manager.enable(req),
}
}
fn verb(&self) -> &'static Verb {
match self {
Action::Remove => &REMOVE,
Action::Disable => &DISABLE,
Action::Enable => &ENABLE,
}
}
}
struct Verb {
command: &'static str,
past: &'static str,
participle: &'static str,
available_header: &'static str,
none_available: &'static str,
note: &'static str,
}
const REMOVE: Verb = Verb {
command: "remove",
past: "Removed",
participle: "removed",
available_header: "Installed skills:",
none_available: "No skills found to remove.",
note: "Removed for every linked agent (they share the canonical skills dir).",
};
const DISABLE: Verb = Verb {
command: "disable",
past: "Disabled",
participle: "disabled",
available_header: "Enabled skills:",
none_available: "No enabled skills found to disable.",
note: "Hidden from every linked agent (they share the canonical skills dir).",
};
const ENABLE: Verb = Verb {
command: "enable",
past: "Enabled",
participle: "enabled",
available_header: "Disabled skills:",
none_available: "No disabled skills found to enable.",
note: "Visible to every linked agent (they share the canonical skills dir).",
};
pub fn run(manager: &Manager, args: SelectionArgs, action: Action) -> Result<()> {
let req = SelectionRequest {
skills: args
.skills
.iter()
.chain(args.skill.iter())
.cloned()
.collect(),
all: args.all,
};
let outcome = match action.apply(manager, &req) {
Ok(o) => o,
Err(e) => return fail_agents(e),
};
render(&req, &outcome, action.verb());
if !outcome.missing.is_empty() {
std::process::exit(1);
}
Ok(())
}
fn render(req: &SelectionRequest, outcome: &SelectionOutcome, verb: &Verb) {
if req.skills.is_empty() && !req.all {
if outcome.available.is_empty() {
println!("{YELLOW}{}{RESET}", verb.none_available);
} else {
println!("{BOLD}{}{RESET}", verb.available_header);
for name in &outcome.available {
println!(" {CYAN}{name}{RESET}");
}
println!();
println!(
"{DIM}Usage: agents-skills {} <name> [options]{RESET}",
verb.command
);
println!("{DIM}Options: -s/--skill, --all{RESET}");
}
return;
}
if outcome.requested.is_empty() {
println!("{YELLOW}{}{RESET}", verb.none_available);
return;
}
for name in &outcome.applied {
println!("{GREEN}✓{RESET} {} {name}", verb.past);
}
for name in &outcome.already {
println!("{DIM}• {name} already {}{RESET}", verb.participle);
}
for name in &outcome.missing {
println!("{YELLOW}! {name} not found{RESET}");
}
println!();
if !outcome.applied.is_empty() {
println!(
"{GREEN}✓ {} {} skill(s){RESET}",
verb.past,
outcome.applied.len()
);
println!("{DIM}{}{RESET}", verb.note);
}
println!();
}