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