vtcode 0.136.4

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use anyhow::Result;
use crossterm::style::Stylize;
use std::env;
use vtcode_core::utils::ansi::MessageStyle;

use super::{SlashCommandContext, SlashCommandControl};
use crate::updater::{InlineUpdateOutcome, Updater, execute_inline_update, run_inline_update_prompt};

fn control_for_update_outcome(outcome: InlineUpdateOutcome) -> SlashCommandControl {
    match outcome {
        InlineUpdateOutcome::Continue => SlashCommandControl::Continue,
        InlineUpdateOutcome::RestartRequested => {
            SlashCommandControl::BreakWithReason(vtcode_core::hooks::SessionEndReason::Completed)
        }
    }
}

pub(crate) async fn handle_update(
    ctx: SlashCommandContext<'_>,
    check_only: bool,
    install: bool,
    force: bool,
) -> Result<SlashCommandControl> {
    let current_version = env!("CARGO_PKG_VERSION");
    let mut updater = Updater::new(current_version)?;

    ctx.renderer
        .line(MessageStyle::Info, &format!("Checking for updates (current: v{current_version})..."))?;

    match updater.check_for_updates().await {
        Ok(Some(update)) => {
            ctx.renderer
                .line(MessageStyle::Info, &format!("New version available: v{}", update.version))?;

            if !update.release_notes.trim().is_empty() {
                ctx.renderer.line(MessageStyle::Info, "Release notes:")?;
                for line in update.release_notes.lines().take(8) {
                    ctx.renderer.line(MessageStyle::Output, line)?;
                }
            }

            if check_only {
                ctx.renderer
                    .line(MessageStyle::Info, &format!("Run {} to apply this release.", "/update install".green()))?;
                return Ok(SlashCommandControl::Continue);
            }

            let notice = updater.notice_for_version(update.version.clone());
            run_inline_update_prompt(
                ctx.renderer,
                ctx.handle,
                ctx.session,
                ctx.ctrl_c_state,
                ctx.ctrl_c_notify,
                ctx.config.workspace.as_path(),
                &notice,
            )
            .await
            .map(control_for_update_outcome)
        }
        Ok(None) => {
            ctx.renderer.line(MessageStyle::Info, "Already on the latest version.")?;
            // Clear any stale "Update" highlight from the header so the UI
            // does not appear stuck on "Checking for updates...".
            ctx.header_context.highlights.retain(|h| h.title != "Update");
            ctx.handle.set_header_context(ctx.header_context.clone());

            if install && force {
                ctx.renderer
                    .line(MessageStyle::Info, "Force reinstall requested; running the current install command.")?;
                let notice = updater.notice_for_version(updater.current_version().clone());
                execute_inline_update(ctx.renderer, ctx.handle, ctx.config.workspace.as_path(), &notice)
                    .await
                    .map(control_for_update_outcome)
            } else {
                Ok(SlashCommandControl::Continue)
            }
        }
        Err(err) => {
            ctx.renderer
                .line(MessageStyle::Error, &format!("Failed to check updates: {err}"))?;
            ctx.header_context.highlights.retain(|h| h.title != "Update");
            ctx.handle.set_header_context(ctx.header_context.clone());
            Ok(SlashCommandControl::Continue)
        }
    }
}