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#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
29#[derive(Debug, Clone, Deserialize, Serialize, Default)]
30pub struct ProviderConfig {
31 #[serde(default)]
33 pub openai: OpenAIConfig,
34
35 #[serde(default)]
37 pub anthropic: AnthropicConfig,
38}
39
40#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
42#[derive(Debug, Clone, Deserialize, Serialize, Default)]
43pub struct VTCodeConfig {
44 #[serde(default)]
46 pub file_opener: FileOpener,
47
48 #[serde(default)]
50 pub project_doc_max_bytes: Option<usize>,
51
52 #[serde(default)]
54 pub project_doc_fallback_filenames: Vec<String>,
55
56 #[serde(default)]
58 pub notify: Vec<String>,
59
60 #[serde(default)]
62 pub history: HistoryConfig,
63
64 #[serde(default)]
66 pub tui: TuiConfig,
67
68 #[serde(default)]
70 pub agent: AgentConfig,
71
72 #[serde(default)]
74 pub auth: AuthConfig,
75
76 #[serde(default)]
78 pub tools: ToolsConfig,
79
80 #[serde(default)]
82 pub commands: CommandsConfig,
83
84 #[serde(default)]
86 pub permissions: PermissionsConfig,
87
88 #[serde(default)]
90 pub security: SecurityConfig,
91
92 #[serde(default)]
94 pub sandbox: SandboxConfig,
95
96 #[serde(default)]
98 pub ui: UiConfig,
99
100 #[serde(default)]
102 pub chat: ChatConfig,
103
104 #[serde(default)]
106 pub pty: PtyConfig,
107
108 #[serde(default)]
110 pub debug: DebugConfig,
111
112 #[serde(default)]
114 pub context: ContextFeaturesConfig,
115
116 #[serde(default)]
118 pub telemetry: TelemetryConfig,
119
120 #[serde(default)]
122 pub optimization: OptimizationConfig,
123
124 #[serde(default)]
126 pub syntax_highlighting: SyntaxHighlightingConfig,
127
128 #[serde(default)]
130 pub timeouts: TimeoutsConfig,
131
132 #[serde(default)]
134 pub automation: AutomationConfig,
135
136 #[serde(default)]
138 pub prompt_cache: PromptCachingConfig,
139
140 #[serde(default)]
142 pub mcp: McpClientConfig,
143
144 #[serde(default)]
146 pub acp: AgentClientProtocolConfig,
147
148 #[serde(default)]
150 pub ide_context: IdeContextConfig,
151
152 #[serde(default)]
154 pub hooks: HooksConfig,
155
156 #[serde(default)]
158 pub model: ModelConfig,
159
160 #[serde(default)]
162 pub provider: ProviderConfig,
163
164 #[serde(default)]
166 pub skills: SkillsConfig,
167
168 #[serde(default)]
172 pub custom_providers: Vec<CustomProviderConfig>,
173
174 #[serde(default)]
176 pub output_style: OutputStyleConfig,
177
178 #[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 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 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 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 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 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 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 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# Onboarding configuration - Customize the startup experience
438[agent.onboarding]
439# Enable the onboarding welcome message on startup
440enabled = true
441
442# Custom introduction text shown on startup
443intro_text = "Let's get oriented. I preloaded workspace context so we can move fast."
444
445# Include project overview information in welcome
446include_project_overview = true
447
448# Include language summary information in welcome
449include_language_summary = false
450
451# Include key guideline highlights from AGENTS.md
452include_guideline_highlights = true
453
454# Include usage tips in the welcome message
455include_usage_tips_in_welcome = false
456
457# Include recommended actions in the welcome message
458include_recommended_actions_in_welcome = false
459
460# Maximum number of guideline highlights to show
461guideline_highlight_limit = 3
462
463# List of usage tips shown during onboarding
464usage_tips = [
465 "Describe your current coding goal or ask for a quick status overview.",
466 "Reference AGENTS.md guidelines when proposing changes.",
467 "Prefer asking for targeted file reads or diffs before editing.",
468]
469
470# List of recommended actions shown during onboarding
471recommended_actions = [
472 "Review the highlighted guidelines and share the task you want to tackle.",
473 "Ask for a workspace tour if you need more context.",
474]
475
476# Checkpointing configuration for session persistence
477[agent.checkpointing]
478# Enable automatic session checkpointing
479enabled = true
480
481# Maximum number of checkpoints to keep on disk
482max_snapshots = 50
483
484# Maximum age of checkpoints to keep (in days)
485max_age_days = 30
486
487# Tool security configuration
488[tools]
489# Default policy when no specific policy is defined ("allow", "prompt", "deny")
490# "allow" - Execute without confirmation
491# "prompt" - Ask for confirmation
492# "deny" - Block the tool
493default_policy = "prompt"
494
495# Maximum number of tool loops allowed per turn
496# Set to 0 to disable the limit and let other turn safeguards govern termination.
497max_tool_loops = 0
498
499# Maximum number of repeated identical tool calls (prevents stuck loops)
500max_repeated_tool_calls = 2
501
502# Maximum consecutive blocked tool calls before force-breaking the turn
503# Helps prevent high-CPU churn when calls are repeatedly denied/blocked
504max_consecutive_blocked_tool_calls_per_turn = 8
505
506# Maximum sequential spool-chunk reads per turn before nudging targeted extraction/summarization
507max_sequential_spool_chunk_reads = 6
508
509# Specific tool policies - Override default policy for individual tools
510[tools.policies]
511apply_patch = "prompt" # Apply code patches (requires confirmation)
512request_user_input = "allow" # Ask focused user questions when the task requires it
513task_tracker = "prompt" # Create or update explicit task plans
514unified_exec = "prompt" # Run commands; pipe-first by default, set tty=true for PTY/interactive sessions
515unified_file = "allow" # Canonical file read/write/edit/move/copy/delete surface
516unified_search = "allow" # Canonical search/list/intelligence/error surface
517
518# Command security - Define safe and dangerous command patterns
519[commands]
520# Commands that are always allowed without confirmation
521allow_list = [
522 "ls", # List directory contents
523 "pwd", # Print working directory
524 "git status", # Show git status
525 "git diff", # Show git differences
526 "cargo check", # Check Rust code
527 "echo", # Print text
528]
529
530# Commands that are never allowed
531deny_list = [
532 "rm -rf /", # Delete root directory (dangerous)
533 "rm -rf ~", # Delete home directory (dangerous)
534 "shutdown", # Shut down system (dangerous)
535 "reboot", # Reboot system (dangerous)
536 "sudo *", # Any sudo command (dangerous)
537 ":(){ :|:& };:", # Fork bomb (dangerous)
538]
539
540# Command patterns that are allowed (supports glob patterns)
541allow_glob = [
542 "git *", # All git commands
543 "cargo *", # All cargo commands
544 "python -m *", # Python module commands
545]
546
547# Command patterns that are denied (supports glob patterns)
548deny_glob = [
549 "rm *", # All rm commands
550 "sudo *", # All sudo commands
551 "chmod *", # All chmod commands
552 "chown *", # All chown commands
553 "kubectl *", # All kubectl commands (admin access)
554]
555
556# Regular expression patterns for allowed commands (if needed)
557allow_regex = []
558
559# Regular expression patterns for denied commands (if needed)
560deny_regex = []
561
562# Security configuration - Safety settings for automated operations
563[security]
564# Require human confirmation for potentially dangerous actions
565human_in_the_loop = true
566
567# Require explicit write tool usage for claims about file modifications
568require_write_tool_for_claims = true
569
570# Auto-apply patches without prompting (DANGEROUS - disable for safety)
571auto_apply_detected_patches = false
572
573# UI configuration - Terminal and display settings
574[ui]
575# Tool output display mode
576# "compact" - Concise tool output
577# "full" - Detailed tool output
578tool_output_mode = "compact"
579
580# Maximum number of lines to display in tool output (prevents transcript flooding)
581# Lines beyond this limit are truncated to a tail preview
582tool_output_max_lines = 600
583
584# Maximum bytes threshold for spooling tool output to disk
585# Output exceeding this size is written to .vtcode/tool-output/*.log
586tool_output_spool_bytes = 200000
587
588# Optional custom directory for spooled tool output logs
589# If not set, defaults to .vtcode/tool-output/
590# tool_output_spool_dir = "/path/to/custom/spool/dir"
591
592# Allow ANSI escape sequences in tool output (enables colors but may cause layout issues)
593allow_tool_ansi = false
594
595# Number of rows to allocate for inline UI viewport
596inline_viewport_rows = 16
597
598# Show elapsed time divider after each completed turn
599show_turn_timer = false
600
601# Show warning/error/fatal diagnostic lines directly in transcript
602# Effective in debug/development builds only
603show_diagnostics_in_transcript = false
604
605# Show timeline navigation panel
606show_timeline_pane = false
607
608# Runtime notification preferences
609[ui.notifications]
610# Master toggle for terminal/desktop notifications
611enabled = true
612
613# Delivery mode: "terminal", "hybrid", or "desktop"
614delivery_mode = "hybrid"
615
616# Suppress notifications while terminal is focused
617suppress_when_focused = true
618
619# Failure/error notifications
620command_failure = false
621tool_failure = false
622error = true
623
624# Completion notifications
625# Legacy master toggle (fallback for split settings when unset)
626completion = true
627completion_success = false
628completion_failure = true
629
630# Human approval/interaction notifications
631hitl = true
632policy_approval = true
633request = false
634
635# Success notifications for tool call results
636tool_success = false
637
638# Repeated notification suppression
639repeat_window_seconds = 30
640max_identical_in_window = 1
641
642# Status line configuration
643[ui.status_line]
644# Status line mode ("auto", "command", "hidden")
645mode = "auto"
646
647# How often to refresh status line (milliseconds)
648refresh_interval_ms = 2000
649
650# Timeout for command execution in status line (milliseconds)
651command_timeout_ms = 200
652
653# Enable Vim-style prompt editing in interactive mode
654vim_mode = false
655
656# PTY (Pseudo Terminal) configuration - For interactive command execution
657[pty]
658# Enable PTY support for interactive commands
659enabled = true
660
661# Default number of terminal rows for PTY sessions
662default_rows = 24
663
664# Default number of terminal columns for PTY sessions
665default_cols = 80
666
667# Maximum number of concurrent PTY sessions
668max_sessions = 10
669
670# Command timeout in seconds (prevents hanging commands)
671command_timeout_seconds = 300
672
673# Number of recent lines to show in PTY output
674stdout_tail_lines = 20
675
676# Total lines to keep in PTY scrollback buffer
677scrollback_lines = 400
678
679# Terminal emulation backend for PTY snapshots
680emulation_backend = "ghostty"
681
682# Optional preferred shell for PTY sessions (falls back to $SHELL when unset)
683# preferred_shell = "/bin/zsh"
684
685# Route shell execution through zsh EXEC_WRAPPER intercept hooks (feature-gated)
686shell_zsh_fork = false
687
688# Absolute path to patched zsh used when shell_zsh_fork is enabled
689# zsh_path = "/usr/local/bin/zsh"
690
691# Context management configuration - Controls conversation memory
692[context]
693# Maximum number of tokens to keep in context (affects model cost and performance)
694# Higher values preserve more context but cost more and may hit token limits
695max_context_tokens = 90000
696
697# Percentage to trim context to when it gets too large
698trim_to_percent = 60
699
700# Number of recent conversation turns to always preserve
701preserve_recent_turns = 6
702
703# Decision ledger configuration - Track important decisions
704[context.ledger]
705# Enable decision tracking and persistence
706enabled = true
707
708# Maximum number of decisions to keep in ledger
709max_entries = 12
710
711# Include ledger summary in model prompts
712include_in_prompt = true
713
714# Preserve ledger during context compression
715preserve_in_compression = true
716
717# AI model routing - Intelligent model selection
718# Telemetry and analytics
719[telemetry]
720# Enable trajectory logging for usage analysis
721trajectory_enabled = true
722
723# Syntax highlighting configuration
724[syntax_highlighting]
725# Enable syntax highlighting for code in tool output
726enabled = true
727
728# Theme for syntax highlighting
729theme = "base16-ocean.dark"
730
731# Cache syntax highlighting themes for performance
732cache_themes = true
733
734# Maximum file size for syntax highlighting (in MB)
735max_file_size_mb = 10
736
737# Programming languages to enable syntax highlighting for
738enabled_languages = [
739 "rust",
740 "python",
741 "javascript",
742 "typescript",
743 "go",
744 "java",
745 "bash",
746 "sh",
747 "shell",
748 "zsh",
749 "markdown",
750 "md",
751]
752
753# Timeout for syntax highlighting operations (milliseconds)
754highlight_timeout_ms = 1000
755
756# Automation features - Full-auto mode settings
757[automation.full_auto]
758# Enable full automation mode (DANGEROUS - requires careful oversight)
759enabled = false
760
761# Maximum number of turns before asking for human input
762max_turns = 30
763
764# Tools allowed in full automation mode
765allowed_tools = [
766 "write_file",
767 "read_file",
768 "list_files",
769 "grep_file",
770]
771
772# Require profile acknowledgment before using full auto
773require_profile_ack = true
774
775# Path to full auto profile configuration
776profile_path = "automation/full_auto_profile.toml"
777
778# Prompt caching - Cache model responses for efficiency
779[prompt_cache]
780# Enable prompt caching (reduces API calls for repeated prompts)
781enabled = true
782
783# Directory for cache storage
784cache_dir = "~/.vtcode/cache/prompts"
785
786# Maximum number of cache entries to keep
787max_entries = 1000
788
789# Maximum age of cache entries (in days)
790max_age_days = 30
791
792# Enable automatic cache cleanup
793enable_auto_cleanup = true
794
795# Minimum quality threshold to keep cache entries
796min_quality_threshold = 0.7
797
798# Keep volatile runtime counters at the end of system prompts to improve provider-side prefix cache reuse
799# (enabled by default; disable only if you need legacy prompt layout)
800cache_friendly_prompt_shaping = true
801
802# Prompt cache configuration for OpenAI
803 [prompt_cache.providers.openai]
804 enabled = true
805 min_prefix_tokens = 1024
806 idle_expiration_seconds = 3600
807 surface_metrics = true
808 # Routing key strategy for OpenAI prompt cache locality.
809 # "session" creates one stable key per VT Code conversation.
810 prompt_cache_key_mode = "session"
811 # Optional: server-side prompt cache retention for OpenAI Responses API
812 # Example: "24h" (leave commented out for default behavior)
813 # prompt_cache_retention = "24h"
814
815# Prompt cache configuration for Anthropic
816[prompt_cache.providers.anthropic]
817enabled = true
818default_ttl_seconds = 300
819extended_ttl_seconds = 3600
820max_breakpoints = 4
821cache_system_messages = true
822cache_user_messages = true
823
824# Prompt cache configuration for Gemini
825[prompt_cache.providers.gemini]
826enabled = true
827mode = "implicit"
828min_prefix_tokens = 1024
829explicit_ttl_seconds = 3600
830
831# Prompt cache configuration for OpenRouter
832[prompt_cache.providers.openrouter]
833enabled = true
834propagate_provider_capabilities = true
835report_savings = true
836
837# Prompt cache configuration for Moonshot
838[prompt_cache.providers.moonshot]
839enabled = true
840
841# Prompt cache configuration for DeepSeek
842[prompt_cache.providers.deepseek]
843enabled = true
844surface_metrics = true
845
846# Prompt cache configuration for Z.AI
847[prompt_cache.providers.zai]
848enabled = false
849
850# Model Context Protocol (MCP) - Connect external tools and services
851[mcp]
852# Enable Model Context Protocol (may impact startup time if services unavailable)
853enabled = true
854max_concurrent_connections = 5
855request_timeout_seconds = 30
856retry_attempts = 3
857
858# MCP UI configuration
859[mcp.ui]
860mode = "compact"
861max_events = 50
862show_provider_names = true
863
864# MCP renderer profiles for different services
865[mcp.ui.renderers]
866sequential-thinking = "sequential-thinking"
867context7 = "context7"
868
869# MCP provider configuration - External services that connect via MCP
870[[mcp.providers]]
871name = "time"
872command = "uvx"
873args = ["mcp-server-time"]
874enabled = true
875max_concurrent_requests = 3
876[mcp.providers.env]
877
878# Agent Client Protocol (ACP) - IDE integration
879[acp]
880enabled = true
881
882[acp.zed]
883enabled = true
884transport = "stdio"
885# workspace_trust controls ACP trust mode: "tools_policy" (prompts) or "full_auto" (no prompts)
886workspace_trust = "full_auto"
887
888[acp.zed.tools]
889read_file = true
890list_files = true
891
892# Cross-IDE editor context bridge
893[ide_context]
894enabled = true
895inject_into_prompt = true
896show_in_tui = true
897include_selection_text = true
898provider_mode = "auto"
899
900[ide_context.providers.vscode_compatible]
901enabled = true
902
903[ide_context.providers.zed]
904enabled = true
905
906[ide_context.providers.generic]
907enabled = true"#.to_string()
908 }
909
910 #[cfg(feature = "bootstrap")]
911 fn default_vtcode_gitignore() -> String {
912 r#"# Security-focused exclusions
913.env, .env.local, secrets/, .aws/, .ssh/
914
915# Development artifacts
916target/, build/, dist/, node_modules/, vendor/
917
918# Database files
919*.db, *.sqlite, *.sqlite3
920
921# Binary files
922*.exe, *.dll, *.so, *.dylib, *.bin
923
924# IDE files (comprehensive)
925.vscode/, .idea/, *.swp, *.swo
926"#
927 .to_string()
928 }
929
930 #[cfg(feature = "bootstrap")]
931 pub fn create_sample_config<P: AsRef<Path>>(output: P) -> Result<()> {
933 let output = output.as_ref();
934 let config_content = Self::default_vtcode_toml_template();
935
936 fs::write(output, config_content)
937 .with_context(|| format!("Failed to write config file: {}", output.display()))?;
938
939 Ok(())
940 }
941}