vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use anyhow::Result;
use vtcode_core::config::types::ReasoningEffortLevel;
use vtcode_core::utils::ansi::{AnsiRenderer, MessageStyle};
use vtcode_tui::ui::interactive_list::SelectionEntry;

use super::common::{prompt_with_placeholder, run_selection};

pub(crate) fn prompt_reasoning_effort(
    renderer: &mut AnsiRenderer,
    default: ReasoningEffortLevel,
) -> Result<ReasoningEffortLevel> {
    renderer.line(
        MessageStyle::Status,
        "Choose reasoning effort level for models that support it:",
    )?;

    let levels = [
        (
            ReasoningEffortLevel::None,
            "None – lowest latency, good default for GPT-5.4",
        ),
        (
            ReasoningEffortLevel::Low,
            "Low – faster responses, less reasoning",
        ),
        (
            ReasoningEffortLevel::Medium,
            "Medium – balanced reasoning for harder multi-step work",
        ),
        (
            ReasoningEffortLevel::High,
            "High – deeper reasoning, slower responses",
        ),
        (
            ReasoningEffortLevel::XHigh,
            "Extra High – best default for advanced coding and agentic work",
        ),
        (
            ReasoningEffortLevel::Max,
            "Max – highest adaptive effort for supported Anthropic models, highest latency and token use",
        ),
    ];

    match select_reasoning_with_ratatui(&levels, default) {
        Ok(level) => Ok(level),
        Err(error) => {
            renderer.line(
                MessageStyle::Info,
                &format!("Falling back to manual input ({error})."),
            )?;
            prompt_reasoning_effort_text(renderer, &levels, default)
        }
    }
}

fn reasoning_entries(levels: &[(ReasoningEffortLevel, &str)]) -> Vec<SelectionEntry> {
    levels
        .iter()
        .map(|(_level, label)| SelectionEntry::new((*label).to_owned(), None))
        .collect()
}

fn select_reasoning_with_ratatui(
    levels: &[(ReasoningEffortLevel, &str)],
    default: ReasoningEffortLevel,
) -> Result<ReasoningEffortLevel> {
    let entries = reasoning_entries(levels);

    let default_index = levels
        .iter()
        .position(|(level, _)| *level == default)
        .unwrap_or(1);

    let instructions = format!(
        "Default: {}. Use ↑/↓ or j/k to choose, Enter to confirm, Esc to keep the default.",
        default.as_str()
    );
    let selected_index = run_selection("Reasoning Effort", &instructions, &entries, default_index)?;
    Ok(levels[selected_index].0)
}

fn prompt_reasoning_effort_text(
    renderer: &mut AnsiRenderer,
    levels: &[(ReasoningEffortLevel, &str)],
    default: ReasoningEffortLevel,
) -> Result<ReasoningEffortLevel> {
    for (index, (_level, label)) in levels.iter().enumerate() {
        renderer.line(MessageStyle::Info, &format!("  {}) {}", index + 1, label))?;
    }

    loop {
        let input = prompt_with_placeholder(&format!("Reasoning effort [{}]", default.as_str()))?;
        let trimmed = input.trim();
        if trimmed.is_empty() {
            return Ok(default);
        }

        if let Ok(index) = trimmed.parse::<usize>()
            && let Some((level, _)) = levels.get(index - 1)
        {
            return Ok(*level);
        }

        if let Some(level) = ReasoningEffortLevel::parse(trimmed) {
            return Ok(level);
        }

        renderer.line(
            MessageStyle::Error,
            "Please choose a valid reasoning effort level (none, low, medium, high, xhigh, max).",
        )?;
    }
}

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

    #[test]
    fn reasoning_entries_keep_labels_without_ordinals() {
        let entries = reasoning_entries(&[
            (
                ReasoningEffortLevel::None,
                "None – lowest latency, good default for GPT-5.4",
            ),
            (
                ReasoningEffortLevel::Low,
                "Low – faster responses, less reasoning",
            ),
        ]);

        assert_eq!(
            entries[0].title,
            "None – lowest latency, good default for GPT-5.4"
        );
        assert_eq!(entries[1].title, "Low – faster responses, less reasoning");
    }
}