Skip to main content

kindly_tools/
lib.rs

1// Modules defined inline below
2pub mod commands;
3pub mod config;
4pub mod output;
5pub mod platform;
6
7use anyhow::Result;
8use dialoguer::Select;
9use std::path::{Path, PathBuf};
10
11use crate::platform::Platform;
12
13/// Common trait for all subcommands
14#[allow(async_fn_in_trait)]
15pub trait Execute {
16    async fn execute(&self) -> Result<()>;
17}
18
19/// Check if a command exists in PATH
20pub fn command_exists(cmd: &str) -> bool {
21    which::which(cmd).is_ok()
22}
23
24/// Get the user's home directory
25pub fn home_dir() -> Result<PathBuf> {
26    directories::BaseDirs::new()
27        .map(|dirs| dirs.home_dir().to_path_buf())
28        .ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))
29}
30
31/// Get the KindlyGuard configuration directory
32pub fn config_dir() -> Result<PathBuf> {
33    let home = home_dir()?;
34    Ok(home.join(".kindlyguard"))
35}
36
37/// Ensure a directory exists, creating it if necessary
38pub fn ensure_dir(path: &Path) -> Result<()> {
39    if !path.exists() {
40        std::fs::create_dir_all(path)?;
41    }
42    Ok(())
43}
44
45/// Get the MCP configuration file path
46pub fn get_mcp_config_path() -> Result<PathBuf> {
47    let home = home_dir()?;
48
49    // Check for different possible locations
50    let candidates = vec![home.join(".mcp.json"), home.join(".config/claude/mcp.json")];
51
52    for path in &candidates {
53        if path.exists() {
54            return Ok(path.clone());
55        }
56    }
57
58    // Default to .mcp.json
59    Ok(home.join(".mcp.json"))
60}
61
62/// Download a file with progress bar
63pub async fn download_file(url: &str, dest: &Path) -> Result<()> {
64    use futures_util::StreamExt;
65    use indicatif::{ProgressBar, ProgressStyle};
66    use std::io::Write;
67    
68    // Create HTTP client
69    let client = reqwest::Client::new();
70    
71    // Start the download
72    let response = client
73        .get(url)
74        .send()
75        .await?;
76    
77    // Check status
78    if !response.status().is_success() {
79        anyhow::bail!("Failed to download: HTTP {}", response.status());
80    }
81    
82    // Get content length for progress bar
83    let total_size = response
84        .content_length()
85        .unwrap_or(0);
86    
87    // Create progress bar
88    let pb = ProgressBar::new(total_size);
89    pb.set_style(
90        ProgressStyle::default_bar()
91            .template("{msg}\n{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {bytes}/{total_bytes} ({bytes_per_sec}, {eta})")?
92            .progress_chars("#>-")
93    );
94    pb.set_message(format!("Downloading {}", dest.file_name().unwrap_or_default().to_string_lossy()));
95    
96    // Create the destination file
97    let mut file = std::fs::File::create(dest)?;
98    
99    // Download with progress
100    let mut downloaded = 0u64;
101    let mut stream = response.bytes_stream();
102    
103    while let Some(chunk) = stream.next().await {
104        let chunk = chunk?;
105        file.write_all(&chunk)?;
106        downloaded += chunk.len() as u64;
107        pb.set_position(downloaded);
108    }
109    
110    pb.finish_with_message("Download complete");
111    
112    Ok(())
113}
114
115/// Get the GitHub release download URL for KindlyGuard based on platform
116pub fn get_kindlyguard_download_url(version: &str, platform: &Platform) -> String {
117    let version_tag = if version == "latest" {
118        "latest".to_string()
119    } else {
120        format!("v{}", version.trim_start_matches('v'))
121    };
122    
123    let (os, arch, ext) = match platform {
124        Platform::Windows => ("pc-windows-msvc", "x86_64", "exe"),
125        Platform::MacOS => {
126            if std::env::consts::ARCH == "aarch64" {
127                ("apple-darwin", "aarch64", "")
128            } else {
129                ("apple-darwin", "x86_64", "")
130            }
131        },
132        Platform::Linux => ("unknown-linux-gnu", "x86_64", ""),
133        Platform::Unknown => {
134            return format!("https://github.com/kindly-software-inc/kindly-guard/releases/{}", version_tag);
135        }
136    };
137    
138    let filename = if ext.is_empty() {
139        format!("kindlyguard-{}-{}", arch, os)
140    } else {
141        format!("kindlyguard-{}-{}.{}", arch, os, ext)
142    };
143    
144    if version_tag == "latest" {
145        format!(
146            "https://github.com/kindly-software-inc/kindly-guard/releases/latest/download/{}",
147            filename
148        )
149    } else {
150        format!(
151            "https://github.com/kindly-software-inc/kindly-guard/releases/download/{}/{}",
152            version_tag, filename
153        )
154    }
155}
156
157/// Install KindlyGuard binary from GitHub releases
158pub async fn install_kindlyguard_from_github(version: &str, platform: &Platform) -> Result<()> {
159    use colored::Colorize;
160    
161    println!("šŸ“¦ {}", "Downloading KindlyGuard from GitHub releases...".green());
162    
163    // Get download URL
164    let url = get_kindlyguard_download_url(version, platform);
165    println!("šŸ“ URL: {}", url.cyan());
166    
167    // Determine installation directory
168    let install_dir = match platform {
169        Platform::Windows => {
170            // Install to user's local bin directory
171            let home = home_dir()?;
172            home.join(".kindlyguard").join("bin")
173        },
174        _ => {
175            // Try ~/.local/bin first, then ~/.cargo/bin
176            let home = home_dir()?;
177            let local_bin = home.join(".local").join("bin");
178            if local_bin.exists() {
179                local_bin
180            } else {
181                home.join(".cargo").join("bin")
182            }
183        }
184    };
185    
186    // Ensure directory exists
187    ensure_dir(&install_dir)?;
188    
189    // Determine binary name
190    let binary_name = if platform == &Platform::Windows {
191        "kindlyguard.exe"
192    } else {
193        "kindlyguard"
194    };
195    
196    let dest_path = install_dir.join(binary_name);
197    let temp_path = dest_path.with_extension("tmp");
198    
199    // Download to temporary file
200    download_file(&url, &temp_path).await?;
201    
202    // Make executable on Unix
203    #[cfg(unix)]
204    {
205        use std::os::unix::fs::PermissionsExt;
206        let mut perms = std::fs::metadata(&temp_path)?.permissions();
207        perms.set_mode(0o755);
208        std::fs::set_permissions(&temp_path, perms)?;
209    }
210    
211    // Move to final location
212    std::fs::rename(&temp_path, &dest_path)?;
213    
214    println!("\nāœ… {}", "Installation successful!".green().bold());
215    println!("šŸ“ {}: {}", "Binary installed to".cyan(), dest_path.display());
216    
217    // Check if directory is in PATH
218    let path_var = std::env::var("PATH").unwrap_or_default();
219    if !path_var.contains(&install_dir.to_string_lossy().to_string()) {
220        println!(
221            "\nāš ļø  {}",
222            "Installation directory is not in your PATH!".yellow()
223        );
224        println!("šŸ“‹ {}", "Add this to your shell configuration:".cyan());
225        
226        match platform {
227            Platform::Windows => {
228                println!(
229                    "   {} $env:Path += \";{}\"",
230                    "$".dimmed(),
231                    install_dir.display()
232                );
233            },
234            _ => {
235                println!(
236                    "   {} export PATH=\"$PATH:{}\"",
237                    "$".dimmed(),
238                    install_dir.display()
239                );
240            }
241        }
242    }
243    
244    Ok(())
245}
246
247/// Detect various environment characteristics
248pub fn detect_environment() -> EnvironmentInfo {
249    use std::env;
250    use std::path::Path;
251
252    EnvironmentInfo {
253        is_docker: Path::new("/.dockerenv").exists(),
254        is_wsl: detect_wsl(),
255        is_ci: env::var("CI").is_ok() || env::var("CONTINUOUS_INTEGRATION").is_ok(),
256        is_ssh: env::var("SSH_CONNECTION").is_ok() || env::var("SSH_CLIENT").is_ok(),
257        has_proxy: env::var("HTTP_PROXY").is_ok()
258            || env::var("HTTPS_PROXY").is_ok()
259            || env::var("http_proxy").is_ok()
260            || env::var("https_proxy").is_ok(),
261    }
262}
263
264/// Check if running in WSL
265fn detect_wsl() -> bool {
266    #[cfg(target_os = "linux")]
267    {
268        if let Ok(content) = std::fs::read_to_string("/proc/version") {
269            return content.to_lowercase().contains("microsoft");
270        }
271    }
272    false
273}
274
275/// Detect Linux distribution
276pub fn detect_linux_distro() -> LinuxDistro {
277    #[cfg(target_os = "linux")]
278    {
279        // Try /etc/os-release first (standard on most modern distros)
280        if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
281            for line in content.lines() {
282                if line.starts_with("ID=") {
283                    let id = line.trim_start_matches("ID=").trim_matches('"');
284                    return match id {
285                        "ubuntu" => LinuxDistro::Ubuntu,
286                        "debian" => LinuxDistro::Debian,
287                        "fedora" => LinuxDistro::Fedora,
288                        "centos" => LinuxDistro::CentOS,
289                        "rhel" => LinuxDistro::RHEL,
290                        "arch" => LinuxDistro::Arch,
291                        "manjaro" => LinuxDistro::Manjaro,
292                        "opensuse" | "opensuse-leap" | "opensuse-tumbleweed" | "suse" => {
293                            LinuxDistro::OpenSUSE
294                        },
295                        "alpine" => LinuxDistro::Alpine,
296                        "nixos" => LinuxDistro::NixOS,
297                        "gentoo" => LinuxDistro::Gentoo,
298                        "void" => LinuxDistro::Void,
299                        "elementary" => LinuxDistro::Elementary,
300                        "pop" => LinuxDistro::PopOS,
301                        "mint" | "linuxmint" => LinuxDistro::Mint,
302                        _ => LinuxDistro::Unknown(id.to_string()),
303                    };
304                }
305            }
306        }
307
308        // Fallback checks for older systems
309        if Path::new("/etc/debian_version").exists() {
310            return LinuxDistro::Debian;
311        }
312        if Path::new("/etc/redhat-release").exists() {
313            return LinuxDistro::RHEL;
314        }
315        if Path::new("/etc/arch-release").exists() {
316            return LinuxDistro::Arch;
317        }
318        if Path::new("/etc/gentoo-release").exists() {
319            return LinuxDistro::Gentoo;
320        }
321        if Path::new("/etc/alpine-release").exists() {
322            return LinuxDistro::Alpine;
323        }
324    }
325
326    LinuxDistro::Unknown("generic".to_string())
327}
328
329/// Detect installed Node.js version managers
330pub fn detect_node_managers() -> NodeManagers {
331    use std::path::Path;
332
333    let home = home_dir().unwrap_or_default();
334
335    NodeManagers {
336        has_nvm: Path::new(&home.join(".nvm")).exists() || std::env::var("NVM_DIR").is_ok(),
337        has_fnm: command_exists("fnm")
338            || Path::new(&home.join(".fnm")).exists()
339            || Path::new(&home.join(".local/share/fnm")).exists(),
340        has_n: command_exists("n")
341            || Path::new("/usr/local/n").exists()
342            || Path::new("/usr/local/bin/n").exists(),
343        has_volta: command_exists("volta") || Path::new(&home.join(".volta")).exists(),
344        has_asdf: command_exists("asdf") || Path::new(&home.join(".asdf")).exists(),
345    }
346}
347
348/// Environment information struct
349#[derive(Debug, Clone)]
350pub struct EnvironmentInfo {
351    pub is_docker: bool,
352    pub is_wsl: bool,
353    pub is_ci: bool,
354    pub is_ssh: bool,
355    pub has_proxy: bool,
356}
357
358/// Linux distribution enum
359#[derive(Debug, Clone, PartialEq)]
360pub enum LinuxDistro {
361    Ubuntu,
362    Debian,
363    Fedora,
364    CentOS,
365    RHEL,
366    Arch,
367    Manjaro,
368    OpenSUSE,
369    Alpine,
370    NixOS,
371    Gentoo,
372    Void,
373    Elementary,
374    PopOS,
375    Mint,
376    Unknown(String),
377}
378
379impl LinuxDistro {
380    /// Get display name with emoji
381    pub fn display_name(&self) -> String {
382        match self {
383            LinuxDistro::Ubuntu => "🟠 Ubuntu".to_string(),
384            LinuxDistro::Debian => "šŸ”“ Debian".to_string(),
385            LinuxDistro::Fedora => "šŸ”µ Fedora".to_string(),
386            LinuxDistro::CentOS => "🟣 CentOS".to_string(),
387            LinuxDistro::RHEL => "šŸ”“ Red Hat Enterprise Linux".to_string(),
388            LinuxDistro::Arch => "šŸ”· Arch Linux".to_string(),
389            LinuxDistro::Manjaro => "🟢 Manjaro".to_string(),
390            LinuxDistro::OpenSUSE => "šŸ¦Ž openSUSE".to_string(),
391            LinuxDistro::Alpine => "šŸ”ļø Alpine Linux".to_string(),
392            LinuxDistro::NixOS => "ā„ļø NixOS".to_string(),
393            LinuxDistro::Gentoo => "🟣 Gentoo".to_string(),
394            LinuxDistro::Void => "šŸŒ‘ Void Linux".to_string(),
395            LinuxDistro::Elementary => "🦚 elementary OS".to_string(),
396            LinuxDistro::PopOS => "šŸš€ Pop!_OS".to_string(),
397            LinuxDistro::Mint => "🌿 Linux Mint".to_string(),
398            LinuxDistro::Unknown(name) => format!("🐧 Linux ({})", name),
399        }
400    }
401
402    /// Get package manager command
403    pub fn package_manager(&self) -> &'static str {
404        match self {
405            LinuxDistro::Ubuntu
406            | LinuxDistro::Debian
407            | LinuxDistro::Elementary
408            | LinuxDistro::PopOS
409            | LinuxDistro::Mint => "apt",
410            LinuxDistro::Fedora | LinuxDistro::CentOS | LinuxDistro::RHEL => "dnf",
411            LinuxDistro::Arch | LinuxDistro::Manjaro => "pacman",
412            LinuxDistro::OpenSUSE => "zypper",
413            LinuxDistro::Alpine => "apk",
414            LinuxDistro::NixOS => "nix-env",
415            LinuxDistro::Gentoo => "emerge",
416            LinuxDistro::Void => "xbps-install",
417            LinuxDistro::Unknown(_) => "apt", // fallback
418        }
419    }
420}
421
422/// Node.js version managers struct
423#[derive(Debug, Clone)]
424pub struct NodeManagers {
425    pub has_nvm: bool,
426    pub has_fnm: bool,
427    pub has_n: bool,
428    pub has_volta: bool,
429    pub has_asdf: bool,
430}
431
432impl NodeManagers {
433    /// Get the recommended manager if any is installed
434    pub fn recommended(&self) -> Option<&'static str> {
435        if self.has_volta {
436            Some("volta")
437        } else if self.has_fnm {
438            Some("fnm")
439        } else if self.has_nvm {
440            Some("nvm")
441        } else if self.has_n {
442            Some("n")
443        } else if self.has_asdf {
444            Some("asdf")
445        } else {
446            None
447        }
448    }
449
450    /// Check if any manager is installed
451    pub fn has_any(&self) -> bool {
452        self.has_nvm || self.has_fnm || self.has_n || self.has_volta || self.has_asdf
453    }
454}
455
456/// Recovery options for installation failures
457#[derive(Debug, Clone, Copy, PartialEq)]
458pub enum RecoveryMethod {
459    TryWithSudo,
460    InstallToHome,
461    UseDifferentPackageManager,
462    DownloadBinary,
463    OfflineInstallation,
464    ShowDiagnostics,
465    Cancel,
466}
467
468/// Show interactive recovery menu when installation fails
469pub fn show_recovery_menu(failed_method: &str) -> Result<RecoveryMethod> {
470    use colored::*;
471
472    println!(
473        "\n🚨 {}",
474        format!("Installation failed using {}", failed_method)
475            .red()
476            .bold()
477    );
478    println!("šŸ”„ {}", "Let's try a different approach...".yellow());
479
480    let options = vec![
481        format!("šŸ” Try with sudo privileges"),
482        format!("šŸ  Install to home directory (~/.local)"),
483        format!("šŸ“¦ Use different package manager"),
484        format!("šŸ’æ Download binary directly"),
485        format!("šŸ““ Offline installation"),
486        format!("šŸ” Show diagnostics"),
487        format!("āŒ Cancel installation"),
488    ];
489
490    let selection = Select::new()
491        .with_prompt("Select recovery method")
492        .items(&options)
493        .default(0)
494        .interact()?;
495
496    let method = match selection {
497        0 => RecoveryMethod::TryWithSudo,
498        1 => RecoveryMethod::InstallToHome,
499        2 => RecoveryMethod::UseDifferentPackageManager,
500        3 => RecoveryMethod::DownloadBinary,
501        4 => RecoveryMethod::OfflineInstallation,
502        5 => RecoveryMethod::ShowDiagnostics,
503        _ => RecoveryMethod::Cancel,
504    };
505
506    Ok(method)
507}
508
509/// Execute recovery method based on user selection
510pub async fn execute_recovery(
511    method: RecoveryMethod,
512    original_method: &str,
513    package: &str,
514    platform: &crate::platform::Platform,
515) -> Result<()> {
516    use colored::*;
517
518    match method {
519        RecoveryMethod::TryWithSudo => {
520            println!("\nšŸ” {}", "Retrying with sudo privileges...".cyan());
521            match original_method {
522                "npm" => {
523                    println!("šŸ“‹ {}", "Command:".cyan());
524                    println!(
525                        "   {} sudo npm install -g {}",
526                        "$".dimmed(),
527                        package.bright_white()
528                    );
529                    println!(
530                        "\nāš ļø  {}",
531                        "This will install globally with root privileges".yellow()
532                    );
533                },
534                "cargo" => {
535                    println!("šŸ“‹ {}", "Command:".cyan());
536                    println!(
537                        "   {} sudo cargo install {}",
538                        "$".dimmed(),
539                        package.bright_white()
540                    );
541                    println!(
542                        "\nāš ļø  {}",
543                        "Note: Using sudo with cargo is not recommended".yellow()
544                    );
545                    println!(
546                        "šŸ’” {}",
547                        "Consider using --root ~/.local/cargo instead".yellow()
548                    );
549                },
550                _ => return Err(anyhow::anyhow!("Sudo not applicable for this method")),
551            }
552        },
553        RecoveryMethod::InstallToHome => {
554            println!("\nšŸ  {}", "Installing to home directory...".cyan());
555            match original_method {
556                "npm" => {
557                    println!("šŸ“‹ {}", "Commands:".cyan());
558                    println!("   {} mkdir -p ~/.local/npm", "$".dimmed());
559                    println!("   {} npm config set prefix ~/.local/npm", "$".dimmed());
560                    println!(
561                        "   {} npm install -g {}",
562                        "$".dimmed(),
563                        package.bright_white()
564                    );
565                    println!("\nšŸ’” {}", "Add to PATH:".yellow());
566                    println!("   {} export PATH=$HOME/.local/npm/bin:$PATH", "$".dimmed());
567                },
568                "cargo" => {
569                    println!("šŸ“‹ {}", "Command:".cyan());
570                    println!(
571                        "   {} cargo install --root ~/.local/cargo {}",
572                        "$".dimmed(),
573                        package.bright_white()
574                    );
575                    println!("\nšŸ’” {}", "Add to PATH:".yellow());
576                    println!(
577                        "   {} export PATH=$HOME/.local/cargo/bin:$PATH",
578                        "$".dimmed()
579                    );
580                },
581                _ => {
582                    println!("šŸ“‹ {}", "Manual installation to ~/.local/bin:".cyan());
583                    println!("   1ļøāƒ£  Download the binary");
584                    println!("   2ļøāƒ£  {} mkdir -p ~/.local/bin", "$".dimmed());
585                    println!("   3ļøāƒ£  {} mv kindlyguard ~/.local/bin/", "$".dimmed());
586                    println!("   4ļøāƒ£  {} chmod +x ~/.local/bin/kindlyguard", "$".dimmed());
587                },
588            }
589        },
590        RecoveryMethod::UseDifferentPackageManager => {
591            println!("\nšŸ“¦ {}", "Alternative package managers:".cyan());
592            match platform {
593                crate::platform::Platform::MacOS => {
594                    println!("šŸŗ {}", "Homebrew:".green());
595                    println!("   {} brew tap kindly-software-inc/tap", "$".dimmed());
596                    println!("   {} brew install kindlyguard", "$".dimmed());
597                    println!("\n🌊 {}", "MacPorts:".green());
598                    println!("   {} sudo port install kindlyguard", "$".dimmed());
599                },
600                crate::platform::Platform::Linux => {
601                    println!("šŸ“¦ {}", "Snap:".green());
602                    println!("   {} sudo snap install kindlyguard", "$".dimmed());
603                    println!("\nšŸ“¦ {}", "Flatpak:".green());
604                    println!(
605                        "   {} flatpak install flathub com.kindly.guard",
606                        "$".dimmed()
607                    );
608                    println!("\nšŸ“¦ {}", "AppImage:".green());
609                    println!("   Download from releases page");
610                },
611                crate::platform::Platform::Windows => {
612                    println!("šŸ« {}", "Chocolatey:".green());
613                    println!("   {} choco install kindlyguard", "$".dimmed());
614                    println!("\nšŸ”· {}", "Scoop:".green());
615                    println!("   {} scoop install kindlyguard", "$".dimmed());
616                    println!("\nšŸ”¶ {}", "WinGet:".green());
617                    println!(
618                        "   {} winget install KindlySoftware.KindlyGuard",
619                        "$".dimmed()
620                    );
621                },
622                _ => {},
623            }
624        },
625        RecoveryMethod::DownloadBinary => {
626            println!("\nšŸ’æ {}", "Direct binary download:".cyan());
627            println!("🌐 {}", "Visit:".cyan());
628            println!(
629                "   {}",
630                "https://github.com/kindly-software-inc/kindly-guard/releases"
631                    .blue()
632                    .underline()
633            );
634
635            let arch = crate::platform::Architecture::detect();
636            match platform {
637                crate::platform::Platform::MacOS => {
638                    let arch_str = if arch == crate::platform::Architecture::Arm64 {
639                        "aarch64"
640                    } else {
641                        "x86_64"
642                    };
643                    println!(
644                        "\nšŸŽ {}",
645                        format!(
646                            "Download: kindly-guard-server-{}-apple-darwin.tar.gz",
647                            arch_str
648                        )
649                        .bright_white()
650                    );
651                },
652                crate::platform::Platform::Linux => {
653                    println!(
654                        "\n🐧 {}",
655                        "Download: kindly-guard-server-x86_64-unknown-linux-gnu.tar.gz"
656                            .bright_white()
657                    );
658                },
659                crate::platform::Platform::Windows => {
660                    println!(
661                        "\n🪟 {}",
662                        "Download: kindly-guard-server-x86_64-pc-windows-msvc.zip".bright_white()
663                    );
664                },
665                _ => {},
666            }
667
668            println!("\nšŸ“‹ {}", "Manual installation steps:".cyan());
669            println!("   1ļøāƒ£  Download the appropriate file");
670            println!("   2ļøāƒ£  Extract the archive");
671            println!("   3ļøāƒ£  Move binary to PATH location");
672            println!("   4ļøāƒ£  Make it executable (Unix/Linux/macOS)");
673        },
674        RecoveryMethod::OfflineInstallation => {
675            println!("\nšŸ““ {}", "Offline installation:".cyan());
676            println!("šŸ’” {}", "For offline environments:".yellow());
677            println!("\nšŸ“‹ {}", "Steps:".cyan());
678            println!("   1ļøāƒ£  Download on a connected machine:");
679            println!("      - Binary from GitHub releases");
680            println!(
681                "      - Or npm package: {} npm pack kindly-guard-server",
682                "$".dimmed()
683            );
684            println!("   2ļøāƒ£  Transfer to target machine via USB/network");
685            println!("   3ļøāƒ£  Install locally:");
686            println!("      - Binary: Copy to /usr/local/bin/");
687            println!(
688                "      - npm: {} npm install -g kindly-guard-server-*.tgz",
689                "$".dimmed()
690            );
691        },
692        RecoveryMethod::ShowDiagnostics => {
693            println!("\nšŸ” {}", "Running diagnostics...".cyan());
694
695            // Check disk space
696            println!("\nšŸ’¾ {}", "Disk space:".yellow());
697            #[cfg(not(target_os = "windows"))]
698            {
699                if let Ok(output) = std::process::Command::new("df").args(["-h", "."]).output() {
700                    println!("{}", String::from_utf8_lossy(&output.stdout));
701                }
702            }
703
704            // Check permissions
705            println!("\nšŸ”’ {}", "Permissions:".yellow());
706            match original_method {
707                "npm" => {
708                    if let Ok(output) = std::process::Command::new("npm")
709                        .args(["config", "get", "prefix"])
710                        .output()
711                    {
712                        let prefix = String::from_utf8_lossy(&output.stdout).trim().to_string();
713                        println!("   npm prefix: {}", prefix);
714
715                        #[cfg(not(target_os = "windows"))]
716                        {
717                            if let Ok(output) = std::process::Command::new("ls")
718                                .args(["-ld", &prefix])
719                                .output()
720                            {
721                                println!("   {}", String::from_utf8_lossy(&output.stdout).trim());
722                            }
723                        }
724                    }
725                },
726                "cargo" => {
727                    if let Ok(home) = home_dir() {
728                        let cargo_home = home.join(".cargo");
729                        println!("   CARGO_HOME: {:?}", cargo_home);
730
731                        #[cfg(not(target_os = "windows"))]
732                        {
733                            if let Ok(output) = std::process::Command::new("ls")
734                                .args(["-ld", cargo_home.to_str().unwrap_or("")])
735                                .output()
736                            {
737                                println!("   {}", String::from_utf8_lossy(&output.stdout).trim());
738                            }
739                        }
740                    }
741                },
742                _ => {},
743            }
744
745            // Check network
746            println!("\n🌐 {}", "Network connectivity:".yellow());
747            #[cfg(not(target_os = "windows"))]
748            {
749                if let Ok(output) = std::process::Command::new("ping")
750                    .args(["-c", "1", "-W", "2", "8.8.8.8"])
751                    .output()
752                {
753                    if output.status.success() {
754                        println!("   āœ… Internet connection OK");
755                    } else {
756                        println!("   āŒ No internet connection");
757                    }
758                }
759            }
760
761            // Check environment
762            let env_info = detect_environment();
763            println!("\nšŸŒ {}", "Environment detection:".yellow());
764            if env_info.is_docker {
765                println!("   🐳 Running in Docker container");
766            }
767            if env_info.is_wsl {
768                println!("   🪟 Running in WSL");
769            }
770            if env_info.is_ci {
771                println!("   šŸ¤– Running in CI/CD environment");
772            }
773            if env_info.is_ssh {
774                println!("   šŸ” Connected via SSH");
775            }
776            if env_info.has_proxy {
777                println!("   🌐 Proxy detected:");
778                if let Ok(http_proxy) =
779                    std::env::var("HTTP_PROXY").or_else(|_| std::env::var("http_proxy"))
780                {
781                    println!("      HTTP_PROXY: {}", http_proxy);
782                }
783                if let Ok(https_proxy) =
784                    std::env::var("HTTPS_PROXY").or_else(|_| std::env::var("https_proxy"))
785                {
786                    println!("      HTTPS_PROXY: {}", https_proxy);
787                }
788
789                // Show proxy configuration for package managers
790                match original_method {
791                    "npm" => {
792                        println!("\n   šŸ’” {}", "Configure npm for proxy:".yellow());
793                        println!("      {} npm config set proxy $HTTP_PROXY", "$".dimmed());
794                        println!(
795                            "      {} npm config set https-proxy $HTTPS_PROXY",
796                            "$".dimmed()
797                        );
798                    },
799                    "cargo" => {
800                        println!(
801                            "\n   šŸ’” {}",
802                            "Cargo uses system proxy automatically".yellow()
803                        );
804                    },
805                    _ => {},
806                }
807            }
808
809            // Check Node.js managers if npm failed
810            if original_method == "npm" {
811                let node_managers = detect_node_managers();
812                if node_managers.has_any() {
813                    println!("\nšŸš€ {}", "Node.js version managers detected:".yellow());
814                    if node_managers.has_nvm {
815                        println!("   āœ… nvm - Try: nvm install --lts");
816                    }
817                    if node_managers.has_fnm {
818                        println!("   āœ… fnm - Try: fnm install --lts");
819                    }
820                    if node_managers.has_n {
821                        println!("   āœ… n - Try: n lts");
822                    }
823                    if node_managers.has_volta {
824                        println!("   āœ… volta - Try: volta install node");
825                    }
826                    if node_managers.has_asdf {
827                        println!("   āœ… asdf - Try: asdf install nodejs latest");
828                    }
829                }
830            }
831
832            // Check proxy settings
833            println!("\nšŸ” {}", "Proxy settings:".yellow());
834            for var in ["HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy"] {
835                if let Ok(value) = std::env::var(var) {
836                    println!("   {}: {}", var, value);
837                }
838            }
839
840            println!("\nšŸ’” {}", "Common fixes:".cyan());
841            println!("   - Free up disk space (200MB needed)");
842            println!("   - Check file permissions");
843            println!("   - Configure proxy if behind firewall");
844            println!("   - Try different installation method");
845        },
846        RecoveryMethod::Cancel => {
847            println!("\nāŒ {}", "Installation cancelled".red());
848            return Err(anyhow::anyhow!("Installation cancelled by user"));
849        },
850    }
851
852    Ok(())
853}
854
855pub mod dev {
856    use super::*;
857    use clap::Subcommand;
858
859    #[derive(clap::Args)]
860    pub struct DevCommand {
861        #[command(subcommand)]
862        command: DevSubcommands,
863    }
864
865    #[derive(Subcommand)]
866    enum DevSubcommands {
867        /// Set up development environment
868        Setup {
869            /// Skip installing Rust tools
870            #[arg(long)]
871            skip_rust: bool,
872        },
873        /// Run security audit
874        Audit,
875        /// Generate documentation
876        Docs {
877            /// Open in browser after generation
878            #[arg(long)]
879            open: bool,
880        },
881    }
882
883    impl Execute for DevCommand {
884        async fn execute(&self) -> Result<()> {
885            match &self.command {
886                DevSubcommands::Setup { skip_rust } => setup_dev_env(*skip_rust).await,
887                DevSubcommands::Audit => run_security_audit().await,
888                DevSubcommands::Docs { open } => generate_docs(*open).await,
889            }
890        }
891    }
892
893    async fn setup_dev_env(skip_rust: bool) -> Result<()> {
894        use crate::platform::Platform;
895        use colored::*;
896
897        println!(
898            "\nšŸš€ {}",
899            "Setting up development environment...".bold().blue()
900        );
901
902        let platform = Platform::detect();
903
904        // Platform-specific emoji
905        let platform_display = match platform {
906            Platform::MacOS => format!("šŸŽ Platform: {}", platform),
907            Platform::Windows => format!("🪟 Platform: {}", platform),
908            Platform::Linux => format!("🐧 Platform: {}", platform),
909            _ => format!("šŸ–„ļø  Platform: {}", platform),
910        };
911        println!("{}", platform_display.cyan());
912
913        // Check development dependencies inline
914        println!("\nšŸ“‹ {}", "Checking system dependencies...".cyan());
915        check_basic_dev_deps(&platform)?;
916
917        if !skip_rust {
918            println!("\nšŸ¦€ {}", "Installing Rust development tools...".cyan());
919
920            let tools = [
921                ("cargo-audit", "šŸ›”ļø  Security vulnerability scanner"),
922                ("cargo-geiger", "ā˜¢ļø  Unsafe code detector"),
923                ("cargo-dist", "šŸ“¦ Distribution packaging tool"),
924            ];
925
926            for (tool, description) in &tools {
927                println!(
928                    "\n   {} {}: {}",
929                    "•".dimmed(),
930                    tool.bright_white(),
931                    description
932                );
933
934                // Check if already installed
935                if command_exists(tool) {
936                    println!("     āœ… {}", "Already installed".green());
937                } else {
938                    println!("     ā³ {}", "Installing...".yellow());
939
940                    let status = std::process::Command::new("cargo")
941                        .args(["install", tool])
942                        .status()?;
943
944                    if status.success() {
945                        println!("     āœ… {}", "Installed successfully!".green());
946                    } else {
947                        println!("     āŒ {}", "Installation failed".red());
948                        println!(
949                            "     šŸ’” {}",
950                            format!("Try: cargo install {} --force", tool).yellow()
951                        );
952                    }
953                }
954            }
955        } else {
956            println!(
957                "\nā­ļø  {}",
958                "Skipping Rust tools installation (--skip-rust)".dimmed()
959            );
960        }
961
962        println!(
963            "\n✨ {}",
964            "Development environment setup complete!".bold().green()
965        );
966        println!("šŸŽÆ {}", "You're ready to start developing!".green());
967
968        Ok(())
969    }
970
971    async fn run_security_audit() -> Result<()> {
972        tracing::info!("Running security audit...");
973
974        let output = std::process::Command::new("cargo")
975            .args(["audit"])
976            .output()?;
977
978        if !output.status.success() {
979            anyhow::bail!("Security audit failed");
980        }
981
982        tracing::info!("Security audit passed!");
983        Ok(())
984    }
985
986    async fn generate_docs(open: bool) -> Result<()> {
987        tracing::info!("Generating documentation...");
988
989        let mut cmd = std::process::Command::new("cargo");
990        cmd.args(["doc", "--no-deps"]);
991
992        if open {
993            cmd.arg("--open");
994        }
995
996        cmd.status()?;
997        Ok(())
998    }
999
1000    fn check_basic_dev_deps(platform: &crate::platform::Platform) -> Result<()> {
1001        use colored::*;
1002
1003        let essentials = match platform {
1004            crate::platform::Platform::Linux => vec![
1005                ("gcc", "šŸ”Ø C compiler", "sudo apt install build-essential"),
1006                ("git", "🌿 Version control", "sudo apt install git"),
1007            ],
1008            crate::platform::Platform::MacOS => vec![
1009                ("git", "🌿 Version control", "xcode-select --install"),
1010                ("cc", "šŸ”Ø C compiler", "xcode-select --install"),
1011            ],
1012            crate::platform::Platform::Windows => vec![(
1013                "git",
1014                "🌿 Version control",
1015                "https://git-scm.com/download/win",
1016            )],
1017            _ => vec![],
1018        };
1019
1020        let mut missing = false;
1021
1022        for (cmd, desc, install_hint) in essentials {
1023            print!("   {} {}: ", "•".dimmed(), desc);
1024            if command_exists(cmd) {
1025                println!("{}", "āœ…".green());
1026            } else {
1027                println!("{}", "āŒ Missing".red());
1028                println!("     šŸ’” Install: {}", install_hint.yellow());
1029                missing = true;
1030            }
1031        }
1032
1033        if missing {
1034            println!("\nāš ļø  {}", "Some essential tools are missing!".yellow());
1035        }
1036
1037        Ok(())
1038    }
1039}
1040
1041pub mod install {
1042    use super::*;
1043    use clap::Subcommand;
1044    use std::env;
1045    use std::process::Command;
1046
1047    #[derive(clap::Args)]
1048    pub struct InstallCommand {
1049        #[command(subcommand)]
1050        command: InstallSubcommands,
1051    }
1052
1053    #[derive(Subcommand)]
1054    enum InstallSubcommands {
1055        /// Install KindlyGuard MCP server
1056        #[command(visible_alias = "kindlyguard")]
1057        KindlyGuard {
1058            /// Installation method (auto-detected if not specified)
1059            #[arg(short, long)]
1060            method: Option<String>,
1061
1062            /// Version to install (latest if not specified)
1063            #[arg(long)]
1064            version: Option<String>,
1065        },
1066        /// Install all recommended tools
1067        All,
1068        /// Install MCP servers
1069        McpServers {
1070            /// Specific server to install
1071            #[arg(short, long)]
1072            server: Option<String>,
1073        },
1074        /// Install development dependencies
1075        DevDeps,
1076    }
1077
1078    impl Execute for InstallCommand {
1079        async fn execute(&self) -> Result<()> {
1080            match &self.command {
1081                InstallSubcommands::KindlyGuard { method, version } => {
1082                    install_kindlyguard(method.as_deref(), version.as_deref()).await
1083                },
1084                InstallSubcommands::All => install_all().await,
1085                InstallSubcommands::McpServers { server } => {
1086                    install_mcp_servers(server.as_deref()).await
1087                },
1088                InstallSubcommands::DevDeps => install_dev_deps().await,
1089            }
1090        }
1091    }
1092
1093    async fn install_kindlyguard(method: Option<&str>, version: Option<&str>) -> Result<()> {
1094        use crate::platform::Platform;
1095        use colored::*;
1096
1097        // Run pre-flight checks
1098        println!("\nšŸ” {}", "Running pre-installation checks...".cyan());
1099        let platform = Platform::detect();
1100        let env_info = detect_environment();
1101
1102        // Platform validation
1103        if platform == Platform::Unknown {
1104            println!(
1105                "šŸ¤” {}",
1106                "Hmm, couldn't detect your platform. Are you on a supported OS?".yellow()
1107            );
1108            println!(
1109                "šŸ’” {}",
1110                "Supported platforms: Linux, macOS, Windows".yellow()
1111            );
1112            println!(
1113                "šŸ’” {}",
1114                "Try specifying method manually: kindly-tools install kindlyguard --method npm"
1115                    .yellow()
1116            );
1117            return Err(anyhow::anyhow!("Unsupported platform"));
1118        }
1119
1120        // Version validation and normalization
1121        let version_str = if let Some(v) = version {
1122            validate_and_normalize_version(v)?
1123        } else {
1124            "latest".to_string()
1125        };
1126
1127        // Auto-detect best installation method if not specified
1128        let install_method = if let Some(m) = method {
1129            m.to_string()
1130        } else {
1131            detect_best_install_method(&platform)?
1132        };
1133
1134        // Display installation plan
1135        println!("\nšŸŽÆ {}", "Installation Plan".bold().blue());
1136        println!(
1137            "   šŸ“¦ Package: {}",
1138            "šŸ›”ļø KindlyGuard MCP Server".bright_white()
1139        );
1140
1141        // Platform-specific emoji
1142        let platform_display = match platform {
1143            Platform::MacOS => format!("šŸŽ {}", platform.to_string()),
1144            Platform::Windows => format!("🪟 {}", platform.to_string()),
1145            Platform::Linux => {
1146                let distro = detect_linux_distro();
1147                distro.display_name()
1148            },
1149            _ => format!("šŸ–„ļø  {}", platform.to_string()),
1150        };
1151        println!("   {}", platform_display.green());
1152
1153        // Show environment details if relevant
1154        if env_info.is_docker {
1155            println!("   🐳 Environment: {}", "Docker Container".cyan());
1156        }
1157        if env_info.is_wsl {
1158            println!(
1159                "   🪟 Environment: {}",
1160                "Windows Subsystem for Linux".cyan()
1161            );
1162        }
1163        if env_info.is_ci {
1164            println!("   šŸ¤– Environment: {}", "CI/CD Pipeline".cyan());
1165        }
1166        if env_info.is_ssh {
1167            println!("   šŸ” Connection: {}", "SSH Session".cyan());
1168        }
1169        if env_info.has_proxy {
1170            println!("   🌐 Network: {}", "Behind Proxy".yellow());
1171        }
1172
1173        println!("   āš™ļø Method: {}", install_method.green());
1174        println!("   šŸ·ļø Version: {}", version_str.green());
1175
1176        // Run additional pre-flight checks
1177        if let Err(e) = run_preflight_checks(&platform, &install_method).await {
1178            println!("\nāš ļø  {}", "Pre-flight check warnings:".yellow());
1179            println!("   {}", e.to_string().yellow());
1180            // Continue anyway, these are just warnings
1181        }
1182
1183        println!("\nā³ {}", "Preparing installation...".cyan());
1184
1185        match install_method.as_str() {
1186            "homebrew" | "brew" => {
1187                println!("šŸŗ {}", "Installing via Homebrew...".green());
1188
1189                if !command_exists("brew") {
1190                    println!("\nāŒ {}", "Homebrew not found!".red());
1191                    println!("šŸ”§ {}", "Install Homebrew first:".yellow());
1192                    println!("   {}", "/bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"".bright_white());
1193                    println!("\nšŸ’” {}", "Or try a different method:".yellow());
1194                    println!(
1195                        "   {}",
1196                        "kindly-tools install kindlyguard --method npm".bright_white()
1197                    );
1198                    return Err(anyhow::anyhow!("Homebrew not installed"));
1199                }
1200
1201                println!("\nšŸ“‹ {}", "Installation steps:".cyan());
1202                println!("   1ļøāƒ£  Add our tap:");
1203                println!(
1204                    "      {} {}",
1205                    "$".dimmed(),
1206                    "brew tap kindly-software-inc/tap".bright_white()
1207                );
1208                println!("   2ļøāƒ£  Install KindlyGuard:");
1209                println!(
1210                    "      {} {}",
1211                    "$".dimmed(),
1212                    "brew install kindlyguard".bright_white()
1213                );
1214
1215                if version_str != "latest" {
1216                    println!(
1217                        "\nāš ļø  {}",
1218                        "Note: Homebrew installs the latest version by default.".yellow()
1219                    );
1220                    println!(
1221                        "šŸ’” {}",
1222                        "For specific versions, use npm or cargo instead.".yellow()
1223                    );
1224                }
1225
1226                // Check if tap exists
1227                let output = std::process::Command::new("brew").args(["tap"]).output()?;
1228
1229                let taps = String::from_utf8_lossy(&output.stdout);
1230                let tap_exists = taps.contains("kindly-software-inc/tap");
1231
1232                if !tap_exists {
1233                    println!("\nā³ {}", "Adding tap...".yellow());
1234                    let tap_status = std::process::Command::new("brew")
1235                        .args(["tap", "kindly-software-inc/tap"])
1236                        .status();
1237
1238                    if tap_status.is_err() || !tap_status.unwrap().success() {
1239                        // Tap failed, show recovery menu
1240                        loop {
1241                            let recovery_method = show_recovery_menu("homebrew")?;
1242
1243                            if recovery_method == RecoveryMethod::Cancel {
1244                                return Err(anyhow::anyhow!("Installation cancelled"));
1245                            }
1246
1247                            execute_recovery(recovery_method, "homebrew", "kindlyguard", &platform)
1248                                .await?;
1249
1250                            let try_again = dialoguer::Confirm::new()
1251                                .with_prompt("Try another recovery method?")
1252                                .default(true)
1253                                .interact()?;
1254
1255                            if !try_again {
1256                                break;
1257                            }
1258                        }
1259                        return Ok(());
1260                    }
1261                }
1262
1263                println!("\nā³ {}", "Installing package...".yellow());
1264                let install_status = std::process::Command::new("brew")
1265                    .args(["install", "kindlyguard"])
1266                    .status();
1267
1268                match install_status {
1269                    Ok(s) if s.success() => {
1270                        println!("\nāœ… {}", "Installation successful!".green().bold());
1271                    },
1272                    _ => {
1273                        // Installation failed, show recovery menu
1274                        loop {
1275                            let recovery_method = show_recovery_menu("homebrew")?;
1276
1277                            if recovery_method == RecoveryMethod::Cancel {
1278                                return Err(anyhow::anyhow!("Installation cancelled"));
1279                            }
1280
1281                            execute_recovery(recovery_method, "homebrew", "kindlyguard", &platform)
1282                                .await?;
1283
1284                            let try_again = dialoguer::Confirm::new()
1285                                .with_prompt("Try another recovery method?")
1286                                .default(true)
1287                                .interact()?;
1288
1289                            if !try_again {
1290                                break;
1291                            }
1292                        }
1293                    },
1294                }
1295            },
1296            "npm" => {
1297                println!("šŸ“¦ {}", "Installing via npm...".green());
1298
1299                if !command_exists("npm") {
1300                    println!("\nāŒ {}", "npm not found!".red());
1301
1302                    // Check for Node.js version managers
1303                    let node_managers = detect_node_managers();
1304                    if node_managers.has_any() {
1305                        println!("šŸ” {}", "Detected Node.js version managers:".cyan());
1306                        if node_managers.has_nvm {
1307                            println!(
1308                                "   āœ… nvm detected! Try: {}",
1309                                "nvm install --lts && nvm use --lts".bright_white()
1310                            );
1311                        }
1312                        if node_managers.has_fnm {
1313                            println!(
1314                                "   āœ… fnm detected! Try: {}",
1315                                "fnm install --lts && fnm use lts-latest".bright_white()
1316                            );
1317                        }
1318                        if node_managers.has_n {
1319                            println!("   āœ… n detected! Try: {}", "n lts".bright_white());
1320                        }
1321                        if node_managers.has_volta {
1322                            println!(
1323                                "   āœ… volta detected! Try: {}",
1324                                "volta install node".bright_white()
1325                            );
1326                        }
1327                        if node_managers.has_asdf {
1328                            println!(
1329                                "   āœ… asdf detected! Try: {}",
1330                                "asdf plugin add nodejs && asdf install nodejs latest"
1331                                    .bright_white()
1332                            );
1333                        }
1334                        println!(
1335                            "\nšŸ’” {}",
1336                            "After activating Node.js, run this command again!".yellow()
1337                        );
1338                    } else {
1339                        println!("šŸ¤– {}", "Let's fix this! Choose your platform:".yellow());
1340
1341                        match platform {
1342                            Platform::MacOS => {
1343                                println!("\nšŸŽ {}", "macOS Options:".cyan());
1344                                println!(
1345                                    "   šŸŗ Using Homebrew: {}",
1346                                    "brew install node".bright_white()
1347                                );
1348                                println!(
1349                                    "   šŸ“„ Direct download: {}",
1350                                    "https://nodejs.org/".blue().underline()
1351                                );
1352                                println!("\nšŸš€ {}", "Recommended: Use a version manager".green());
1353                                println!("   • fnm (fast): {}", "brew install fnm".bright_white());
1354                                println!(
1355                                    "   • volta (reliable): {}",
1356                                    "brew install volta".bright_white()
1357                                );
1358                            },
1359                            Platform::Linux => {
1360                                let distro = detect_linux_distro();
1361                                println!("\n{} {}", distro.display_name(), "Options:".cyan());
1362
1363                                match distro {
1364                                    LinuxDistro::Ubuntu
1365                                    | LinuxDistro::Debian
1366                                    | LinuxDistro::Mint => {
1367                                        println!(
1368                                            "   šŸ“¦ System package: {}",
1369                                            "sudo apt update && sudo apt install nodejs npm"
1370                                                .bright_white()
1371                                        );
1372                                    },
1373                                    LinuxDistro::Fedora
1374                                    | LinuxDistro::CentOS
1375                                    | LinuxDistro::RHEL => {
1376                                        println!(
1377                                            "   šŸ“¦ System package: {}",
1378                                            "sudo dnf install nodejs npm".bright_white()
1379                                        );
1380                                    },
1381                                    LinuxDistro::Arch | LinuxDistro::Manjaro => {
1382                                        println!(
1383                                            "   šŸ“¦ System package: {}",
1384                                            "sudo pacman -S nodejs npm".bright_white()
1385                                        );
1386                                    },
1387                                    LinuxDistro::Alpine => {
1388                                        println!(
1389                                            "   šŸ“¦ System package: {}",
1390                                            "sudo apk add nodejs npm".bright_white()
1391                                        );
1392                                    },
1393                                    LinuxDistro::NixOS => {
1394                                        println!(
1395                                            "   šŸ“¦ System package: {}",
1396                                            "nix-env -iA nixpkgs.nodejs".bright_white()
1397                                        );
1398                                    },
1399                                    _ => {
1400                                        println!(
1401                                            "   šŸ“„ Direct download: {}",
1402                                            "https://nodejs.org/".blue().underline()
1403                                        );
1404                                    },
1405                                }
1406
1407                                println!("\nšŸš€ {}", "Recommended: Use a version manager".green());
1408                                println!(
1409                                    "   • fnm (fast): {}",
1410                                    "curl -fsSL https://fnm.vercel.app/install | bash"
1411                                        .bright_white()
1412                                );
1413                                println!(
1414                                    "   • volta (reliable): {}",
1415                                    "curl https://get.volta.sh | bash".bright_white()
1416                                );
1417                            },
1418                            Platform::Windows => {
1419                                println!("\n🪟 {}", "Windows Options:".cyan());
1420                                println!(
1421                                    "   šŸ« Using Chocolatey: {}",
1422                                    "choco install nodejs".bright_white()
1423                                );
1424                                println!(
1425                                    "   šŸž Using Scoop: {}",
1426                                    "scoop install nodejs".bright_white()
1427                                );
1428                                println!(
1429                                    "   šŸŒ€ Using winget: {}",
1430                                    "winget install OpenJS.NodeJS".bright_white()
1431                                );
1432                                println!(
1433                                    "   šŸ“„ Direct download: {}",
1434                                    "https://nodejs.org/".blue().underline()
1435                                );
1436                                println!("\nšŸš€ {}", "Recommended: Use volta".green());
1437                                println!("   • Install: {}", "choco install volta".bright_white());
1438                            },
1439                            _ => {
1440                                println!(
1441                                    "   šŸ“„ Direct download: {}",
1442                                    "https://nodejs.org/".blue().underline()
1443                                );
1444                            },
1445                        }
1446                    }
1447
1448                    println!(
1449                        "\nšŸ’” {}",
1450                        "After installing Node.js, run this command again!".yellow()
1451                    );
1452                    return Err(anyhow::anyhow!("npm not installed"));
1453                }
1454
1455                let package = if version_str != "latest" {
1456                    format!("kindly-guard-server@{}", version_str)
1457                } else {
1458                    "kindly-guard-server".to_string()
1459                };
1460
1461                println!("\nšŸ“‹ {}", "Installation command:".cyan());
1462                println!(
1463                    "   {} npm install -g {}",
1464                    "$".dimmed(),
1465                    package.bright_white()
1466                );
1467
1468                println!("\nā³ {}", "Attempting installation...".yellow());
1469                let status = std::process::Command::new("npm")
1470                    .args(["install", "-g", &package])
1471                    .status();
1472
1473                match status {
1474                    Ok(s) if s.success() => {
1475                        println!("\nāœ… {}", "Installation successful!".green().bold());
1476                    },
1477                    _ => {
1478                        // Installation failed, show recovery menu
1479                        loop {
1480                            let recovery_method = show_recovery_menu("npm")?;
1481
1482                            if recovery_method == RecoveryMethod::Cancel {
1483                                return Err(anyhow::anyhow!("Installation cancelled"));
1484                            }
1485
1486                            execute_recovery(recovery_method, "npm", &package, &platform).await?;
1487
1488                            // Ask if user wants to try another recovery method
1489                            let try_again = dialoguer::Confirm::new()
1490                                .with_prompt("Try another recovery method?")
1491                                .default(true)
1492                                .interact()?;
1493
1494                            if !try_again {
1495                                break;
1496                            }
1497                        }
1498                    },
1499                }
1500            },
1501            "cargo" => {
1502                println!("šŸ¦€ {}", "Installing KindlyGuard...".green());
1503                println!("šŸ“¦ {}", "Using GitHub releases for faster installation".cyan());
1504
1505                // Try to install from GitHub releases
1506                match install_kindlyguard_from_github(&version_str, &platform).await {
1507                    Ok(_) => {
1508                        // Success - installation complete
1509                    },
1510                    Err(e) => {
1511                        println!("\nāš ļø  {}", format!("GitHub download failed: {}", e).yellow());
1512                        println!("šŸ”„ {}", "Falling back to cargo install...".cyan());
1513
1514                        // Check if cargo is available for fallback
1515                        if !command_exists("cargo") {
1516                            println!("\nāŒ {}", "Cargo not found!".red());
1517                            println!("šŸ¦€ {}", "Let's install Rust and Cargo:".yellow());
1518                            println!("\nšŸ“‹ {}", "Quick install (all platforms):".cyan());
1519                            println!(
1520                                "   {} {}",
1521                                "$".dimmed(),
1522                                "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
1523                                    .bright_white()
1524                            );
1525
1526                            match platform {
1527                                Platform::Windows => {
1528                                    println!("\n🪟 {}", "Windows alternative:".cyan());
1529                                    println!(
1530                                        "   šŸ“„ Download installer: {}",
1531                                        "https://rustup.rs/".blue().underline()
1532                                    );
1533                                },
1534                                _ => {},
1535                            }
1536
1537                            println!(
1538                                "\nšŸ’” {}",
1539                                "After installing Rust, restart your terminal and try again!".yellow()
1540                            );
1541                            return Err(anyhow::anyhow!("Installation failed - no fallback available"));
1542                        }
1543
1544                        // Fallback to cargo install
1545                        let package = if version_str != "latest" {
1546                            format!("kindlyguard@{}", version_str)
1547                        } else {
1548                            "kindlyguard".to_string()
1549                        };
1550
1551                        println!("\nšŸ“‹ {}", "Fallback installation command:".cyan());
1552                        println!(
1553                            "   {} cargo install {}",
1554                            "$".dimmed(),
1555                            package.bright_white()
1556                        );
1557
1558                        println!(
1559                            "\nā³ {}",
1560                            "Attempting installation (this may take a while)...".yellow()
1561                        );
1562                        let status = std::process::Command::new("cargo")
1563                            .args(["install", &package])
1564                            .status();
1565
1566                        match status {
1567                            Ok(s) if s.success() => {
1568                                println!("\nāœ… {}", "Installation successful!".green().bold());
1569                                println!("šŸ“ {}", "Binary installed to ~/.cargo/bin/".cyan());
1570                            },
1571                            _ => {
1572                                // Installation failed, show recovery menu
1573                                loop {
1574                                    let recovery_method = show_recovery_menu("cargo")?;
1575
1576                                    if recovery_method == RecoveryMethod::Cancel {
1577                                        return Err(anyhow::anyhow!("Installation cancelled"));
1578                                    }
1579
1580                                    execute_recovery(recovery_method, "cargo", &package, &platform).await?;
1581
1582                                    // Ask if user wants to try another recovery method
1583                                    let try_again = dialoguer::Confirm::new()
1584                                        .with_prompt("Try another recovery method?")
1585                                        .default(true)
1586                                        .interact()?;
1587
1588                                    if !try_again {
1589                                        break;
1590                                    }
1591                                }
1592                            },
1593                        }
1594                    }
1595                }
1596            },
1597            "binary" => {
1598                println!("šŸ’æ {}", "Direct binary installation...".green());
1599
1600                let arch = crate::platform::Architecture::detect();
1601                println!(
1602                    "\nšŸ—ļø  {}",
1603                    format!("Detected architecture: {}", arch.name()).cyan()
1604                );
1605
1606                // Ask if user wants automatic download
1607                let auto_download = dialoguer::Confirm::new()
1608                    .with_prompt("Download and install automatically?")
1609                    .default(true)
1610                    .interact()?;
1611
1612                if auto_download {
1613                    // Use the same GitHub download functionality
1614                    match install_kindlyguard_from_github(&version_str, &platform).await {
1615                        Ok(_) => {
1616                            // Success - installation complete
1617                        },
1618                        Err(e) => {
1619                            println!("\nāš ļø  {}", format!("Automatic download failed: {}", e).yellow());
1620                            println!("šŸ“‹ {}", "You can download manually:".cyan());
1621                            
1622                            // Fall through to manual instructions
1623                            show_manual_download_instructions(&platform, &arch);
1624                        }
1625                    }
1626                } else {
1627                    show_manual_download_instructions(&platform, &arch);
1628                }
1629            },
1630            _ => {
1631                println!(
1632                    "\nāŒ {}",
1633                    format!("Unknown installation method: {}", install_method).red()
1634                );
1635                println!(
1636                    "šŸ¤” {}",
1637                    "Valid methods: homebrew, npm, cargo, binary".yellow()
1638                );
1639
1640                // Suggest best method
1641                let suggested = detect_best_install_method(&platform)?;
1642                println!(
1643                    "\nšŸ’” {}",
1644                    format!(
1645                        "Try: kindly-tools install kindlyguard --method {}",
1646                        suggested
1647                    )
1648                    .green()
1649                );
1650
1651                return Err(anyhow::anyhow!("Invalid installation method"));
1652            },
1653        }
1654
1655        println!("\nšŸŽÆ {}", "Next steps:".bold().green());
1656        println!(
1657            "   šŸš€ Start server: {}",
1658            "kindlyguard --stdio".bright_white()
1659        );
1660        println!("   šŸ“– Get help: {}", "kindlyguard --help".bright_white());
1661        println!("   šŸ”§ Configure: {}", "kindlyguard config".bright_white());
1662
1663        println!("\n🩺 {}", "If something goes wrong:".cyan());
1664        show_troubleshooting_tips();
1665
1666        // Run post-installation verification
1667        println!("\nšŸ”Ž {}", "Verifying installation...".bold().cyan());
1668        verify_installation(&install_method)?;
1669
1670        Ok(())
1671    }
1672
1673    /// Verify that the installation succeeded
1674    fn verify_installation(method: &str) -> Result<()> {
1675        use colored::*;
1676        use std::path::Path;
1677
1678        let mut checks_passed = true;
1679        let mut warnings = Vec::new();
1680
1681        // 1. Check if binary exists in expected locations
1682        println!("\nšŸ“ {}", "Checking binary locations...".cyan());
1683
1684        let binary_locations: Vec<String> = match method {
1685            "homebrew" | "brew" => vec![
1686                "/usr/local/bin/kindlyguard".to_string(),
1687                "/opt/homebrew/bin/kindlyguard".to_string(),
1688            ],
1689            "npm" => {
1690                let npm_prefix = Command::new("npm")
1691                    .args(["prefix", "-g"])
1692                    .output()
1693                    .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
1694                    .unwrap_or_default();
1695
1696                if npm_prefix.is_empty() {
1697                    vec![
1698                        "/usr/local/bin/kindlyguard".to_string(),
1699                        "/usr/bin/kindlyguard".to_string(),
1700                    ]
1701                } else {
1702                    vec![
1703                        format!("{}/bin/kindlyguard", npm_prefix),
1704                        "/usr/local/bin/kindlyguard".to_string(),
1705                    ]
1706                }
1707            },
1708            "cargo" => vec![
1709                format!(
1710                    "{}/.cargo/bin/kindlyguard",
1711                    env::var("HOME").unwrap_or_default()
1712                ),
1713                "/usr/local/bin/kindlyguard".to_string(),
1714            ],
1715            "binary" => vec![
1716                "/usr/local/bin/kindlyguard".to_string(),
1717                "/opt/kindlyguard/bin/kindlyguard".to_string(),
1718                format!("{}/bin/kindlyguard", env::var("HOME").unwrap_or_default()),
1719            ],
1720            _ => vec!["/usr/local/bin/kindlyguard".to_string()],
1721        };
1722
1723        let mut found_binary = None;
1724        for location in &binary_locations {
1725            if Path::new(location).exists() {
1726                println!("   āœ… Found binary at: {}", location.green());
1727                found_binary = Some(location.clone());
1728                break;
1729            }
1730        }
1731
1732        if found_binary.is_none() {
1733            println!("   āŒ {}", "Binary not found in expected locations".red());
1734            checks_passed = false;
1735
1736            // Additional check using 'which'
1737            if let Ok(output) = Command::new("which").arg("kindlyguard").output() {
1738                if output.status.success() {
1739                    let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
1740                    if !path.is_empty() {
1741                        println!("   āœ… Found binary via PATH at: {}", path.green());
1742                        found_binary = Some(path);
1743                        checks_passed = true;
1744                    }
1745                }
1746            }
1747        }
1748
1749        // 2. Run --version to verify it works
1750        if let Some(binary_path) = &found_binary {
1751            println!("\nšŸ”§ {}", "Checking binary execution...".cyan());
1752
1753            match Command::new(binary_path).arg("--version").output() {
1754                Ok(output) => {
1755                    if output.status.success() {
1756                        let version = String::from_utf8_lossy(&output.stdout);
1757                        println!("   āœ… Binary executes successfully");
1758                        println!("   šŸ“Œ Version: {}", version.trim().green());
1759                    } else {
1760                        println!("   āŒ {}", "Binary failed to execute".red());
1761                        let stderr = String::from_utf8_lossy(&output.stderr);
1762                        if !stderr.is_empty() {
1763                            println!("   šŸ’” Error: {}", stderr.trim().yellow());
1764                        }
1765                        checks_passed = false;
1766                    }
1767                },
1768                Err(e) => {
1769                    println!("   āŒ {}", format!("Failed to run binary: {}", e).red());
1770                    checks_passed = false;
1771                },
1772            }
1773        }
1774
1775        // 3. Check file permissions (Unix only)
1776        #[cfg(unix)]
1777        if let Some(binary_path) = &found_binary {
1778            println!("\nšŸ” {}", "Checking file permissions...".cyan());
1779
1780            use std::os::unix::fs::PermissionsExt;
1781            match std::fs::metadata(binary_path) {
1782                Ok(metadata) => {
1783                    let mode = metadata.permissions().mode();
1784                    let is_executable = mode & 0o111 != 0;
1785
1786                    if is_executable {
1787                        println!("   āœ… Binary has executable permissions");
1788                    } else {
1789                        println!("   āŒ {}", "Binary is not executable".red());
1790                        println!(
1791                            "   šŸ’” Fix with: {}",
1792                            format!("chmod +x {}", binary_path).yellow()
1793                        );
1794                        checks_passed = false;
1795                    }
1796                },
1797                Err(e) => {
1798                    println!(
1799                        "   āš ļø  {}",
1800                        format!("Could not check permissions: {}", e).yellow()
1801                    );
1802                    warnings.push("Could not verify file permissions");
1803                },
1804            }
1805        }
1806
1807        // 4. Check if PATH contains the install directory
1808        println!("\n🌐 {}", "Checking PATH configuration...".cyan());
1809
1810        if let Ok(path_var) = env::var("PATH") {
1811            let path_contains_binary = if let Some(binary_path) = &found_binary {
1812                if let Some(parent) = Path::new(binary_path).parent() {
1813                    path_var.split(':').any(|p| Path::new(p) == parent)
1814                } else {
1815                    false
1816                }
1817            } else {
1818                false
1819            };
1820
1821            if path_contains_binary || command_exists("kindlyguard") {
1822                println!("   āœ… kindlyguard is accessible via PATH");
1823            } else {
1824                println!("   āš ļø  {}", "kindlyguard directory not in PATH".yellow());
1825                warnings.push("PATH configuration needed");
1826
1827                // Detect shell and provide instructions
1828                detect_and_show_path_instructions(method);
1829            }
1830        }
1831
1832        // Summary
1833        println!("\nšŸ“‹ {}", "Verification Summary".bold().blue());
1834
1835        if checks_passed && warnings.is_empty() {
1836            println!(
1837                "\nāœ… {}",
1838                "All checks passed! Installation verified.".bold().green()
1839            );
1840            println!("šŸš€ {}", "You can now run: kindlyguard --help".green());
1841        } else if checks_passed && !warnings.is_empty() {
1842            println!(
1843                "\nāœ… {}",
1844                "Installation succeeded with warnings:".bold().yellow()
1845            );
1846            for warning in &warnings {
1847                println!("   āš ļø  {}", warning.yellow());
1848            }
1849        } else {
1850            println!("\nāŒ {}", "Installation verification failed!".bold().red());
1851            println!(
1852                "šŸ”§ {}",
1853                "Please check the errors above and try again.".yellow()
1854            );
1855            return Err(anyhow::anyhow!("Installation verification failed"));
1856        }
1857
1858        Ok(())
1859    }
1860
1861    /// Show manual download instructions for binary installation
1862    fn show_manual_download_instructions(platform: &Platform, arch: &crate::platform::Architecture) {
1863        use colored::*;
1864        
1865        println!("\nšŸ“„ {}", "Download options:".cyan());
1866        println!(
1867            "   🌐 Visit: {}",
1868            "https://github.com/kindly-software-inc/kindly-guard/releases"
1869                .blue()
1870                .underline()
1871        );
1872
1873        match platform {
1874            Platform::MacOS => {
1875                let arch_str = if *arch == crate::platform::Architecture::Arm64 {
1876                    "aarch64"
1877                } else {
1878                    "x86_64"
1879                };
1880                println!("\nšŸŽ {}", "macOS binary:".cyan());
1881                println!(
1882                    "   šŸ“¦ File: {}",
1883                    format!("kindlyguard-{}-apple-darwin", arch_str)
1884                        .bright_white()
1885                );
1886
1887                println!("\nšŸ“‹ {}", "Installation steps:".cyan());
1888                println!("   1ļøāƒ£  Download the binary file");
1889                println!(
1890                    "   2ļøāƒ£  Move to PATH: {}",
1891                    "sudo mv kindlyguard /usr/local/bin/".bright_white()
1892                );
1893                println!(
1894                    "   3ļøāƒ£  Make executable: {}",
1895                    "sudo chmod +x /usr/local/bin/kindlyguard".bright_white()
1896                );
1897            },
1898            Platform::Linux => {
1899                println!("\n🐧 {}", "Linux binary:".cyan());
1900                println!(
1901                    "   šŸ“¦ File: {}",
1902                    "kindlyguard-x86_64-unknown-linux-gnu".bright_white()
1903                );
1904
1905                println!("\nšŸ“‹ {}", "Installation steps:".cyan());
1906                println!("   1ļøāƒ£  Download the binary file");
1907                println!(
1908                    "   2ļøāƒ£  Move to PATH: {}",
1909                    "sudo mv kindlyguard /usr/local/bin/".bright_white()
1910                );
1911                println!(
1912                    "   3ļøāƒ£  Make executable: {}",
1913                    "sudo chmod +x /usr/local/bin/kindlyguard".bright_white()
1914                );
1915            },
1916            Platform::Windows => {
1917                println!("\n🪟 {}", "Windows binary:".cyan());
1918                println!(
1919                    "   šŸ“¦ File: {}",
1920                    "kindlyguard-x86_64-pc-windows-msvc.exe".bright_white()
1921                );
1922
1923                println!("\nšŸ“‹ {}", "Installation steps:".cyan());
1924                println!("   1ļøāƒ£  Download the .exe file");
1925                println!("   šŸ“‚ Move to C:\\Program Files\\KindlyGuard\\");
1926                println!(
1927                    "   šŸ”§ Add to PATH: {}",
1928                    "%ProgramFiles%\\KindlyGuard".yellow()
1929                );
1930            },
1931            Platform::Unknown => {
1932                println!("\nā“ {}", "Unknown platform:".yellow());
1933                println!("   Please visit the releases page for manual download");
1934            },
1935        }
1936
1937        println!("\nāš ļø  {}", "Important:".yellow());
1938        println!("   šŸ” Verify checksums after download");
1939        println!("   šŸ”’ Check file permissions are correct");
1940        println!("   šŸ“ Ensure binary is in your PATH");
1941    }
1942
1943    /// Detect shell and show PATH configuration instructions
1944    fn detect_and_show_path_instructions(method: &str) -> () {
1945        use colored::*;
1946
1947        // Detect current shell
1948        let shell = env::var("SHELL").unwrap_or_default();
1949        let shell_name = if shell.contains("bash") {
1950            "bash"
1951        } else if shell.contains("zsh") {
1952            "zsh"
1953        } else if shell.contains("fish") {
1954            "fish"
1955        } else {
1956            "sh"
1957        };
1958
1959        println!("\nšŸ’” {}", "To add kindlyguard to your PATH:".cyan());
1960
1961        let path_to_add = match method {
1962            "npm" => {
1963                let npm_prefix = Command::new("npm")
1964                    .args(["prefix", "-g"])
1965                    .output()
1966                    .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
1967                    .unwrap_or_else(|_| "/usr/local".to_string());
1968                format!("{}/bin", npm_prefix)
1969            },
1970            "cargo" => format!("$HOME/.cargo/bin"),
1971            "homebrew" | "brew" => {
1972                if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") {
1973                    "/opt/homebrew/bin".to_string()
1974                } else {
1975                    "/usr/local/bin".to_string()
1976                }
1977            },
1978            _ => "/usr/local/bin".to_string(),
1979        };
1980
1981        match shell_name {
1982            "bash" => {
1983                println!("\n   🐚 For Bash:");
1984                println!("      1. Add to ~/.bashrc:");
1985                println!(
1986                    "         {} echo 'export PATH=\"{}:$PATH\"' >> ~/.bashrc",
1987                    "$".dimmed(),
1988                    path_to_add.bright_white()
1989                );
1990                println!("      2. Reload:");
1991                println!("         {} source ~/.bashrc", "$".dimmed());
1992            },
1993            "zsh" => {
1994                println!("\n   🐚 For Zsh:");
1995                println!("      1. Add to ~/.zshrc:");
1996                println!(
1997                    "         {} echo 'export PATH=\"{}:$PATH\"' >> ~/.zshrc",
1998                    "$".dimmed(),
1999                    path_to_add.bright_white()
2000                );
2001                println!("      2. Reload:");
2002                println!("         {} source ~/.zshrc", "$".dimmed());
2003            },
2004            "fish" => {
2005                println!("\n   🐚 For Fish:");
2006                println!("      1. Add to config:");
2007                println!(
2008                    "         {} fish_add_path {}",
2009                    "$".dimmed(),
2010                    path_to_add.bright_white()
2011                );
2012                println!("      2. Or manually:");
2013                println!(
2014                    "         {} set -Ua fish_user_paths {}",
2015                    "$".dimmed(),
2016                    path_to_add.bright_white()
2017                );
2018            },
2019            _ => {
2020                println!("\n   🐚 For your shell:");
2021                println!("      1. Add to your shell config file:");
2022                println!(
2023                    "         export PATH=\"{}:$PATH\"",
2024                    path_to_add.bright_white()
2025                );
2026                println!("      2. Reload your shell configuration");
2027            },
2028        }
2029
2030        println!("\n   šŸ’” After updating PATH, restart your terminal or run the reload command");
2031    }
2032
2033    fn validate_and_normalize_version(version: &str) -> Result<String> {
2034        use colored::*;
2035
2036        // Remove common prefixes
2037        let normalized = version
2038            .trim()
2039            .trim_start_matches('v')
2040            .trim_start_matches('V');
2041
2042        // Basic validation
2043        if normalized.is_empty() {
2044            println!("āš ļø  {}", "Empty version specified, using 'latest'".yellow());
2045            return Ok("latest".to_string());
2046        }
2047
2048        // Check format (basic semver validation)
2049        let parts: Vec<&str> = normalized.split('.').collect();
2050        if parts.len() != 3 && normalized != "latest" {
2051            println!(
2052                "āš ļø  {}",
2053                format!(
2054                    "Version '{}' doesn't look like semantic versioning",
2055                    version
2056                )
2057                .yellow()
2058            );
2059            println!("šŸ’” {}", "Expected format: X.Y.Z (e.g., 0.10.3)".yellow());
2060            println!(
2061                "šŸ“‹ {}",
2062                "Available versions: latest, 0.10.3, 0.10.2, 0.10.1".cyan()
2063            );
2064
2065            // Still allow it, just warn
2066        }
2067
2068        Ok(normalized.to_string())
2069    }
2070
2071    fn detect_best_install_method(platform: &crate::platform::Platform) -> Result<String> {
2072        match platform {
2073            crate::platform::Platform::MacOS => {
2074                if command_exists("brew") {
2075                    Ok("homebrew".to_string())
2076                } else if command_exists("npm") {
2077                    Ok("npm".to_string())
2078                } else if command_exists("cargo") {
2079                    Ok("cargo".to_string())
2080                } else {
2081                    Ok("binary".to_string())
2082                }
2083            },
2084            crate::platform::Platform::Linux => {
2085                if command_exists("npm") {
2086                    Ok("npm".to_string())
2087                } else if command_exists("cargo") {
2088                    Ok("cargo".to_string())
2089                } else {
2090                    Ok("binary".to_string())
2091                }
2092            },
2093            crate::platform::Platform::Windows => {
2094                if command_exists("npm") {
2095                    Ok("npm".to_string())
2096                } else if command_exists("cargo") {
2097                    Ok("cargo".to_string())
2098                } else {
2099                    Ok("binary".to_string())
2100                }
2101            },
2102            _ => Ok("npm".to_string()),
2103        }
2104    }
2105
2106    async fn run_preflight_checks(
2107        _platform: &crate::platform::Platform,
2108        method: &str,
2109    ) -> Result<()> {
2110        use colored::*;
2111
2112        // Check disk space (simplified - just a warning)
2113        #[cfg(not(target_os = "windows"))]
2114        {
2115            if let Ok(output) = std::process::Command::new("df").args(["-h", "/"]).output() {
2116                let output_str = String::from_utf8_lossy(&output.stdout);
2117                // Very basic check - just warn if root partition seems full
2118                if output_str.contains("100%")
2119                    || output_str.contains("99%")
2120                    || output_str.contains("98%")
2121                {
2122                    println!("šŸ’¾ {}", "Warning: Disk space is running low!".yellow());
2123                    println!("   {}", "Installation needs approximately 200MB".yellow());
2124                }
2125            }
2126        }
2127
2128        // Check network connectivity for non-binary methods
2129        if method != "binary" {
2130            // Simple check - try to resolve a common domain
2131            #[cfg(not(target_os = "windows"))]
2132            {
2133                if let Err(_) = std::process::Command::new("ping")
2134                    .args(["-c", "1", "-W", "2", "8.8.8.8"])
2135                    .output()
2136                {
2137                    println!("🌐 {}", "Network connectivity might be limited".yellow());
2138                    println!(
2139                        "   {}",
2140                        "If installation fails, check your internet connection".yellow()
2141                    );
2142                }
2143            }
2144        }
2145
2146        Ok(())
2147    }
2148
2149    fn show_troubleshooting_tips() {
2150        use colored::*;
2151
2152        println!("   šŸ“ Check internet connection");
2153        println!("   šŸ“ Verify disk space (200MB needed)");
2154        println!("   šŸ“ Ensure admin/sudo permissions");
2155        println!("   šŸ“ Try a different installation method");
2156        println!("\nšŸ†˜ {}", "Need help?".cyan());
2157        println!(
2158            "   šŸ“š Docs: {}",
2159            "https://github.com/kindly-software-inc/kindly-guard/wiki"
2160                .blue()
2161                .underline()
2162        );
2163        println!(
2164            "   šŸ› Issues: {}",
2165            "https://github.com/kindly-software-inc/kindly-guard/issues"
2166                .blue()
2167                .underline()
2168        );
2169    }
2170
2171    async fn install_all() -> Result<()> {
2172        use colored::*;
2173
2174        println!(
2175            "\nšŸŽ {}",
2176            "Installing all recommended tools...".bold().blue()
2177        );
2178        println!("šŸ“¦ {}", "This will install:".cyan());
2179        println!("   1ļøāƒ£  KindlyGuard MCP Server");
2180        println!("   2ļøāƒ£  Recommended MCP servers");
2181        println!("   3ļøāƒ£  Development dependencies");
2182
2183        println!("\nā³ {}", "Step 1/3: Installing KindlyGuard...".cyan());
2184        install_kindlyguard(None, None).await?;
2185
2186        println!("\nā³ {}", "Step 2/3: Installing MCP servers...".cyan());
2187        install_mcp_servers(None).await?;
2188
2189        println!("\nā³ {}", "Step 3/3: Installing dev dependencies...".cyan());
2190        install_dev_deps().await?;
2191
2192        println!(
2193            "\nšŸŽ‰ {}",
2194            "All tools installed successfully!".bold().green()
2195        );
2196        println!("✨ {}", "Your development environment is ready!".green());
2197
2198        Ok(())
2199    }
2200
2201    async fn install_mcp_servers(server: Option<&str>) -> Result<()> {
2202        use colored::*;
2203        use dialoguer::Confirm;
2204
2205        let servers = if let Some(s) = server {
2206            vec![(s, get_server_description(s))]
2207        } else {
2208            vec![
2209                ("tree-sitter", "🌳 Parse and analyze code structure"),
2210                ("ast-grep", "šŸ” Search code with AST patterns"),
2211                ("filesystem", "šŸ“ Enhanced file system access"),
2212            ]
2213        };
2214
2215        println!("\nšŸ”Œ {}", "MCP Server Installation".bold().blue());
2216
2217        for (server_name, description) in servers {
2218            println!("\nšŸ“¦ {}: {}", server_name.cyan(), description);
2219
2220            if Confirm::new()
2221                .with_prompt(format!("   Install '{}'?", server_name))
2222                .default(true)
2223                .interact()?
2224            {
2225                println!(
2226                    "   ā³ {}",
2227                    format!("Installing {}...", server_name).yellow()
2228                );
2229
2230                // TODO: Add actual installation logic
2231                println!(
2232                    "   āœ… {}",
2233                    format!("{} installed successfully!", server_name).green()
2234                );
2235
2236                // Provide configuration tips
2237                match server_name {
2238                    "tree-sitter" => {
2239                        println!(
2240                            "   šŸ’” {}",
2241                            "Tip: Use for code navigation and refactoring".yellow()
2242                        );
2243                    },
2244                    "ast-grep" => {
2245                        println!("   šŸ’” {}", "Tip: Great for finding code patterns".yellow());
2246                    },
2247                    "filesystem" => {
2248                        println!(
2249                            "   šŸ’” {}",
2250                            "Tip: Provides secure file access to Claude".yellow()
2251                        );
2252                    },
2253                    _ => {},
2254                }
2255            } else {
2256                println!("   ā­ļø  {}", format!("Skipping {}", server_name).dimmed());
2257            }
2258        }
2259
2260        println!(
2261            "\nšŸ“ {}",
2262            "Note: Restart Claude Desktop after installing MCP servers".cyan()
2263        );
2264
2265        Ok(())
2266    }
2267
2268    fn get_server_description(server: &str) -> &'static str {
2269        match server {
2270            "tree-sitter" => "🌳 Parse and analyze code structure",
2271            "ast-grep" => "šŸ” Search code with AST patterns",
2272            "filesystem" => "šŸ“ Enhanced file system access",
2273            "semgrep" => "šŸ›”ļø  Security vulnerability scanning",
2274            "github" => "šŸ™ GitHub repository integration",
2275            _ => "šŸ“¦ MCP server extension",
2276        }
2277    }
2278
2279    async fn install_dev_deps() -> Result<()> {
2280        use colored::*;
2281
2282        println!("\nšŸ› ļø  {}", "Development Dependencies Check".bold().blue());
2283
2284        let platform = crate::platform::Platform::detect();
2285        let packages = match platform {
2286            crate::platform::Platform::Linux => vec![
2287                (
2288                    "build-essential",
2289                    "šŸ”Ø C/C++ compiler toolchain",
2290                    "sudo apt install build-essential",
2291                ),
2292                (
2293                    "pkg-config",
2294                    "šŸ“¦ Library configuration tool",
2295                    "sudo apt install pkg-config",
2296                ),
2297                (
2298                    "libssl-dev",
2299                    "šŸ” SSL development headers",
2300                    "sudo apt install libssl-dev",
2301                ),
2302            ],
2303            crate::platform::Platform::MacOS => vec![
2304                (
2305                    "xcode-select",
2306                    "šŸŽ Xcode command line tools",
2307                    "xcode-select --install",
2308                ),
2309                (
2310                    "pkg-config",
2311                    "šŸ“¦ Library configuration tool",
2312                    "brew install pkg-config",
2313                ),
2314            ],
2315            crate::platform::Platform::Windows => vec![(
2316                "visual-studio",
2317                "🪟 Visual Studio Build Tools",
2318                "Download from https://visualstudio.microsoft.com/downloads/",
2319            )],
2320            _ => vec![],
2321        };
2322
2323        let mut missing = Vec::new();
2324
2325        println!("\nšŸ” {}", "Checking system dependencies...".cyan());
2326
2327        for (pkg, description, install_cmd) in &packages {
2328            print!("   {} {}: ", "•".dimmed(), description);
2329
2330            // Simple check - just see if command exists or path exists
2331            let is_installed = match *pkg {
2332                "xcode-select" => command_exists("xcodebuild"),
2333                "visual-studio" => {
2334                    // Check common VS paths
2335                    std::path::Path::new("C:\\Program Files\\Microsoft Visual Studio").exists()
2336                        || std::path::Path::new("C:\\Program Files (x86)\\Microsoft Visual Studio")
2337                            .exists()
2338                },
2339                _ => command_exists(pkg),
2340            };
2341
2342            if is_installed {
2343                println!("{}", "āœ… Installed".green());
2344            } else {
2345                println!("{}", "āŒ Not found".red());
2346                missing.push((pkg, description, install_cmd));
2347            }
2348        }
2349
2350        if !missing.is_empty() {
2351            println!("\nāš ļø  {}", "Missing dependencies detected!".yellow());
2352            println!("šŸ“‹ {}", "Installation commands:".cyan());
2353
2354            for (pkg, _desc, cmd) in missing {
2355                println!("\n   {} {}:", "•".dimmed(), pkg.bright_white());
2356                println!("     {}", cmd.bright_white());
2357            }
2358
2359            println!(
2360                "\nšŸ’” {}",
2361                "Install these dependencies for optimal development experience".yellow()
2362            );
2363        } else {
2364            println!(
2365                "\nāœ… {}",
2366                "All development dependencies are installed!".green()
2367            );
2368        }
2369
2370        // Additional recommendations
2371        println!("\nšŸš€ {}", "Recommended Rust tools:".cyan());
2372        println!(
2373            "   {} cargo-watch - {}",
2374            "•".dimmed(),
2375            "Auto-rebuild on file changes".yellow()
2376        );
2377        println!(
2378            "   {} cargo-nextest - {}",
2379            "•".dimmed(),
2380            "3x faster test runner".yellow()
2381        );
2382        println!(
2383            "   {} sccache - {}",
2384            "•".dimmed(),
2385            "Compilation cache for faster builds".yellow()
2386        );
2387
2388        println!(
2389            "\nšŸ’” {}",
2390            "Install with: cargo install cargo-watch cargo-nextest sccache".cyan()
2391        );
2392
2393        Ok(())
2394    }
2395}
2396
2397pub mod mcp {
2398    use super::*;
2399    use clap::Subcommand;
2400    use serde::{Deserialize, Serialize};
2401    use std::collections::HashMap;
2402    use std::io::Write;
2403    use std::process::{Command, Stdio};
2404
2405    #[derive(clap::Args)]
2406    pub struct McpCommand {
2407        #[command(subcommand)]
2408        command: McpSubcommands,
2409    }
2410
2411    #[derive(Subcommand)]
2412    enum McpSubcommands {
2413        /// Set up and install MCP server for KindlyGuard
2414        Setup {
2415            /// Skip interactive prompts and use defaults
2416            #[arg(short, long)]
2417            non_interactive: bool,
2418
2419            /// Force reinstall even if already set up
2420            #[arg(short, long)]
2421            force: bool,
2422        },
2423
2424        /// Verify MCP configuration and server status
2425        Verify {
2426            /// Show detailed verification output
2427            #[arg(short, long)]
2428            verbose: bool,
2429        },
2430
2431        /// Show current MCP server status
2432        Status {
2433            /// Show process information
2434            #[arg(short, long)]
2435            processes: bool,
2436        },
2437
2438        /// Start the MCP server
2439        Start {
2440            /// Run in background (daemon mode)
2441            #[arg(short, long)]
2442            daemon: bool,
2443        },
2444
2445        /// Stop the MCP server
2446        Stop {
2447            /// Force stop all instances
2448            #[arg(short, long)]
2449            force: bool,
2450        },
2451
2452        /// List installed MCP servers
2453        List,
2454
2455        /// Configure MCP servers
2456        Config {
2457            /// Path to custom configuration file
2458            #[arg(short, long)]
2459            file: Option<PathBuf>,
2460
2461            /// Show current configuration
2462            #[arg(short, long)]
2463            show: bool,
2464        },
2465
2466        /// Test MCP server connection
2467        Test {
2468            /// Server to test
2469            server: String,
2470        },
2471    }
2472
2473    #[derive(Serialize, Deserialize)]
2474    struct McpConfig {
2475        #[serde(rename = "mcpServers")]
2476        servers: std::collections::HashMap<String, ServerConfig>,
2477    }
2478
2479    #[derive(Serialize, Deserialize)]
2480    struct ServerConfig {
2481        #[serde(rename = "type", default)]
2482        server_type: Option<String>,
2483        command: String,
2484        args: Vec<String>,
2485        #[serde(default)]
2486        env: std::collections::HashMap<String, String>,
2487    }
2488
2489    impl Execute for McpCommand {
2490        async fn execute(&self) -> Result<()> {
2491            match &self.command {
2492                McpSubcommands::Setup {
2493                    non_interactive,
2494                    force,
2495                } => setup_mcp_server(*non_interactive, *force).await,
2496                McpSubcommands::Verify { verbose } => verify_mcp_setup(*verbose).await,
2497                McpSubcommands::Status { processes } => show_mcp_status(*processes).await,
2498                McpSubcommands::Start { daemon } => start_mcp_server(*daemon).await,
2499                McpSubcommands::Stop { force } => stop_mcp_server(*force).await,
2500                McpSubcommands::List => list_mcp_servers().await,
2501                McpSubcommands::Config { file, show } => {
2502                    configure_mcp(file.as_deref(), *show).await
2503                },
2504                McpSubcommands::Test { server } => test_mcp_server(server).await,
2505            }
2506        }
2507    }
2508
2509    async fn setup_mcp_server(non_interactive: bool, force: bool) -> Result<()> {
2510        tracing::info!("Setting up MCP server for KindlyGuard");
2511
2512        // Check if already configured
2513        let config_path = get_mcp_config_path()?;
2514        if config_path.exists() && !force {
2515            tracing::warn!("MCP configuration already exists at {:?}", config_path);
2516            if !non_interactive {
2517                let proceed = dialoguer::Confirm::new()
2518                    .with_prompt("Configuration exists. Overwrite?")
2519                    .default(false)
2520                    .interact()?;
2521                if !proceed {
2522                    tracing::info!("Setup cancelled");
2523                    return Ok(());
2524                }
2525            } else {
2526                tracing::info!("Use --force to overwrite existing configuration");
2527                return Ok(());
2528            }
2529        }
2530
2531        // Build the project first if needed
2532        if !non_interactive {
2533            let build = dialoguer::Confirm::new()
2534                .with_prompt("Build kindly-guard-server first?")
2535                .default(true)
2536                .interact()?;
2537            if build {
2538                tracing::info!("Building kindly-guard-server in release mode...");
2539                let status = Command::new("cargo")
2540                    .args(["build", "--release", "--package", "kindly-guard-server"])
2541                    .current_dir(find_project_root()?)
2542                    .status()?;
2543                if !status.success() {
2544                    return Err(anyhow::anyhow!("Build failed"));
2545                }
2546            }
2547        }
2548
2549        // Find KindlyGuard server binary
2550        let kg_server = find_kindlyguard_server()?;
2551        tracing::info!("Found KindlyGuard server at: {:?}", kg_server);
2552
2553        // Create MCP server directory
2554        let mcp_server_dir = home_dir()?.join(".claude/mcp-servers/kindly-guard");
2555        std::fs::create_dir_all(&mcp_server_dir)?;
2556
2557        // Copy binary to MCP server directory
2558        let target_binary = mcp_server_dir.join("kindly-guard");
2559        std::fs::copy(&kg_server, &target_binary)?;
2560        #[cfg(unix)]
2561        {
2562            use std::os::unix::fs::PermissionsExt;
2563            let mut perms = std::fs::metadata(&target_binary)?.permissions();
2564            perms.set_mode(0o755);
2565            std::fs::set_permissions(&target_binary, perms)?;
2566        }
2567
2568        // Create default configuration file
2569        let server_config_file = mcp_server_dir.join("config.toml");
2570        if !server_config_file.exists() || force {
2571            let config_content = r#"# Kindly Guard Configuration
2572mode = "standard"
2573log_level = "info"
2574
2575[rate_limit]
2576window_secs = 60
2577max_requests = 100
2578
2579[scanner]
2580max_input_size = 1048576  # 1MB
2581patterns_file = ""
2582
2583[metrics]
2584enabled = true
2585export_interval_secs = 60
2586
2587[auth]
2588require_auth = false
2589"#;
2590            std::fs::write(&server_config_file, config_content)?;
2591        }
2592
2593        // Create or update MCP configuration
2594        let mut config = if config_path.exists() {
2595            let content = std::fs::read_to_string(&config_path)?;
2596            serde_json::from_str(&content)?
2597        } else {
2598            McpConfig {
2599                servers: HashMap::new(),
2600            }
2601        };
2602
2603        // Add KindlyGuard server
2604        let mut env = HashMap::new();
2605        env.insert("RUST_LOG".to_string(), "info".to_string());
2606
2607        config.servers.insert(
2608            "kindly-guard".to_string(),
2609            ServerConfig {
2610                server_type: Some("stdio".to_string()),
2611                command: target_binary.to_string_lossy().to_string(),
2612                args: vec![
2613                    "--config".to_string(),
2614                    server_config_file.to_string_lossy().to_string(),
2615                ],
2616                env,
2617            },
2618        );
2619
2620        // Save configuration
2621        let content = serde_json::to_string_pretty(&config)?;
2622        std::fs::write(&config_path, content)?;
2623
2624        tracing::info!("MCP configuration saved to: {:?}", config_path);
2625        tracing::info!("Setup complete! Restart Claude Desktop to use the MCP server.");
2626
2627        Ok(())
2628    }
2629
2630    async fn verify_mcp_setup(verbose: bool) -> Result<()> {
2631        tracing::info!("Verifying MCP configuration");
2632
2633        // Check configuration file
2634        let config_path = get_mcp_config_path()?;
2635        if !config_path.exists() {
2636            tracing::error!("MCP configuration not found at {:?}", config_path);
2637            return Err(anyhow::anyhow!("MCP not configured"));
2638        }
2639
2640        // Load configuration
2641        let content = std::fs::read_to_string(&config_path)?;
2642        let config: McpConfig = serde_json::from_str(&content)?;
2643
2644        // Check for KindlyGuard server
2645        if let Some(server) = config.servers.get("kindly-guard") {
2646            let command_path = Path::new(&server.command);
2647            if !command_path.exists() {
2648                tracing::error!("Server binary not found at: {:?}", command_path);
2649                return Err(anyhow::anyhow!("Server binary not found"));
2650            }
2651
2652            // Test server execution
2653            if verbose {
2654                tracing::info!("Testing server execution...");
2655                let output = Command::new(&server.command).arg("--version").output()?;
2656
2657                if output.status.success() {
2658                    let version = String::from_utf8_lossy(&output.stdout);
2659                    tracing::info!("Server version: {}", version.trim());
2660                }
2661            }
2662
2663            // Test MCP protocol
2664            tracing::info!("Testing MCP protocol communication...");
2665            test_mcp_protocol(&server.command, &server.args)?;
2666
2667            tracing::info!("MCP configuration verified successfully");
2668        } else {
2669            tracing::error!("KindlyGuard server not configured");
2670            return Err(anyhow::anyhow!("Server not in configuration"));
2671        }
2672
2673        Ok(())
2674    }
2675
2676    async fn show_mcp_status(show_processes: bool) -> Result<()> {
2677        tracing::info!("Checking MCP server status");
2678
2679        // Check configuration
2680        let config_path = get_mcp_config_path()?;
2681        if !config_path.exists() {
2682            tracing::error!("MCP not configured");
2683            return Ok(());
2684        }
2685
2686        let content = std::fs::read_to_string(&config_path)?;
2687        let config: McpConfig = serde_json::from_str(&content)?;
2688
2689        tracing::info!("Configuration loaded from: {:?}", config_path);
2690
2691        // Check server configuration
2692        if let Some(server) = config.servers.get("kindly-guard") {
2693            tracing::info!("KindlyGuard server configured:");
2694            tracing::info!("  Command: {}", server.command);
2695            tracing::info!("  Args: {:?}", server.args);
2696        } else {
2697            tracing::warn!("KindlyGuard server not configured");
2698        }
2699
2700        // Check running processes
2701        if show_processes {
2702            check_running_processes()?;
2703        }
2704
2705        Ok(())
2706    }
2707
2708    async fn start_mcp_server(daemon: bool) -> Result<()> {
2709        tracing::info!("Starting MCP server");
2710
2711        // Load configuration
2712        let config_path = get_mcp_config_path()?;
2713        let content = std::fs::read_to_string(&config_path)?;
2714        let config: McpConfig = serde_json::from_str(&content)?;
2715
2716        let server = config
2717            .servers
2718            .get("kindly-guard")
2719            .ok_or_else(|| anyhow::anyhow!("KindlyGuard server not configured"))?;
2720
2721        if daemon {
2722            // Start in background
2723            tracing::info!("Starting server in daemon mode");
2724
2725            let mut cmd = Command::new(&server.command);
2726            cmd.args(&server.args);
2727            cmd.stdin(Stdio::null());
2728            cmd.stdout(Stdio::null());
2729            cmd.stderr(Stdio::null());
2730
2731            for (key, value) in &server.env {
2732                cmd.env(key, value);
2733            }
2734
2735            cmd.spawn()?;
2736            tracing::info!("Server started in background");
2737        } else {
2738            // Start in foreground
2739            tracing::info!("Starting server in foreground mode");
2740            tracing::info!("Press Ctrl+C to stop");
2741
2742            let mut cmd = Command::new(&server.command);
2743            cmd.args(&server.args);
2744
2745            for (key, value) in &server.env {
2746                cmd.env(key, value);
2747            }
2748
2749            let status = cmd.status()?;
2750            if !status.success() {
2751                tracing::error!("Server exited with status: {:?}", status);
2752            }
2753        }
2754
2755        Ok(())
2756    }
2757
2758    async fn stop_mcp_server(force: bool) -> Result<()> {
2759        tracing::info!("Stopping MCP server");
2760
2761        // Find running processes
2762        let pids = find_kindlyguard_processes()?;
2763
2764        if pids.is_empty() {
2765            tracing::info!("No running KindlyGuard processes found");
2766            return Ok(());
2767        }
2768
2769        tracing::info!("Found {} running process(es)", pids.len());
2770
2771        for pid in pids {
2772            if force {
2773                Command::new("kill")
2774                    .arg("-9")
2775                    .arg(pid.to_string())
2776                    .status()?;
2777                tracing::info!("Force killed process {}", pid);
2778            } else {
2779                Command::new("kill")
2780                    .arg("-TERM")
2781                    .arg(pid.to_string())
2782                    .status()?;
2783                tracing::info!("Sent TERM signal to process {}", pid);
2784            }
2785        }
2786
2787        Ok(())
2788    }
2789
2790    async fn list_mcp_servers() -> Result<()> {
2791        let config_path = get_mcp_config_path()?;
2792
2793        if !config_path.exists() {
2794            tracing::warn!("No MCP configuration found at {:?}", config_path);
2795            return Ok(());
2796        }
2797
2798        let content = tokio::fs::read_to_string(&config_path).await?;
2799        let config: McpConfig = serde_json::from_str(&content)?;
2800
2801        tracing::info!("Installed MCP servers:");
2802        for (name, server) in config.servers.iter() {
2803            tracing::info!("  {} - {}", name, server.command);
2804        }
2805
2806        Ok(())
2807    }
2808
2809    async fn configure_mcp(file: Option<&Path>, show: bool) -> Result<()> {
2810        let config_path = file
2811            .map(PathBuf::from)
2812            .unwrap_or_else(|| get_mcp_config_path().unwrap());
2813
2814        if show {
2815            if !config_path.exists() {
2816                tracing::error!("Configuration file not found: {:?}", config_path);
2817                return Ok(());
2818            }
2819
2820            let content = std::fs::read_to_string(&config_path)?;
2821            println!("{}", content);
2822        } else if let Some(custom_file) = file {
2823            tracing::info!("Loading configuration from: {:?}", custom_file);
2824
2825            if !custom_file.exists() {
2826                return Err(anyhow::anyhow!("Configuration file not found"));
2827            }
2828
2829            // Validate configuration
2830            let content = std::fs::read_to_string(custom_file)?;
2831            let _: McpConfig = serde_json::from_str(&content)?;
2832
2833            // Copy to default location
2834            let default_path = get_mcp_config_path()?;
2835            std::fs::copy(custom_file, &default_path)?;
2836            tracing::info!("Configuration updated at: {:?}", default_path);
2837        } else {
2838            // Interactive configuration editor
2839            tracing::info!("Opening configuration editor...");
2840            let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
2841
2842            Command::new(&editor).arg(&config_path).status()?;
2843        }
2844
2845        Ok(())
2846    }
2847
2848    async fn test_mcp_server(server: &str) -> Result<()> {
2849        tracing::info!("Testing MCP server '{}'...", server);
2850
2851        let config_path = get_mcp_config_path()?;
2852        let content = std::fs::read_to_string(&config_path)?;
2853        let config: McpConfig = serde_json::from_str(&content)?;
2854
2855        if let Some(server_config) = config.servers.get(server) {
2856            test_mcp_protocol(&server_config.command, &server_config.args)?;
2857            tracing::info!("Test completed successfully");
2858        } else {
2859            tracing::error!("Server '{}' not found in configuration", server);
2860        }
2861
2862        Ok(())
2863    }
2864
2865    // Helper functions
2866
2867    fn get_mcp_config_path() -> Result<PathBuf> {
2868        let home = home_dir()?;
2869
2870        // Check for different possible locations
2871        let candidates = vec![home.join(".mcp.json"), home.join(".config/claude/mcp.json")];
2872
2873        for path in &candidates {
2874            if path.exists() {
2875                return Ok(path.clone());
2876            }
2877        }
2878
2879        // Default to .mcp.json
2880        Ok(home.join(".mcp.json"))
2881    }
2882
2883    fn find_project_root() -> Result<PathBuf> {
2884        let mut current = std::env::current_dir()?;
2885
2886        loop {
2887            if current.join("Cargo.toml").exists() && current.join("kindly-guard-server").exists() {
2888                return Ok(current);
2889            }
2890
2891            if let Some(parent) = current.parent() {
2892                current = parent.to_path_buf();
2893            } else {
2894                break;
2895            }
2896        }
2897
2898        // Try common locations
2899        let home = home_dir()?;
2900        let candidates = vec![
2901            home.join("kindly-guard"),
2902            PathBuf::from("/home/samuel/kindly-guard"),
2903        ];
2904
2905        for path in candidates {
2906            if path.join("Cargo.toml").exists() && path.join("kindly-guard-server").exists() {
2907                return Ok(path);
2908            }
2909        }
2910
2911        Err(anyhow::anyhow!("Could not find kindly-guard project root"))
2912    }
2913
2914    fn find_kindlyguard_server() -> Result<PathBuf> {
2915        let candidates = vec![
2916            PathBuf::from("target/release/kindly-guard-server"),
2917            PathBuf::from("target/debug/kindly-guard-server"),
2918            PathBuf::from("../kindly-guard-server/target/release/kindly-guard-server"),
2919            PathBuf::from("../kindly-guard-server/target/debug/kindly-guard-server"),
2920            PathBuf::from("/usr/local/bin/kindly-guard-server"),
2921            PathBuf::from("/usr/bin/kindly-guard-server"),
2922            home_dir()?.join(".cargo/bin/kindly-guard-server"),
2923        ];
2924
2925        for path in candidates {
2926            if path.exists() {
2927                return Ok(path.canonicalize()?);
2928            }
2929        }
2930
2931        // Try using 'which'
2932        if let Ok(output) = Command::new("which").arg("kindly-guard-server").output() {
2933            if output.status.success() {
2934                let path = String::from_utf8_lossy(&output.stdout);
2935                return Ok(PathBuf::from(path.trim()));
2936            }
2937        }
2938
2939        Err(anyhow::anyhow!(
2940            "KindlyGuard server not found. Build it with 'cargo build --release' in the kindly-guard directory"
2941        ))
2942    }
2943
2944    fn test_mcp_protocol(command: &str, args: &[String]) -> Result<()> {
2945        let init_request = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"kindly-tools","version":"0.1.0"}}}"#;
2946
2947        let mut child = Command::new(command)
2948            .args(args)
2949            .stdin(Stdio::piped())
2950            .stdout(Stdio::piped())
2951            .stderr(Stdio::null())
2952            .spawn()?;
2953
2954        if let Some(stdin) = child.stdin.as_mut() {
2955            stdin.write_all(init_request.as_bytes())?;
2956            stdin.write_all(b"\n")?;
2957            stdin.flush()?;
2958        }
2959
2960        // Wait briefly for response
2961        std::thread::sleep(std::time::Duration::from_millis(500));
2962
2963        let output = child.wait_with_output()?;
2964        let response = String::from_utf8_lossy(&output.stdout);
2965
2966        if response.contains("jsonrpc") && response.contains("result") {
2967            tracing::info!("MCP protocol test successful");
2968        } else if response.contains("error") {
2969            tracing::error!("MCP protocol test failed");
2970            return Err(anyhow::anyhow!("MCP protocol error"));
2971        } else {
2972            tracing::warn!("Unexpected MCP protocol response");
2973        }
2974
2975        Ok(())
2976    }
2977
2978    fn check_running_processes() -> Result<()> {
2979        tracing::info!("Checking for running processes...");
2980
2981        let output = Command::new("ps").args(["aux"]).output()?;
2982
2983        let output_str = String::from_utf8_lossy(&output.stdout);
2984        let mut found_any = false;
2985
2986        for line in output_str.lines() {
2987            if line.contains("kindly-guard-server") && !line.contains("grep") {
2988                println!("{}", line);
2989                found_any = true;
2990            }
2991        }
2992
2993        if !found_any {
2994            tracing::info!("No running KindlyGuard processes found");
2995        }
2996
2997        Ok(())
2998    }
2999
3000    fn find_kindlyguard_processes() -> Result<Vec<u32>> {
3001        let output = Command::new("pgrep")
3002            .arg("-f")
3003            .arg("kindly-guard-server")
3004            .output()?;
3005
3006        if !output.status.success() {
3007            return Ok(vec![]);
3008        }
3009
3010        let output_str = String::from_utf8_lossy(&output.stdout);
3011        let pids: Vec<u32> = output_str
3012            .lines()
3013            .filter_map(|line| line.trim().parse().ok())
3014            .collect();
3015
3016        Ok(pids)
3017    }
3018} // End of mcp module
3019
3020/// Wrap command module for protecting AI CLIs
3021pub mod wrap {
3022    use super::*;
3023    use clap::Args;
3024
3025    /// Wrap any AI CLI command with KindlyGuard protection
3026    #[derive(Debug, Args)]
3027    pub struct WrapCommand {
3028        /// The command to wrap and protect
3029        #[arg(trailing_var_arg = true, required = true)]
3030        pub command: Vec<String>,
3031
3032        /// KindlyGuard server URL
3033        #[arg(short, long, default_value = "http://localhost:8080")]
3034        pub server: String,
3035
3036        /// Block on threat detection instead of warning
3037        #[arg(short, long)]
3038        pub block: bool,
3039    }
3040
3041    impl Execute for WrapCommand {
3042        async fn execute(&self) -> Result<()> {
3043            crate::commands::wrap::wrap_command(
3044                self.command.clone(),
3045                self.server.clone(),
3046                self.block,
3047            )
3048            .await
3049        }
3050    }
3051}
3052
3053pub mod monitor {
3054    use super::*;
3055    use clap::Args;
3056
3057    /// Monitor KindlyGuard server status in real-time
3058    #[derive(Debug, Args)]
3059    pub struct MonitorCommand {
3060        /// Server URL to monitor
3061        #[arg(short, long, default_value = "http://localhost:8080")]
3062        pub url: String,
3063
3064        /// Update interval in seconds
3065        #[arg(short, long, default_value = "5")]
3066        pub interval: u64,
3067    }
3068
3069    impl Execute for MonitorCommand {
3070        async fn execute(&self) -> Result<()> {
3071            crate::commands::monitor::run(self.url.clone(), self.interval).await
3072        }
3073    }
3074}
3075
3076pub mod shield {
3077    use super::*;
3078
3079    pub use crate::commands::shield::ShieldCommand;
3080
3081    impl Execute for ShieldCommand {
3082        async fn execute(&self) -> Result<()> {
3083            // Clone self to call the run method
3084            let cmd = self.clone();
3085            cmd.run().await
3086        }
3087    }
3088}
3089
3090pub mod utils {
3091    use super::*;
3092    use std::process::Command;
3093
3094    /// Run a command and return its output
3095    pub fn run_command(cmd: &str, args: &[&str]) -> Result<String> {
3096        let output = Command::new(cmd).args(args).output()?;
3097
3098        if !output.status.success() {
3099            anyhow::bail!("Command failed: {} {}", cmd, args.join(" "));
3100        }
3101
3102        Ok(String::from_utf8(output.stdout)?)
3103    }
3104
3105    /// Check if running in CI environment
3106    pub fn is_ci() -> bool {
3107        std::env::var("CI").is_ok()
3108    }
3109
3110    /// Get the current git branch
3111    pub fn current_git_branch() -> Result<String> {
3112        run_command("git", &["rev-parse", "--abbrev-ref", "HEAD"]).map(|s| s.trim().to_string())
3113    }
3114}