use crate::error::*;
use crate::{package_definition, paths, receipt};
use std::path::Path;
pub fn run(home: &Path, platforms: &[String]) -> Result<()> {
let cwd = std::env::current_dir().context("failed to get current directory")?;
let resolved = if platforms.is_empty() {
let detected = detect_platforms(&cwd);
if detected.is_empty() {
bail!(
"No platforms detected. Specify platforms explicitly:\n \
zacor init claude-code gemini\n\n\
Valid platforms: {}",
zacor_package::skills::PLATFORMS
.iter()
.map(|p| p.name)
.collect::<Vec<_>>()
.join(", ")
);
}
eprintln!(
"Auto-detected platforms: {}",
detected.join(", ")
);
detected
} else {
zacor_package::skills::validate_platforms(platforms)
.map_err(|e| anyhow::anyhow!("{e}"))?;
platforms.to_vec()
};
let zr_dir = cwd.join(".zr");
if !zr_dir.exists() {
std::fs::create_dir_all(&zr_dir).context("failed to create .zr/")?;
eprintln!("Created .zr/");
}
let packages = receipt::list_all(home).context("failed to list packages")?;
let platforms_csv = resolved.join(",");
let mut dispatched = 0;
let mut failed = 0;
for (name, r) in &packages {
if !r.active {
continue;
}
let def_path = paths::definition_path(home, name, &r.current);
let def = match package_definition::parse_file(&def_path) {
Ok(d) => d,
Err(_) => continue,
};
if !def.commands.contains_key("init") {
continue;
}
eprint!(" {name}... ");
let status = std::process::Command::new("zr")
.args([name.as_str(), "init", &platforms_csv])
.status();
match status {
Ok(s) if s.success() => {
eprintln!("ok");
dispatched += 1;
}
Ok(s) => {
eprintln!("failed (exit {})", s.code().unwrap_or(-1));
failed += 1;
}
Err(e) => {
eprintln!("failed ({e})");
failed += 1;
}
}
}
eprintln!(
"\nPlatforms: [{}]\nPackages dispatched: {dispatched}",
resolved.join(", "),
);
if failed > 0 {
bail!("{failed} package(s) failed during init");
}
Ok(())
}
fn detect_platforms(root: &Path) -> Vec<String> {
zacor_package::skills::PLATFORMS
.iter()
.filter(|p| root.join(p.dir).is_dir())
.map(|p| p.name.to_string())
.collect()
}