use anyhow::Result;
use std::path::PathBuf;
fn which(bin: &str) -> bool {
std::process::Command::new("which")
.arg(bin)
.output()
.is_ok_and(|o| o.status.success())
}
pub fn check_prereqs() {
let prereqs = [
("tmux", "Install tmux: https://github.com/tmux/tmux/wiki/Installing"),
("claude", "Install Claude Code CLI: https://docs.anthropic.com/en/docs/claude-code"),
];
for (bin, install_hint) in &prereqs {
if which(bin) {
eprintln!("[install] {} ... ok", bin);
} else {
eprintln!("[install] {} ... MISSING", bin);
eprintln!("[install] hint: {}", install_hint);
}
}
}
fn detect_editors() -> Vec<&'static str> {
let mut editors = Vec::new();
let jetbrains_found = {
let home = std::env::var("HOME").unwrap_or_default();
let linux_path = PathBuf::from(&home).join(".local/share/JetBrains");
let macos_path = PathBuf::from("/Applications");
let linux_ok = linux_path.is_dir()
&& std::fs::read_dir(&linux_path)
.map(|mut d| d.next().is_some())
.unwrap_or(false);
let macos_ok = macos_path.is_dir()
&& std::fs::read_dir(&macos_path)
.map(|d| {
d.flatten()
.any(|e| e.file_name().to_string_lossy().starts_with("IntelliJ"))
})
.unwrap_or(false);
linux_ok || macos_ok
};
if jetbrains_found {
editors.push("jetbrains");
}
if which("cursor") || which("codium") || which("code") {
editors.push("vscode");
}
editors
}
pub fn run(editor: Option<&str>, skip_prereqs: bool, skip_plugins: bool) -> Result<()> {
if !skip_prereqs {
check_prereqs();
}
if skip_plugins {
eprintln!("[install] Skipping plugin installation (--skip-plugins).");
return Ok(());
}
let editors_to_install: Vec<&str> = if let Some(e) = editor {
vec![e]
} else {
detect_editors()
};
if editors_to_install.is_empty() {
eprintln!("[install] No supported editors detected. Skipping plugin installation.");
eprintln!("[install] To install manually: agent-doc install --editor jetbrains|vscode");
return Ok(());
}
let mut installed = Vec::new();
let mut failed = Vec::new();
for ed in &editors_to_install {
eprintln!("[install] Installing plugin for {} ...", ed);
match crate::plugin::install(ed) {
Ok(()) => installed.push(*ed),
Err(e) => {
eprintln!("[install] Plugin install failed for {}: {:#}", ed, e);
failed.push(*ed);
}
}
}
eprintln!("[install] ---");
if !installed.is_empty() {
eprintln!("[install] Installed plugins: {}", installed.join(", "));
}
if !failed.is_empty() {
eprintln!("[install] Failed plugins: {}", failed.join(", "));
}
if installed.is_empty() && failed.is_empty() {
eprintln!("[install] Nothing to install.");
}
Ok(())
}