use std::path::PathBuf;
use eyre::Result;
use crate::config::{Config, Settings};
use crate::dirs;
#[derive(Debug, usage_rs::Args)]
#[usage(
verbatim_doc_comment,
example(
r###"mise skills sync"###,
help = r###"into the project's .claude/skills, or wherever skills.dir says"###
),
example(
r###"mise skills sync --dir .agents/skills --prune"###,
help = r###"somewhere else, and drop links for skills that are no longer active"###
),
effect = "write"
)]
pub(super) struct SkillsSync {
#[usage(long, value_hint = usage_rs::ValueHint::DirPath)]
dir: Option<PathBuf>,
#[usage(long, short, conflicts = "dir")]
global: bool,
#[usage(long)]
prune: bool,
}
impl SkillsSync {
pub(super) async fn run(self) -> Result<()> {
let config = Config::get().await?;
let skills = crate::packslip::active_skills(&config).await?;
let settings = Settings::get();
let dir = match (&self.dir, self.global) {
(Some(dir), _) => dir.clone(),
(None, true) => crate::packslip::skills_dir(&dirs::HOME),
(None, false) => crate::packslip::skills_dir(
&config
.project_root
.clone()
.map(Ok)
.unwrap_or_else(std::env::current_dir)?,
),
};
let prune = self.prune || settings.skills.prune;
if skills.is_empty() && !prune {
miseprintln!("no skills declared by the active tools; nothing to link");
return Ok(());
}
let report = crate::packslip::sync_skills(&dir, &skills, &dirs::INSTALLS, prune)?;
for name in &report.linked {
let skill = skills.iter().find(|s| &s.name == name);
miseprintln!(
"linked {} -> {}",
dir.join(name).display(),
skill
.map(|s| s.path.display().to_string())
.unwrap_or_default()
);
}
for name in &report.pruned {
miseprintln!("removed {}", dir.join(name).display());
}
for (name, why) in &report.skipped {
warn!("skipped {name}: {why}");
}
if report.linked.is_empty() && report.pruned.is_empty() {
miseprintln!(
"{} skill(s) already linked in {}",
report.unchanged.len(),
dir.display()
);
}
Ok(())
}
}