Skip to main content

vtcode_config/loader/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::fs;
4use std::path::Path;
5
6use crate::acp::AgentClientProtocolConfig;
7use crate::codex::{FileOpener, HistoryConfig, TuiConfig};
8use crate::context::ContextFeaturesConfig;
9use crate::core::{
10    AgentConfig, AnthropicConfig, AuthConfig, AutomationConfig, CommandsConfig,
11    CustomProviderConfig, DotfileProtectionConfig, ModelConfig, OpenAIConfig, PermissionsConfig,
12    PromptCachingConfig, SandboxConfig, SecurityConfig, SkillsConfig, ToolsConfig,
13};
14use crate::debug::DebugConfig;
15use crate::defaults::{self, ConfigDefaultsProvider};
16use crate::hooks::HooksConfig;
17use crate::ide_context::IdeContextConfig;
18use crate::mcp::McpClientConfig;
19use crate::optimization::OptimizationConfig;
20use crate::output_styles::OutputStyleConfig;
21use crate::root::{ChatConfig, PtyConfig, UiConfig};
22use crate::telemetry::TelemetryConfig;
23use crate::timeouts::TimeoutsConfig;
24
25use crate::loader::syntax_highlighting::SyntaxHighlightingConfig;
26
27/// Provider-specific configuration
28#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
29#[derive(Debug, Clone, Deserialize, Serialize, Default)]
30pub struct ProviderConfig {
31    /// OpenAI provider configuration
32    #[serde(default)]
33    pub openai: OpenAIConfig,
34
35    /// Anthropic provider configuration
36    #[serde(default)]
37    pub anthropic: AnthropicConfig,
38}
39
40/// Main configuration structure for VT Code
41#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
42#[derive(Debug, Clone, Deserialize, Serialize, Default)]
43pub struct VTCodeConfig {
44    /// Codex-compatible clickable citation URI scheme.
45    #[serde(default)]
46    pub file_opener: FileOpener,
47
48    /// Codex-compatible project doc byte limit override.
49    #[serde(default)]
50    pub project_doc_max_bytes: Option<usize>,
51
52    /// Codex-compatible fallback filenames for project instructions discovery.
53    #[serde(default)]
54    pub project_doc_fallback_filenames: Vec<String>,
55
56    /// External notification command invoked for supported events.
57    #[serde(default)]
58    pub notify: Vec<String>,
59
60    /// Codex-compatible local history persistence controls.
61    #[serde(default)]
62    pub history: HistoryConfig,
63
64    /// Codex-compatible TUI settings.
65    #[serde(default)]
66    pub tui: TuiConfig,
67
68    /// Agent-wide settings
69    #[serde(default)]
70    pub agent: AgentConfig,
71
72    /// Authentication configuration for OAuth flows
73    #[serde(default)]
74    pub auth: AuthConfig,
75
76    /// Tool execution policies
77    #[serde(default)]
78    pub tools: ToolsConfig,
79
80    /// Unix command permissions
81    #[serde(default)]
82    pub commands: CommandsConfig,
83
84    /// Permission system settings (resolution, audit logging, caching)
85    #[serde(default)]
86    pub permissions: PermissionsConfig,
87
88    /// Security settings
89    #[serde(default)]
90    pub security: SecurityConfig,
91
92    /// Sandbox settings for command execution isolation
93    #[serde(default)]
94    pub sandbox: SandboxConfig,
95
96    /// UI settings
97    #[serde(default)]
98    pub ui: UiConfig,
99
100    /// Chat settings
101    #[serde(default)]
102    pub chat: ChatConfig,
103
104    /// PTY settings
105    #[serde(default)]
106    pub pty: PtyConfig,
107
108    /// Debug and tracing settings
109    #[serde(default)]
110    pub debug: DebugConfig,
111
112    /// Context features (e.g., Decision Ledger)
113    #[serde(default)]
114    pub context: ContextFeaturesConfig,
115
116    /// Telemetry configuration (logging, trajectory)
117    #[serde(default)]
118    pub telemetry: TelemetryConfig,
119
120    /// Performance optimization settings
121    #[serde(default)]
122    pub optimization: OptimizationConfig,
123
124    /// Syntax highlighting configuration
125    #[serde(default)]
126    pub syntax_highlighting: SyntaxHighlightingConfig,
127
128    /// Timeout ceilings and UI warning thresholds
129    #[serde(default)]
130    pub timeouts: TimeoutsConfig,
131
132    /// Automation configuration
133    #[serde(default)]
134    pub automation: AutomationConfig,
135
136    /// Prompt cache configuration (local + provider integration)
137    #[serde(default)]
138    pub prompt_cache: PromptCachingConfig,
139
140    /// Model Context Protocol configuration
141    #[serde(default)]
142    pub mcp: McpClientConfig,
143
144    /// Agent Client Protocol configuration
145    #[serde(default)]
146    pub acp: AgentClientProtocolConfig,
147
148    /// IDE context configuration
149    #[serde(default)]
150    pub ide_context: IdeContextConfig,
151
152    /// Lifecycle hooks configuration
153    #[serde(default)]
154    pub hooks: HooksConfig,
155
156    /// Model-specific behavior configuration
157    #[serde(default)]
158    pub model: ModelConfig,
159
160    /// Provider-specific configuration
161    #[serde(default)]
162    pub provider: ProviderConfig,
163
164    /// Skills system configuration (Agent Skills spec)
165    #[serde(default)]
166    pub skills: SkillsConfig,
167
168    /// User-defined OpenAI-compatible provider endpoints.
169    /// These entries are editable in `/config` and appear in the model picker
170    /// using each entry's `display_name`.
171    #[serde(default)]
172    pub custom_providers: Vec<CustomProviderConfig>,
173
174    /// Output style configuration
175    #[serde(default)]
176    pub output_style: OutputStyleConfig,
177
178    /// Dotfile protection configuration
179    #[serde(default)]
180    pub dotfile_protection: DotfileProtectionConfig,
181}
182
183impl VTCodeConfig {
184    pub fn apply_compat_defaults(&mut self) {
185        if let Some(max_bytes) = self.project_doc_max_bytes {
186            self.agent.project_doc_max_bytes = max_bytes;
187        }
188
189        if !self.project_doc_fallback_filenames.is_empty() {
190            self.agent.project_doc_fallback_filenames = self.project_doc_fallback_filenames.clone();
191        }
192    }
193
194    pub fn validate(&self) -> Result<()> {
195        self.syntax_highlighting
196            .validate()
197            .context("Invalid syntax_highlighting configuration")?;
198
199        self.context
200            .validate()
201            .context("Invalid context configuration")?;
202
203        self.hooks
204            .validate()
205            .context("Invalid hooks configuration")?;
206
207        self.timeouts
208            .validate()
209            .context("Invalid timeouts configuration")?;
210
211        self.prompt_cache
212            .validate()
213            .context("Invalid prompt_cache configuration")?;
214
215        self.ui
216            .keyboard_protocol
217            .validate()
218            .context("Invalid keyboard_protocol configuration")?;
219
220        self.pty.validate().context("Invalid pty configuration")?;
221
222        // Validate custom providers
223        let mut seen_names = std::collections::HashSet::new();
224        for cp in &self.custom_providers {
225            cp.validate()
226                .map_err(|msg| anyhow::anyhow!(msg))
227                .context("Invalid custom_providers configuration")?;
228            if !seen_names.insert(cp.name.to_lowercase()) {
229                anyhow::bail!("custom_providers: duplicate name `{}`", cp.name);
230            }
231        }
232
233        Ok(())
234    }
235
236    /// Look up a custom provider by its stable key.
237    pub fn custom_provider(&self, name: &str) -> Option<&CustomProviderConfig> {
238        let lower = name.to_lowercase();
239        self.custom_providers
240            .iter()
241            .find(|cp| cp.name.to_lowercase() == lower)
242    }
243
244    /// Get the display name for any provider key, falling back to the raw key
245    /// if no custom provider matches.
246    pub fn provider_display_name(&self, provider_key: &str) -> String {
247        if let Some(cp) = self.custom_provider(provider_key) {
248            cp.display_name.clone()
249        } else if let Ok(p) = std::str::FromStr::from_str(provider_key) {
250            let p: crate::models::Provider = p;
251            p.label().to_string()
252        } else {
253            provider_key.to_string()
254        }
255    }
256
257    #[cfg(feature = "bootstrap")]
258    /// Bootstrap project with config + gitignore
259    pub fn bootstrap_project<P: AsRef<Path>>(workspace: P, force: bool) -> Result<Vec<String>> {
260        Self::bootstrap_project_with_options(workspace, force, false)
261    }
262
263    #[cfg(feature = "bootstrap")]
264    /// Bootstrap project with config + gitignore, with option to create in home directory
265    pub fn bootstrap_project_with_options<P: AsRef<Path>>(
266        workspace: P,
267        force: bool,
268        use_home_dir: bool,
269    ) -> Result<Vec<String>> {
270        let workspace = workspace.as_ref().to_path_buf();
271        defaults::with_config_defaults(|provider| {
272            Self::bootstrap_project_with_provider(&workspace, force, use_home_dir, provider)
273        })
274    }
275
276    #[cfg(feature = "bootstrap")]
277    /// Bootstrap project files using the supplied [`ConfigDefaultsProvider`].
278    pub fn bootstrap_project_with_provider<P: AsRef<Path>>(
279        workspace: P,
280        force: bool,
281        use_home_dir: bool,
282        defaults_provider: &dyn ConfigDefaultsProvider,
283    ) -> Result<Vec<String>> {
284        let workspace = workspace.as_ref();
285        let config_file_name = defaults_provider.config_file_name().to_string();
286        let (config_path, gitignore_path) = crate::loader::bootstrap::determine_bootstrap_targets(
287            workspace,
288            use_home_dir,
289            &config_file_name,
290            defaults_provider,
291        )?;
292
293        crate::loader::bootstrap::ensure_parent_dir(&config_path)?;
294        crate::loader::bootstrap::ensure_parent_dir(&gitignore_path)?;
295
296        let mut created_files = Vec::new();
297
298        if !config_path.exists() || force {
299            let config_content = Self::default_vtcode_toml_template();
300
301            fs::write(&config_path, config_content).with_context(|| {
302                format!("Failed to write config file: {}", config_path.display())
303            })?;
304
305            if let Some(file_name) = config_path.file_name().and_then(|name| name.to_str()) {
306                created_files.push(file_name.to_string());
307            }
308        }
309
310        if !gitignore_path.exists() || force {
311            let gitignore_content = Self::default_vtcode_gitignore();
312            fs::write(&gitignore_path, gitignore_content).with_context(|| {
313                format!(
314                    "Failed to write gitignore file: {}",
315                    gitignore_path.display()
316                )
317            })?;
318
319            if let Some(file_name) = gitignore_path.file_name().and_then(|name| name.to_str()) {
320                created_files.push(file_name.to_string());
321            }
322        }
323
324        Ok(created_files)
325    }
326
327    #[cfg(feature = "bootstrap")]
328    /// Generate the default `vtcode.toml` template used by bootstrap helpers.
329    fn default_vtcode_toml_template() -> String {
330        r#"# VT Code Configuration File (Example)
331# Getting-started reference; see docs/config/CONFIGURATION_PRECEDENCE.md for override order.
332# Copy this file to vtcode.toml and customize as needed.
333
334# Clickable file citation URI scheme ("vscode", "cursor", "windsurf", "vscode-insiders", "none")
335file_opener = "none"
336
337# Additional fallback filenames to use when AGENTS.md is absent
338project_doc_fallback_filenames = []
339
340# Optional external command invoked after each completed agent turn
341notify = []
342
343# User-defined OpenAI-compatible providers
344custom_providers = []
345
346# [[custom_providers]]
347# name = "mycorp"
348# display_name = "MyCorporateName"
349# base_url = "https://llm.corp.example/v1"
350# api_key_env = "MYCORP_API_KEY"
351# model = "gpt-4o-mini"
352
353[history]
354# Persist local session transcripts to disk
355persistence = "file"
356
357# Optional max size budget for each persisted session snapshot
358# max_bytes = 104857600
359
360[tui]
361# Enable all built-in TUI notifications, disable them, or restrict to specific event types.
362# notifications = true
363# notifications = ["agent-turn-complete", "approval-requested"]
364
365# Notification transport: "auto", "osc9", or "bel"
366# notification_method = "auto"
367
368# Set to false to reduce shimmer/animation effects
369# animations = true
370
371# Alternate-screen override: "always" or "never"
372# alternate_screen = "never"
373
374# Show onboarding hints on the welcome screen
375# show_tooltips = true
376
377# Core agent behavior; see docs/config/CONFIGURATION_PRECEDENCE.md.
378[agent]
379# Primary LLM provider to use (e.g., "openai", "gemini", "anthropic", "openrouter")
380provider = "openai"
381
382# Environment variable containing the API key for the provider
383api_key_env = "OPENAI_API_KEY"
384
385# Default model to use when no specific model is specified
386default_model = "gpt-5.4"
387
388# Visual theme for the terminal interface
389theme = "ciapre-dark"
390
391# Enable TODO planning helper mode for structured task management
392todo_planning_mode = true
393
394# UI surface to use ("auto", "alternate", "inline")
395ui_surface = "auto"
396
397# Maximum number of conversation turns before rotating context (affects memory usage)
398# Lower values reduce memory footprint but may lose context; higher values preserve context but use more memory
399max_conversation_turns = 150
400
401# Reasoning effort level ("none", "minimal", "low", "medium", "high", "xhigh") - affects model usage and response speed
402reasoning_effort = "none"
403
404# Temperature for main model responses (0.0-1.0)
405temperature = 0.7
406
407# Enable self-review loop to check and improve responses (increases API calls)
408enable_self_review = false
409
410# Maximum number of review passes when self-review is enabled
411max_review_passes = 1
412
413# Enable prompt refinement loop for improved prompt quality (increases processing time)
414refine_prompts_enabled = false
415
416# Maximum passes for prompt refinement when enabled
417refine_prompts_max_passes = 1
418
419# Optional alternate model for refinement (leave empty to use default)
420refine_prompts_model = ""
421
422# Maximum size of project documentation to include in context (in bytes)
423project_doc_max_bytes = 16384
424
425# Maximum size of instruction files to process (in bytes)
426instruction_max_bytes = 16384
427
428# List of additional instruction files to include in context
429instruction_files = []
430
431# Default editing mode on startup: "edit" or "plan"
432# "edit" - Full tool access for file modifications and command execution (default)
433# "plan" - Read-only mode that produces implementation plans without making changes
434# Toggle during session with Shift+Tab or /plan command
435default_editing_mode = "edit"
436
437# Inline prompt suggestions for the chat composer
438[agent.prompt_suggestions]
439# Enable Alt+P ghost-text suggestions in the composer
440enabled = true
441
442# Lightweight model for prompt suggestions (leave empty to auto-pick)
443model = ""
444
445# Lower values keep suggestions stable and completion-like
446temperature = 0.3
447
448# Show a one-time note that LLM-backed suggestions can consume tokens
449show_cost_notice = true
450
451# Onboarding configuration - Customize the startup experience
452[agent.onboarding]
453# Enable the onboarding welcome message on startup
454enabled = true
455
456# Custom introduction text shown on startup
457intro_text = "Let's get oriented. I preloaded workspace context so we can move fast."
458
459# Include project overview information in welcome
460include_project_overview = true
461
462# Include language summary information in welcome
463include_language_summary = false
464
465# Include key guideline highlights from AGENTS.md
466include_guideline_highlights = true
467
468# Include usage tips in the welcome message
469include_usage_tips_in_welcome = false
470
471# Include recommended actions in the welcome message
472include_recommended_actions_in_welcome = false
473
474# Maximum number of guideline highlights to show
475guideline_highlight_limit = 3
476
477# List of usage tips shown during onboarding
478usage_tips = [
479    "Describe your current coding goal or ask for a quick status overview.",
480    "Reference AGENTS.md guidelines when proposing changes.",
481    "Prefer asking for targeted file reads or diffs before editing.",
482]
483
484# List of recommended actions shown during onboarding
485recommended_actions = [
486    "Review the highlighted guidelines and share the task you want to tackle.",
487    "Ask for a workspace tour if you need more context.",
488]
489
490# Checkpointing configuration for session persistence
491[agent.checkpointing]
492# Enable automatic session checkpointing
493enabled = true
494
495# Maximum number of checkpoints to keep on disk
496max_snapshots = 50
497
498# Maximum age of checkpoints to keep (in days)
499max_age_days = 30
500
501# Tool security configuration
502[tools]
503# Default policy when no specific policy is defined ("allow", "prompt", "deny")
504# "allow" - Execute without confirmation
505# "prompt" - Ask for confirmation
506# "deny" - Block the tool
507default_policy = "prompt"
508
509# Maximum number of tool loops allowed per turn
510# Set to 0 to disable the limit and let other turn safeguards govern termination.
511max_tool_loops = 0
512
513# Maximum number of repeated identical tool calls (prevents stuck loops)
514max_repeated_tool_calls = 2
515
516# Maximum consecutive blocked tool calls before force-breaking the turn
517# Helps prevent high-CPU churn when calls are repeatedly denied/blocked
518max_consecutive_blocked_tool_calls_per_turn = 8
519
520# Maximum sequential spool-chunk reads per turn before nudging targeted extraction/summarization
521max_sequential_spool_chunk_reads = 6
522
523# Specific tool policies - Override default policy for individual tools
524[tools.policies]
525apply_patch = "prompt"            # Apply code patches (requires confirmation)
526request_user_input = "allow"      # Ask focused user questions when the task requires it
527task_tracker = "prompt"           # Create or update explicit task plans
528unified_exec = "prompt"           # Run commands; pipe-first by default, set tty=true for PTY/interactive sessions
529unified_file = "allow"            # Canonical file read/write/edit/move/copy/delete surface
530unified_search = "allow"          # Canonical search/list/intelligence/error surface
531
532# Command security - Define safe and dangerous command patterns
533[commands]
534# Commands that are always allowed without confirmation
535allow_list = [
536    "ls",           # List directory contents
537    "pwd",          # Print working directory
538    "git status",   # Show git status
539    "git diff",     # Show git differences
540    "cargo check",  # Check Rust code
541    "echo",         # Print text
542]
543
544# Commands that are never allowed
545deny_list = [
546    "rm -rf /",        # Delete root directory (dangerous)
547    "rm -rf ~",        # Delete home directory (dangerous)
548    "shutdown",        # Shut down system (dangerous)
549    "reboot",          # Reboot system (dangerous)
550    "sudo *",          # Any sudo command (dangerous)
551    ":(){ :|:& };:",   # Fork bomb (dangerous)
552]
553
554# Command patterns that are allowed (supports glob patterns)
555allow_glob = [
556    "git *",        # All git commands
557    "cargo *",      # All cargo commands
558    "python -m *",  # Python module commands
559]
560
561# Command patterns that are denied (supports glob patterns)
562deny_glob = [
563    "rm *",         # All rm commands
564    "sudo *",       # All sudo commands
565    "chmod *",      # All chmod commands
566    "chown *",      # All chown commands
567    "kubectl *",    # All kubectl commands (admin access)
568]
569
570# Regular expression patterns for allowed commands (if needed)
571allow_regex = []
572
573# Regular expression patterns for denied commands (if needed)
574deny_regex = []
575
576# Security configuration - Safety settings for automated operations
577[security]
578# Require human confirmation for potentially dangerous actions
579human_in_the_loop = true
580
581# Require explicit write tool usage for claims about file modifications
582require_write_tool_for_claims = true
583
584# Auto-apply patches without prompting (DANGEROUS - disable for safety)
585auto_apply_detected_patches = false
586
587# UI configuration - Terminal and display settings
588[ui]
589# Tool output display mode
590# "compact" - Concise tool output
591# "full" - Detailed tool output
592tool_output_mode = "compact"
593
594# Maximum number of lines to display in tool output (prevents transcript flooding)
595# Lines beyond this limit are truncated to a tail preview
596tool_output_max_lines = 600
597
598# Maximum bytes threshold for spooling tool output to disk
599# Output exceeding this size is written to .vtcode/tool-output/*.log
600tool_output_spool_bytes = 200000
601
602# Optional custom directory for spooled tool output logs
603# If not set, defaults to .vtcode/tool-output/
604# tool_output_spool_dir = "/path/to/custom/spool/dir"
605
606# Allow ANSI escape sequences in tool output (enables colors but may cause layout issues)
607allow_tool_ansi = false
608
609# Number of rows to allocate for inline UI viewport
610inline_viewport_rows = 16
611
612# Show elapsed time divider after each completed turn
613show_turn_timer = false
614
615# Show warning/error/fatal diagnostic lines directly in transcript
616# Effective in debug/development builds only
617show_diagnostics_in_transcript = false
618
619# Show timeline navigation panel
620show_timeline_pane = false
621
622# Runtime notification preferences
623[ui.notifications]
624# Master toggle for terminal/desktop notifications
625enabled = true
626
627# Delivery mode: "terminal", "hybrid", or "desktop"
628delivery_mode = "hybrid"
629
630# Suppress notifications while terminal is focused
631suppress_when_focused = true
632
633# Failure/error notifications
634command_failure = false
635tool_failure = false
636error = true
637
638# Completion notifications
639# Legacy master toggle (fallback for split settings when unset)
640completion = true
641completion_success = false
642completion_failure = true
643
644# Human approval/interaction notifications
645hitl = true
646policy_approval = true
647request = false
648
649# Success notifications for tool call results
650tool_success = false
651
652# Repeated notification suppression
653repeat_window_seconds = 30
654max_identical_in_window = 1
655
656# Status line configuration
657[ui.status_line]
658# Status line mode ("auto", "command", "hidden")
659mode = "auto"
660
661# How often to refresh status line (milliseconds)
662refresh_interval_ms = 2000
663
664# Timeout for command execution in status line (milliseconds)
665command_timeout_ms = 200
666
667# Enable Vim-style prompt editing in interactive mode
668vim_mode = false
669
670# PTY (Pseudo Terminal) configuration - For interactive command execution
671[pty]
672# Enable PTY support for interactive commands
673enabled = true
674
675# Default number of terminal rows for PTY sessions
676default_rows = 24
677
678# Default number of terminal columns for PTY sessions
679default_cols = 80
680
681# Maximum number of concurrent PTY sessions
682max_sessions = 10
683
684# Command timeout in seconds (prevents hanging commands)
685command_timeout_seconds = 300
686
687# Number of recent lines to show in PTY output
688stdout_tail_lines = 20
689
690# Total lines to keep in PTY scrollback buffer
691scrollback_lines = 400
692
693# Terminal emulation backend for PTY snapshots
694emulation_backend = "legacy_vt100"
695
696# Optional preferred shell for PTY sessions (falls back to $SHELL when unset)
697# preferred_shell = "/bin/zsh"
698
699# Route shell execution through zsh EXEC_WRAPPER intercept hooks (feature-gated)
700shell_zsh_fork = false
701
702# Absolute path to patched zsh used when shell_zsh_fork is enabled
703# zsh_path = "/usr/local/bin/zsh"
704
705# Context management configuration - Controls conversation memory
706[context]
707# Maximum number of tokens to keep in context (affects model cost and performance)
708# Higher values preserve more context but cost more and may hit token limits
709max_context_tokens = 90000
710
711# Percentage to trim context to when it gets too large
712trim_to_percent = 60
713
714# Number of recent conversation turns to always preserve
715preserve_recent_turns = 6
716
717# Decision ledger configuration - Track important decisions
718[context.ledger]
719# Enable decision tracking and persistence
720enabled = true
721
722# Maximum number of decisions to keep in ledger
723max_entries = 12
724
725# Include ledger summary in model prompts
726include_in_prompt = true
727
728# Preserve ledger during context compression
729preserve_in_compression = true
730
731# AI model routing - Intelligent model selection
732# Telemetry and analytics
733[telemetry]
734# Enable trajectory logging for usage analysis
735trajectory_enabled = true
736
737# Syntax highlighting configuration
738[syntax_highlighting]
739# Enable syntax highlighting for code in tool output
740enabled = true
741
742# Theme for syntax highlighting
743theme = "base16-ocean.dark"
744
745# Cache syntax highlighting themes for performance
746cache_themes = true
747
748# Maximum file size for syntax highlighting (in MB)
749max_file_size_mb = 10
750
751# Programming languages to enable syntax highlighting for
752enabled_languages = [
753    "rust",
754    "python",
755    "javascript",
756    "typescript",
757    "go",
758    "java",
759    "bash",
760    "sh",
761    "shell",
762    "zsh",
763    "markdown",
764    "md",
765]
766
767# Timeout for syntax highlighting operations (milliseconds)
768highlight_timeout_ms = 1000
769
770# Automation features - Full-auto mode settings
771[automation.full_auto]
772# Enable full automation mode (DANGEROUS - requires careful oversight)
773enabled = false
774
775# Maximum number of turns before asking for human input
776max_turns = 30
777
778# Tools allowed in full automation mode
779allowed_tools = [
780    "write_file",
781    "read_file",
782    "list_files",
783    "grep_file",
784]
785
786# Require profile acknowledgment before using full auto
787require_profile_ack = true
788
789# Path to full auto profile configuration
790profile_path = "automation/full_auto_profile.toml"
791
792# Prompt caching - Cache model responses for efficiency
793[prompt_cache]
794# Enable prompt caching (reduces API calls for repeated prompts)
795enabled = true
796
797# Directory for cache storage
798cache_dir = "~/.vtcode/cache/prompts"
799
800# Maximum number of cache entries to keep
801max_entries = 1000
802
803# Maximum age of cache entries (in days)
804max_age_days = 30
805
806# Enable automatic cache cleanup
807enable_auto_cleanup = true
808
809# Minimum quality threshold to keep cache entries
810min_quality_threshold = 0.7
811
812# Keep volatile runtime counters at the end of system prompts to improve provider-side prefix cache reuse
813# (enabled by default; disable only if you need legacy prompt layout)
814cache_friendly_prompt_shaping = true
815
816# Prompt cache configuration for OpenAI
817    [prompt_cache.providers.openai]
818    enabled = true
819    min_prefix_tokens = 1024
820    idle_expiration_seconds = 3600
821    surface_metrics = true
822    # Routing key strategy for OpenAI prompt cache locality.
823    # "session" creates one stable key per VT Code conversation.
824    prompt_cache_key_mode = "session"
825    # Optional: server-side prompt cache retention for OpenAI Responses API
826    # Supported values: "in_memory" or "24h" (leave commented out for default behavior)
827    # prompt_cache_retention = "24h"
828
829# Prompt cache configuration for Anthropic
830[prompt_cache.providers.anthropic]
831enabled = true
832default_ttl_seconds = 300
833extended_ttl_seconds = 3600
834max_breakpoints = 4
835cache_system_messages = true
836cache_user_messages = true
837
838# Prompt cache configuration for Gemini
839[prompt_cache.providers.gemini]
840enabled = true
841mode = "implicit"
842min_prefix_tokens = 1024
843explicit_ttl_seconds = 3600
844
845# Prompt cache configuration for OpenRouter
846[prompt_cache.providers.openrouter]
847enabled = true
848propagate_provider_capabilities = true
849report_savings = true
850
851# Prompt cache configuration for Moonshot
852[prompt_cache.providers.moonshot]
853enabled = true
854
855# Prompt cache configuration for DeepSeek
856[prompt_cache.providers.deepseek]
857enabled = true
858surface_metrics = true
859
860# Prompt cache configuration for Z.AI
861[prompt_cache.providers.zai]
862enabled = false
863
864# Model Context Protocol (MCP) - Connect external tools and services
865[mcp]
866# Enable Model Context Protocol (may impact startup time if services unavailable)
867enabled = true
868max_concurrent_connections = 5
869request_timeout_seconds = 30
870retry_attempts = 3
871
872# MCP UI configuration
873[mcp.ui]
874mode = "compact"
875max_events = 50
876show_provider_names = true
877
878# MCP renderer profiles for different services
879[mcp.ui.renderers]
880sequential-thinking = "sequential-thinking"
881context7 = "context7"
882
883# MCP provider configuration - External services that connect via MCP
884[[mcp.providers]]
885name = "time"
886command = "uvx"
887args = ["mcp-server-time"]
888enabled = true
889max_concurrent_requests = 3
890[mcp.providers.env]
891
892# Agent Client Protocol (ACP) - IDE integration
893[acp]
894enabled = true
895
896[acp.zed]
897enabled = true
898transport = "stdio"
899# workspace_trust controls ACP trust mode: "tools_policy" (prompts) or "full_auto" (no prompts)
900workspace_trust = "full_auto"
901
902[acp.zed.tools]
903read_file = true
904list_files = true
905
906# Cross-IDE editor context bridge
907[ide_context]
908enabled = true
909inject_into_prompt = true
910show_in_tui = true
911include_selection_text = true
912provider_mode = "auto"
913
914[ide_context.providers.vscode_compatible]
915enabled = true
916
917[ide_context.providers.zed]
918enabled = true
919
920[ide_context.providers.generic]
921enabled = true"#.to_string()
922    }
923
924    #[cfg(feature = "bootstrap")]
925    fn default_vtcode_gitignore() -> String {
926        r#"# Security-focused exclusions
927.env, .env.local, secrets/, .aws/, .ssh/
928
929# Development artifacts
930target/, build/, dist/, node_modules/, vendor/
931
932# Database files
933*.db, *.sqlite, *.sqlite3
934
935# Binary files
936*.exe, *.dll, *.so, *.dylib, *.bin
937
938# IDE files (comprehensive)
939.vscode/, .idea/, *.swp, *.swo
940"#
941        .to_string()
942    }
943
944    #[cfg(feature = "bootstrap")]
945    /// Create sample configuration file
946    pub fn create_sample_config<P: AsRef<Path>>(output: P) -> Result<()> {
947        let output = output.as_ref();
948        let config_content = Self::default_vtcode_toml_template();
949
950        fs::write(output, config_content)
951            .with_context(|| format!("Failed to write config file: {}", output.display()))?;
952
953        Ok(())
954    }
955}