Skip to main content

vtcode_core/config/
mod.rs

1//! Configuration facade for vtcode-core.
2//!
3//! This module re-exports the extracted `vtcode-config` crate so existing
4//! call sites continue to access configuration types and helpers through
5//! `vtcode_core::config`.
6
7use vtcode_terminal_detection::is_ghostty_terminal;
8
9pub mod acp;
10pub mod api;
11pub mod api_keys;
12pub mod constants;
13pub mod context;
14pub mod core;
15pub mod defaults;
16pub mod hooks;
17pub mod ide_context;
18pub mod loader;
19pub mod mcp;
20pub mod models;
21pub mod output_styles;
22pub mod telemetry;
23pub mod types;
24pub mod validation;
25pub mod validator;
26
27pub use acp::{
28    AgentClientProtocolConfig, AgentClientProtocolTransport, AgentClientProtocolZedConfig,
29    AgentClientProtocolZedToolsConfig, AgentClientProtocolZedWorkspaceTrustMode,
30    WorkspaceTrustLevel,
31};
32pub use api::{
33    ConfigLayerView, ConfigReadRequest, ConfigReadResponse, ConfigService, ConfigWriteRequest,
34    ConfigWriteResponse, ConfigWriteStrategy, ConfigWriteTarget, OverrideMetadata,
35};
36pub use api_keys::ApiKeySources;
37pub use context::{ContextFeaturesConfig, LedgerConfig};
38pub use core::{
39    AgentConfig, AgentOnboardingConfig, AgentPromptSuggestionsConfig, AutoModeConfig,
40    AutoModeEnvironmentConfig, AutomationConfig, CommandsConfig, EditorToolConfig, FullAutoConfig,
41    GatekeeperConfig, ModelConfig, OpenAIPromptCacheKeyMode, PermissionMode, PermissionsConfig,
42    PersistentMemoryConfig, PromptCachingConfig, ProviderPromptCachingConfig, ScheduledTasksConfig,
43    SecurityConfig, ToolPolicy, ToolsConfig, build_openai_prompt_cache_key,
44    tool_call_delay_for_rate, tool_loop_limit_reached,
45};
46pub use core::{PluginRuntimeConfig, PluginTrustLevel};
47pub use defaults::{
48    ConfigDefaultsProvider, ContextStoreDefaults, PerformanceDefaults, ScenarioDefaults,
49    SyntaxHighlightingDefaults, WorkspacePathsDefaults, current_config_defaults, get_config_dir,
50    get_data_dir, install_config_defaults_provider, reset_to_default_config_defaults,
51    with_config_defaults,
52};
53pub use hooks::{
54    HookCommandConfig, HookCommandKind, HookGroupConfig, HooksConfig, LifecycleHooksConfig,
55};
56pub use ide_context::{
57    IdeContextConfig, IdeContextProviderConfig, IdeContextProviderFamily, IdeContextProviderMode,
58    IdeContextProvidersConfig,
59};
60pub use loader::{ConfigManager, SyntaxHighlightingConfig, VTCodeConfig};
61pub use mcp::{
62    McpAllowListConfig, McpAllowListRules, McpClientConfig, McpHttpServerConfig, McpProviderConfig,
63    McpStdioServerConfig, McpTransportConfig, McpUiConfig, McpUiMode,
64};
65pub use models::{ModelId, OpenRouterMetadata};
66pub use telemetry::TelemetryConfig;
67pub use types::{
68    ReasoningEffortLevel, SystemPromptMode, ToolDocumentationMode, UiSurfacePreference,
69    VerbosityLevel,
70};
71pub use validation::{ValidationResult, validate_config, validate_model_exists};
72pub use validator::ConfigValidator;
73pub use vtcode_config::root::{
74    KeyboardProtocolConfig, LayoutModeOverride, PtyConfig, ReasoningDisplayMode, ToolOutputMode,
75    UiConfig, UiDisplayMode,
76};
77pub use vtcode_config::status_line::{StatusLineConfig, StatusLineMode};
78pub use vtcode_config::terminal_title::{DEFAULT_TERMINAL_TITLE_ITEMS, TerminalTitleConfig};
79pub use vtcode_config::{
80    FileOpener, HistoryConfig, HistoryPersistence, NotificationCondition,
81    TerminalNotificationMethod, TuiAlternateScreen, TuiConfig, TuiNotificationEvent,
82    TuiNotificationsConfig,
83};
84pub use vtcode_config::{TimeoutsConfig, resolve_timeout};
85
86/// Convert KeyboardProtocolConfig to KeyboardEnhancementFlags.
87#[cfg(feature = "tui")]
88pub fn keyboard_protocol_to_flags(
89    config: &KeyboardProtocolConfig,
90) -> crossterm::event::KeyboardEnhancementFlags {
91    keyboard_protocol_to_flags_for_terminal(
92        config,
93        cfg!(target_os = "macos"),
94        std::env::var("TERM_PROGRAM").ok().as_deref(),
95        std::env::var("TERM").ok().as_deref(),
96    )
97}
98
99#[cfg(feature = "tui")]
100fn keyboard_protocol_to_flags_for_terminal(
101    config: &KeyboardProtocolConfig,
102    is_macos: bool,
103    term_program: Option<&str>,
104    term: Option<&str>,
105) -> crossterm::event::KeyboardEnhancementFlags {
106    use ratatui::crossterm::event::KeyboardEnhancementFlags;
107
108    if !config.enabled {
109        return KeyboardEnhancementFlags::empty();
110    }
111
112    let mut flags = match config.mode.as_str() {
113        "default" => {
114            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
115                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
116                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
117        }
118        "full" => {
119            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
120                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
121                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
122                | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
123        }
124        "minimal" => KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES,
125        "custom" => {
126            let mut flags = KeyboardEnhancementFlags::empty();
127            if config.disambiguate_escape_codes {
128                flags |= KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES;
129            }
130            if config.report_event_types {
131                flags |= KeyboardEnhancementFlags::REPORT_EVENT_TYPES;
132            }
133            if config.report_alternate_keys {
134                flags |= KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS;
135            }
136            if config.report_all_keys {
137                flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
138            }
139            flags
140        }
141        _ => {
142            tracing::warn!(
143                "Invalid keyboard protocol mode '{}', using default",
144                config.mode
145            );
146            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
147                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
148                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
149        }
150    };
151
152    if should_force_report_all_keys(config.mode.as_str(), is_macos, term_program, term) {
153        flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
154    }
155
156    flags
157}
158
159#[cfg(feature = "tui")]
160fn should_force_report_all_keys(
161    mode: &str,
162    is_macos: bool,
163    term_program: Option<&str>,
164    term: Option<&str>,
165) -> bool {
166    if !is_macos || !matches!(mode, "default") {
167        return false;
168    }
169
170    // Ghostty on macOS needs "report all keys" enabled so bare Command presses
171    // surface as modifier-key events that transcript link clicks can merge in.
172    is_ghostty_terminal(term_program, term)
173}
174
175#[cfg(all(test, feature = "tui"))]
176mod keyboard_protocol_tests {
177    use super::*;
178    use ratatui::crossterm::event::KeyboardEnhancementFlags;
179
180    fn default_keyboard_protocol_config() -> KeyboardProtocolConfig {
181        KeyboardProtocolConfig {
182            enabled: true,
183            mode: "default".to_string(),
184            disambiguate_escape_codes: true,
185            report_event_types: true,
186            report_alternate_keys: true,
187            report_all_keys: false,
188        }
189    }
190
191    #[test]
192    fn test_keyboard_protocol_default_mode() {
193        let flags = keyboard_protocol_to_flags_for_terminal(
194            &default_keyboard_protocol_config(),
195            false,
196            Some("Ghostty"),
197            Some("xterm-ghostty"),
198        );
199
200        assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
201        assert!(flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
202        assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
203        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
204    }
205
206    #[test]
207    fn test_keyboard_protocol_default_mode_enables_all_keys_for_ghostty_on_macos() {
208        let flags = keyboard_protocol_to_flags_for_terminal(
209            &default_keyboard_protocol_config(),
210            true,
211            Some("Ghostty"),
212            Some("xterm-ghostty"),
213        );
214
215        assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
216    }
217
218    #[test]
219    fn test_keyboard_protocol_minimal_mode() {
220        let config = KeyboardProtocolConfig {
221            enabled: true,
222            mode: "minimal".to_string(),
223            disambiguate_escape_codes: true,
224            report_event_types: true,
225            report_alternate_keys: true,
226            report_all_keys: false,
227        };
228
229        let flags = keyboard_protocol_to_flags_for_terminal(
230            &config,
231            true,
232            Some("Ghostty"),
233            Some("xterm-ghostty"),
234        );
235
236        assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
237        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
238        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
239        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
240    }
241
242    #[test]
243    fn test_keyboard_protocol_disabled() {
244        let config = KeyboardProtocolConfig {
245            enabled: false,
246            mode: "default".to_string(),
247            disambiguate_escape_codes: true,
248            report_event_types: true,
249            report_alternate_keys: true,
250            report_all_keys: false,
251        };
252
253        let flags = keyboard_protocol_to_flags_for_terminal(
254            &config,
255            true,
256            Some("Ghostty"),
257            Some("xterm-ghostty"),
258        );
259        assert!(flags.is_empty());
260    }
261
262    #[test]
263    fn test_keyboard_protocol_custom_mode() {
264        let config = KeyboardProtocolConfig {
265            enabled: true,
266            mode: "custom".to_string(),
267            disambiguate_escape_codes: true,
268            report_event_types: false,
269            report_alternate_keys: true,
270            report_all_keys: false,
271        };
272
273        let flags = keyboard_protocol_to_flags_for_terminal(
274            &config,
275            true,
276            Some("Ghostty"),
277            Some("xterm-ghostty"),
278        );
279
280        assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
281        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
282        assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
283        assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
284    }
285
286    #[test]
287    fn test_keyboard_protocol_validation() {
288        let mut config = KeyboardProtocolConfig {
289            enabled: true,
290            mode: "invalid".to_string(),
291            disambiguate_escape_codes: true,
292            report_event_types: true,
293            report_alternate_keys: true,
294            report_all_keys: false,
295        };
296
297        assert!(config.validate().is_err());
298
299        config.mode = "default".to_string();
300        config.validate().unwrap();
301    }
302}