vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};

use anyhow::{Context, Result, anyhow, bail};
use vtcode_core::config::VTCodeConfig;
use vtcode_core::config::models::ModelId;
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
use vtcode_core::core::agent::runner::{AgentRunner, RunnerSettings};
use vtcode_core::core::agent::task::{ContextItem, Task};
use vtcode_core::core::agent::types::AgentType;
use vtcode_core::utils::colors::style;

use crate::startup::require_full_auto_workspace_trust;

const AUTO_SESSION_PREFIX: &str = "auto-task";
const AUTO_TASK_ID: &str = "auto-task";
const AUTO_TASK_TITLE: &str = "Autonomous Task";

pub async fn handle_auto_task_command(
    config: &CoreAgentConfig,
    vt_cfg: &VTCodeConfig,
    prompt: &str,
) -> Result<()> {
    let trimmed = prompt.trim();
    if trimmed.is_empty() {
        bail!("Automation prompt is empty. Provide instructions after --auto/--full-auto.");
    }

    require_full_auto_workspace_trust(&config.workspace, "autonomous runs", "--auto/--full-auto")
        .await?;

    let automation_cfg = &vt_cfg.automation.full_auto;
    if !automation_cfg.enabled {
        bail!(
            "Automation is disabled in configuration. Enable [automation.full_auto] to continue."
        );
    }

    if config
        .provider
        .eq_ignore_ascii_case(crate::codex_app_server::CODEX_PROVIDER)
    {
        let completed = crate::codex_app_server::run_codex_noninteractive(
            config,
            Some(vt_cfg),
            crate::codex_app_server::CodexNonInteractiveRun {
                prompt: trimmed.to_string(),
                read_only: false,
                plan_mode: false,
                skip_confirmations: true,
                ephemeral: true,
                resume_thread_id: None,
                seed_messages: Vec::new(),
                review_target: None,
            },
        )
        .await?;
        if !completed.output.trim().is_empty() {
            println!("{}", completed.output.trim());
        }
        return Ok(());
    }

    let model_id = ModelId::from_str(&config.model).with_context(|| {
        format!(
            "Model '{}' is not recognized for autonomous execution. Update vtcode.toml to a \
             supported identifier.",
            config.model
        )
    })?;

    let session_id = format!(
        "{AUTO_SESSION_PREFIX}-{}",
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map_err(|err| anyhow!("Failed to derive session identifier timestamp: {}", err))?
            .as_secs()
    );

    let mut runner = AgentRunner::new_with_openai_auth(
        AgentType::Single,
        model_id,
        config.api_key.clone(),
        config.workspace.clone(),
        session_id,
        RunnerSettings {
            reasoning_effort: Some(config.reasoning_effort),
            verbosity: None,
        },
        None,
        config.openai_chatgpt_auth.clone(),
    )
    .await?;

    runner.enable_full_auto(&automation_cfg.allowed_tools).await;

    let task = Task {
        id: AUTO_TASK_ID.to_string(),
        title: AUTO_TASK_TITLE.to_string(),
        description: trimmed.to_string(),
        instructions: None,
    };

    let max_retries = vt_cfg.agent.max_task_retries;
    let result = runner
        .execute_task_with_retry(&task, &[] as &[ContextItem], max_retries)
        .await
        .context("Failed to execute autonomous task after retries")?;

    if !result.summary.trim().is_empty() {
        println!(
            "{} {}",
            style("[SUMMARY]").green().bold(),
            result.summary.trim()
        );
    }

    if !result.modified_files.is_empty() {
        println!(
            "{} {}",
            style("[FILES]").cyan().bold(),
            result.modified_files.join(", ")
        );
    }

    if !result.executed_commands.is_empty() {
        println!(
            "{} {}",
            style("[COMMANDS]").cyan().bold(),
            result.executed_commands.join(", ")
        );
    }

    if !result.warnings.is_empty() {
        for warning in result.warnings {
            println!("{} {}", style("[WARNING]").red().bold(), warning);
        }
    }

    Ok(())
}