vtcode 0.98.7

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use anyhow::Result;
use crossterm::style::Stylize;
use vtcode_core::cli::args::{DependenciesSubcommand, ManagedDependency};
use vtcode_core::tools::ast_grep_binary::AST_GREP_INSTALL_COMMAND;
use vtcode_core::tools::ripgrep_binary::RIPGREP_INSTALL_COMMAND;
use vtcode_core::tools::{AstGrepStatus, RipgrepStatus};

const SEARCH_TOOLS_INSTALL_COMMAND: &str = "vtcode dependencies install search-tools";

pub async fn handle_dependencies_command(command: DependenciesSubcommand) -> Result<()> {
    match command {
        DependenciesSubcommand::Install { dependency } => handle_install(dependency).await,
        DependenciesSubcommand::Status { dependency } => handle_status(dependency),
    }
}

async fn handle_install(dependency: ManagedDependency) -> Result<()> {
    match dependency {
        ManagedDependency::SearchTools => {
            println!("{} Installing the optional search tools bundle", "".cyan());
            install_ripgrep()?;
            install_ast_grep().await?;
            Ok(())
        }
        ManagedDependency::Ripgrep => {
            install_ripgrep()?;
            Ok(())
        }
        ManagedDependency::AstGrep => {
            install_ast_grep().await?;
            Ok(())
        }
    }
}

fn handle_status(dependency: ManagedDependency) -> Result<()> {
    match dependency {
        ManagedDependency::SearchTools => {
            report_ripgrep_status();
            report_ast_grep_status();
            Ok(())
        }
        ManagedDependency::Ripgrep => {
            report_ripgrep_status();
            Ok(())
        }
        ManagedDependency::AstGrep => {
            report_ast_grep_status();
            Ok(())
        }
    }
}

fn print_ast_grep_next_steps() {
    println!(
        "{} For a local repository, run `vtcode init` to materialize `sgconfig.yml`, `rules/`, and `rule-tests/`.",
        "".cyan()
    );
    println!("{} Then run `vtcode check ast-grep`.", "".cyan());
}

fn install_ripgrep() -> Result<()> {
    match RipgrepStatus::check() {
        RipgrepStatus::Available { version } => {
            println!(
                "{} ripgrep already available: {}",
                "".green(),
                version.green()
            );
        }
        RipgrepStatus::NotFound => {
            println!(
                "{} Installing ripgrep using a supported system installer",
                "".cyan()
            );
            RipgrepStatus::install()?;
            report_ripgrep_status();
        }
        RipgrepStatus::Error { reason } => {
            println!(
                "{} ripgrep was found but could not be verified: {}",
                "!".yellow(),
                reason
            );
            println!(
                "{} Retrying installation via `{}`",
                "".cyan(),
                RIPGREP_INSTALL_COMMAND
            );
            RipgrepStatus::install()?;
            report_ripgrep_status();
        }
    }

    Ok(())
}

async fn install_ast_grep() -> Result<()> {
    match AstGrepStatus::check() {
        AstGrepStatus::Available {
            version,
            binary,
            managed,
        } => {
            println!(
                "{} ast-grep already available: {}",
                "".green(),
                version.green()
            );
            println!("{} Binary: {}", "".cyan(), binary.display());
            println!(
                "{} Source: {}",
                "".cyan(),
                if managed {
                    "VT Code-managed"
                } else {
                    "system PATH or override"
                }
            );
            print_ast_grep_next_steps();
        }
        AstGrepStatus::NotFound | AstGrepStatus::Error { .. } => {
            println!(
                "{} Installing ast-grep into {}",
                "".cyan(),
                vtcode_core::tools::ast_grep_binary::managed_ast_grep_bin_dir().display()
            );

            let outcome = AstGrepStatus::install().await?;
            println!(
                "{} Installed {} at {}",
                "".green(),
                outcome.version.green(),
                outcome.binary_path.display()
            );

            if let Some(alias_path) = outcome.alias_path {
                println!(
                    "{} Installed compatibility alias at {}",
                    "".cyan(),
                    alias_path.display()
                );
            }

            if let Some(warning) = outcome.warning {
                println!("{} {}", "".yellow(), warning);
            }

            println!(
                "{} Add this to your shell if you want ast-grep outside VT Code:",
                "".cyan()
            );
            println!(
                "  export PATH=\"{}:$PATH\"",
                outcome.managed_bin_dir.display()
            );
            print_ast_grep_next_steps();
        }
    }

    Ok(())
}

fn report_ripgrep_status() {
    match RipgrepStatus::check() {
        RipgrepStatus::Available { version } => {
            println!("{} ripgrep available: {}", "".green(), version.green());
            println!("{} Source: system PATH", "".cyan());
        }
        RipgrepStatus::NotFound => {
            println!(
                "{} ripgrep is not installed. Run `{}` or `{}`.",
                "".red(),
                SEARCH_TOOLS_INSTALL_COMMAND,
                RIPGREP_INSTALL_COMMAND
            );
        }
        RipgrepStatus::Error { reason } => {
            println!("{} ripgrep check failed: {}", "".red(), reason);
        }
    }
}

fn report_ast_grep_status() {
    match AstGrepStatus::check() {
        AstGrepStatus::Available {
            version,
            binary,
            managed,
        } => {
            println!("{} ast-grep available: {}", "".green(), version.green());
            println!("{} Binary: {}", "".cyan(), binary.display());
            println!(
                "{} Source: {}",
                "".cyan(),
                if managed {
                    "VT Code-managed"
                } else {
                    "system PATH or override"
                }
            );
        }
        AstGrepStatus::NotFound => {
            println!(
                "{} ast-grep is not installed. Run `{}` or `{}`.",
                "".red(),
                SEARCH_TOOLS_INSTALL_COMMAND,
                AST_GREP_INSTALL_COMMAND
            );
        }
        AstGrepStatus::Error { reason } => {
            println!("{} ast-grep check failed: {}", "".red(), reason);
        }
    }
}