vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use serde_json::Value;
use vtcode_core::config::constants::tools;

pub(super) const TEXTUAL_TOOL_PREFIXES: &[&str] = &["default_api."];
pub(super) const DIRECT_FUNCTION_ALIASES: &[&str] = &[
    "run",
    "run_cmd",
    "runcommand",
    "terminalrun",
    "terminal_cmd",
    "terminalcommand",
];

#[derive(Clone, Copy)]
pub(super) struct UnifiedExecDefaults {
    pub(super) action: &'static str,
    pub(super) force_tty: Option<bool>,
}

pub(super) fn canonicalize_tool_name(raw: &str) -> Option<String> {
    let normalized = canonicalize_normalized_name(raw)?;
    if normalized.is_empty() {
        None
    } else if unified_exec_defaults_from_normalized_name(&normalized).is_some() {
        Some(tools::UNIFIED_EXEC.to_string())
    } else {
        Some(normalized)
    }
}

pub(super) fn canonicalize_tool_result(name: String, mut args: Value) -> Option<(String, Value)> {
    let normalized = canonicalize_normalized_name(&name)?;
    let canonical = canonicalize_tool_name(&name)?;
    if canonical == tools::UNIFIED_EXEC
        && let Some(defaults) = unified_exec_defaults_from_normalized_name(&normalized)
        && let Some(payload) = args.as_object_mut()
    {
        apply_unified_exec_defaults(payload, defaults);
    }
    if is_known_textual_tool(&canonical) {
        Some((canonical, args))
    } else {
        None
    }
}

pub(super) fn is_known_textual_tool(name: &str) -> bool {
    matches!(
        name,
        tools::WRITE_FILE
            | tools::EDIT_FILE
            | tools::READ_FILE
            | tools::UNIFIED_FILE
            | tools::UNIFIED_EXEC
            | tools::UNIFIED_SEARCH
            | tools::GREP_FILE
            | tools::LIST_FILES
            | tools::APPLY_PATCH
            | tools::RESIZE_PTY_SESSION
    )
}

fn canonicalize_normalized_name(raw: &str) -> Option<String> {
    let trimmed = raw.trim();
    if trimmed.is_empty() {
        return None;
    }

    let trimmed = trimmed.trim_matches(|ch| matches!(ch, '"' | '\'' | '`'));

    let mut normalized = String::with_capacity(trimmed.len());
    let mut last_was_separator = false;
    for ch in trimmed.chars() {
        if ch.is_ascii_alphanumeric() {
            normalized.push(ch.to_ascii_lowercase());
            last_was_separator = false;
        } else if ch == '_' {
            normalized.push('_');
            last_was_separator = false;
        } else if matches!(ch, ' ' | '\t' | '\n' | '-' | ':' | '.')
            && !last_was_separator
            && !normalized.is_empty()
        {
            normalized.push('_');
            last_was_separator = true;
        }
    }

    let normalized = normalized.trim_matches('_').to_string();
    if normalized.is_empty() {
        None
    } else {
        Some(normalized)
    }
}

pub(super) fn unified_exec_defaults_for_name(raw: &str) -> Option<UnifiedExecDefaults> {
    canonicalize_normalized_name(raw)
        .and_then(|normalized| unified_exec_defaults_from_normalized_name(normalized.as_str()))
}

pub(super) fn apply_unified_exec_defaults(
    payload: &mut serde_json::Map<String, Value>,
    defaults: UnifiedExecDefaults,
) {
    payload
        .entry("action".to_string())
        .or_insert_with(|| Value::String(defaults.action.to_string()));
    if let Some(tty) = defaults.force_tty {
        payload
            .entry("tty".to_string())
            .or_insert_with(|| Value::Bool(tty));
    }
}

fn unified_exec_defaults_from_normalized_name(normalized: &str) -> Option<UnifiedExecDefaults> {
    match normalized {
        tools::UNIFIED_EXEC => Some(UnifiedExecDefaults {
            action: "run",
            force_tty: None,
        }),
        "run" | "runcmd" | "runcommand" | "terminalrun" | "terminalcmd" | "terminalcommand"
        | "command" | "shell" | "bash" | "container_exec" | "exec" | "exec_command" => {
            Some(UnifiedExecDefaults {
                action: "run",
                force_tty: None,
            })
        }
        "run_pty_cmd" | "create_pty_session" => Some(UnifiedExecDefaults {
            action: "run",
            force_tty: Some(true),
        }),
        "send_pty_input" => Some(UnifiedExecDefaults {
            action: "write",
            force_tty: None,
        }),
        "read_pty_session" => Some(UnifiedExecDefaults {
            action: "poll",
            force_tty: None,
        }),
        "list_pty_sessions" => Some(UnifiedExecDefaults {
            action: "list",
            force_tty: None,
        }),
        "close_pty_session" => Some(UnifiedExecDefaults {
            action: "close",
            force_tty: None,
        }),
        _ => None,
    }
}