vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use std::io::{self, Write};
use std::path::Path;

use anstyle::Ansi256Color;
use anyhow::{Context, Result, bail};
use vtcode_commons::color256_theme::rgb_to_ansi256_for_theme;
use vtcode_core::utils::dot_config::{
    WorkspaceTrustLevel, load_workspace_trust_level, update_workspace_trust,
};
use vtcode_core::utils::style_helpers::{ColorPalette, render_styled};
use vtcode_core::utils::{ansi_capabilities, ansi_capabilities::ColorScheme};

const WARNING_RGB: (u8, u8, u8) = (166, 51, 51);
const INFO_RGB: (u8, u8, u8) = (217, 154, 78);
const CHOICE_PROMPT: &str = "Enter your choice (a/q): ";
const INVALID_CHOICE_MESSAGE: &str = "Invalid selection. Please enter 'a' or 'q'.";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TrustSelection {
    FullAuto,
    Quit,
}

pub(crate) async fn ensure_full_auto_workspace_trust(workspace: &Path) -> Result<bool> {
    let current_level = load_workspace_trust_level(workspace)
        .await
        .context("Failed to determine workspace trust level for benchmark command")?;

    if current_level == Some(WorkspaceTrustLevel::FullAuto) {
        return Ok(true);
    }

    let require_full_auto_upgrade = current_level.is_some();
    let palette = ColorPalette::default();
    render_prompt(workspace, require_full_auto_upgrade);

    match read_user_selection()? {
        TrustSelection::FullAuto => {
            update_workspace_trust(workspace, WorkspaceTrustLevel::FullAuto)
                .await
                .context("Failed to persist full-auto workspace trust")?;
            let msg = "Workspace marked as trusted with full auto capabilities.";
            println!("{}", render_styled(msg, palette.success, None));
            Ok(true)
        }
        TrustSelection::Quit => {
            let msg = "Workspace not trusted. Exiting benchmark command.";
            println!("{}", render_styled(msg, palette.warning, None));
            Ok(false)
        }
    }
}

pub(crate) async fn require_full_auto_workspace_trust(
    workspace: &Path,
    denied_action: &str,
    command_name: &str,
) -> Result<()> {
    let trust_level = load_workspace_trust_level(workspace)
        .await
        .context("Failed to determine workspace trust level")?;

    match trust_level {
        Some(WorkspaceTrustLevel::FullAuto) => Ok(()),
        Some(level) => bail!(
            "Workspace trust level '{level}' does not permit {denied_action}. Upgrade trust to full auto."
        ),
        None => bail!(
            "Workspace is not trusted. Start VT Code interactively once and mark it as full auto before using {command_name}."
        ),
    }
}

fn render_prompt(workspace: &Path, require_full_auto_upgrade: bool) {
    println!();
    print_prompt_line("[WARN] Workspace Trust Required", PromptTone::Heading);
    println!();
    print_prompt_line(
        "VT Code can execute code and access files in your workspace.",
        PromptTone::Body,
    );
    print_prompt_line(
        "Trusting this workspace also trusts all MCP servers configured here.",
        PromptTone::Body,
    );
    println!();
    print_prompt_line(
        "VT Code benchmark execution requires full auto workspace trust.",
        PromptTone::Body,
    );
    println!();

    let workspace_display = workspace.display();
    print_prompt_line(
        &format!("Workspace: {}", workspace_display),
        PromptTone::Body,
    );
    println!();

    let requirement_line = if require_full_auto_upgrade {
        "Full-auto mode requires upgrading this workspace to full auto."
    } else {
        "Trust this workspace with full auto capabilities to continue:"
    };
    print_prompt_line(requirement_line, PromptTone::Body);
    println!();

    print_prompt_line("  [a] Trust with full auto capabilities", PromptTone::Body);
    print_prompt_line("  [q] Quit without trusting", PromptTone::Body);
    println!();
}

fn read_user_selection() -> Result<TrustSelection> {
    loop {
        print_prompt(CHOICE_PROMPT, PromptTone::Heading)?;
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .context("Failed to read workspace trust selection")?;

        if let Some(selection) = parse_trust_selection(input.trim()) {
            return Ok(selection);
        }

        print_prompt_line(INVALID_CHOICE_MESSAGE, PromptTone::Heading);
    }
}

fn parse_trust_selection(input: &str) -> Option<TrustSelection> {
    if input.eq_ignore_ascii_case("a") {
        Some(TrustSelection::FullAuto)
    } else if input.eq_ignore_ascii_case("q") {
        Some(TrustSelection::Quit)
    } else {
        None
    }
}

enum PromptTone {
    Heading,
    Body,
}

fn print_prompt(message: &str, tone: PromptTone) -> Result<()> {
    print!("{}", styled_prompt_message(message, tone));
    io::stdout()
        .flush()
        .context("Failed to flush workspace trust prompt")
}

fn print_prompt_line(message: &str, tone: PromptTone) {
    println!("{}", styled_prompt_message(message, tone));
}

fn styled_prompt_message(message: &str, tone: PromptTone) -> String {
    use anstyle::Color;

    let is_light_theme = matches!(ansi_capabilities::detect_color_scheme(), ColorScheme::Light);
    let (rgb, is_heading) = match tone {
        PromptTone::Heading => (WARNING_RGB, true),
        PromptTone::Body => (INFO_RGB, false),
    };

    let color = Color::Ansi256(Ansi256Color(rgb_to_ansi256_for_theme(
        rgb.0,
        rgb.1,
        rgb.2,
        is_light_theme,
    )));
    if is_heading {
        render_styled(message, color, Some("bold".to_string()))
    } else {
        render_styled(message, color, None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_trust_selection_accepts_expected_choices() {
        assert_eq!(parse_trust_selection("a"), Some(TrustSelection::FullAuto));
        assert_eq!(parse_trust_selection("q"), Some(TrustSelection::Quit));
    }

    #[test]
    fn parse_trust_selection_rejects_unknown_choices() {
        assert_eq!(parse_trust_selection(""), None);
        assert_eq!(parse_trust_selection("w"), None);
    }
}