vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use super::*;

fn test_context() -> SystemPromptContext {
    SystemPromptContext {
        full_auto: false,
        auto_mode: false,
        plan_mode: false,
        discovered_skills: Vec::new(),
        active_instruction_directory: None,
        instruction_context_paths: Vec::new(),
    }
}

#[tokio::test]
async fn test_incremental_prompt_caching() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let base_prompt = "Test system prompt";
    let context = test_context();

    let prompt1 = prompt_builder
        .get_system_prompt(base_prompt, 1, context.hash(), &context, None)
        .await;
    let prompt2 = prompt_builder
        .get_system_prompt(base_prompt, 1, context.hash(), &context, None)
        .await;

    assert_eq!(prompt1, prompt2);
    assert!(prompt1.contains("Test system prompt"));
    assert!(!prompt1.contains("[Context]"));
    assert!(!prompt1.contains("[Runtime Context]"));

    let (is_cached, size) = prompt_builder.cache_stats().await;
    assert!(is_cached);
    assert!(size >= base_prompt.len());
}

#[tokio::test]
async fn test_base_prompt_hash_is_stable() {
    assert_eq!(
        hash_base_system_prompt("Test"),
        hash_base_system_prompt("Test")
    );
}

#[tokio::test]
async fn test_instruction_appendix_uses_explicit_directory() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let workspace = tempfile::TempDir::new().expect("workspace");
    std::fs::write(workspace.path().join(".git"), "gitdir: /tmp/git").expect("write git");
    std::fs::write(workspace.path().join("AGENTS.md"), "root rule").expect("write root");
    let nested = workspace.path().join("nested/sub");
    std::fs::create_dir_all(&nested).expect("create nested");
    std::fs::write(nested.join("AGENTS.md"), "nested rule").expect("write nested");

    let config = vtcode_config::core::AgentConfig {
        user_instructions: Some("be brief".to_string()),
        instruction_max_bytes: 4096,
        include_temporal_context: true,
        temporal_context_use_utc: true,
        ..Default::default()
    };

    let context = SystemPromptContext {
        active_instruction_directory: Some(nested.clone()),
        instruction_context_paths: vec![nested.join("file.rs")],
        ..test_context()
    };

    let prompt = prompt_builder
        .get_system_prompt(
            "Stable base prompt",
            1,
            context.hash(),
            &context,
            Some(&config),
        )
        .await;

    assert!(prompt.contains("be brief"));
    assert!(prompt.contains("### Instruction map"));
    assert!(prompt.contains("AGENTS.md (workspace AGENTS)"));
    assert!(prompt.contains("nested/sub/AGENTS.md (workspace AGENTS)"));
    assert!(prompt.contains("root rule"));
    assert!(prompt.contains("nested rule"));
    assert!(!prompt.contains("[Runtime Context]"));
    assert!(!prompt.contains("Time (UTC):"));
}

#[tokio::test]
async fn test_prompt_omits_runtime_context_sections() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let agent_config = vtcode_config::core::AgentConfig {
        include_temporal_context: true,
        temporal_context_use_utc: true,
        ..Default::default()
    };
    let context = test_context();
    let prompt = prompt_builder
        .get_system_prompt(
            "You are a helpful assistant.",
            1,
            context.hash(),
            &context,
            Some(&agent_config),
        )
        .await;

    assert!(!prompt.contains("[Context]"));
    assert!(!prompt.contains("[Runtime Context]"));
    assert!(!prompt.contains("Retry #"));
    assert!(!prompt.contains("task_tracker"));
    assert!(!prompt.contains("Time (UTC):"));
    assert!(!prompt.contains("<budget:token_budget>"));
    assert!(!prompt.contains("<system_warning>"));
    assert!(!prompt.contains("token_usage:"));
}

#[tokio::test]
async fn test_plan_mode_notice_appended() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let context = SystemPromptContext {
        plan_mode: true,
        ..test_context()
    };

    let prompt = prompt_builder
        .get_system_prompt(
            "You are a helpful assistant.",
            1,
            context.hash(),
            &context,
            None,
        )
        .await;

    assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_READ_ONLY_HEADER));
    assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_EXIT_INSTRUCTION_LINE));
    assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_PLAN_QUALITY_LINE));
    assert!(prompt.contains("<proposed_plan>"));
    assert!(prompt.contains("Next open decision"));
    assert!(!prompt.contains("Scope checkpoint"));
    assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_NO_AUTO_EXIT_LINE));
    assert!(prompt.contains(vtcode_core::prompts::system::PLAN_MODE_TASK_TRACKER_LINE));
    assert!(!prompt.contains("[Context]"));
}

#[tokio::test]
async fn test_full_auto_is_constrained_in_plan_mode() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let context = SystemPromptContext {
        full_auto: true,
        plan_mode: true,
        ..test_context()
    };

    let prompt = prompt_builder
        .get_system_prompt(
            "You are a helpful assistant.",
            1,
            context.hash(),
            &context,
            None,
        )
        .await;

    assert!(
        prompt.contains("# FULL-AUTO (PLAN MODE): Work autonomously within Plan Mode constraints.")
    );
    assert!(!prompt.contains("# FULL-AUTO: Complete task autonomously until done or blocked."));
}

#[tokio::test]
async fn test_mode_changes_invalidate_cached_prompt() {
    let prompt_builder = IncrementalSystemPrompt::new();
    let base_prompt = "Base prompt";
    let base_context = test_context();
    let full_auto_context = SystemPromptContext {
        full_auto: true,
        ..test_context()
    };

    let base = prompt_builder
        .get_system_prompt(base_prompt, 1, base_context.hash(), &base_context, None)
        .await;
    let full_auto = prompt_builder
        .get_system_prompt(
            base_prompt,
            1,
            full_auto_context.hash(),
            &full_auto_context,
            None,
        )
        .await;

    assert_ne!(base, full_auto);
    assert!(!base.contains("# FULL-AUTO:"));
    assert!(full_auto.contains("# FULL-AUTO: Complete task autonomously until done or blocked."));
}