Skip to main content

st/
feature_flags.rs

1// Feature Flags - Enterprise-friendly configuration for Smart Tree
2// "Your tool, your rules!" - Hue
3
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6use std::fs;
7
8/// Feature flags for controlling Smart Tree capabilities
9/// Organizations can disable features via config file or environment variables
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct FeatureFlags {
12    // Core features
13    pub enable_mcp_server: bool,
14    pub enable_classic_tree: bool,
15    pub enable_formatters: bool,
16
17    // AI/ML features
18    pub enable_ai_modes: bool,
19    pub enable_consciousness: bool,
20    pub enable_memory_manager: bool,
21    pub enable_context_absorption: bool,
22    pub enable_smart_search: bool,
23
24    // Data collection features
25    pub enable_activity_logging: bool,
26    pub enable_telemetry: bool,
27    pub enable_file_watching: bool,
28    pub enable_auto_context: bool,
29
30    // Interactive features
31    pub enable_tui: bool,
32    pub enable_hooks: bool,
33    pub enable_tips: bool,
34
35    // Advanced features
36    pub enable_quantum_modes: bool,
37    pub enable_wave_signatures: bool,
38    pub enable_mega_sessions: bool,
39    pub enable_q8_caster: bool,
40
41    // MCP-specific tools (granular control)
42    pub mcp_tools: McpToolFlags,
43
44    // Privacy settings
45    pub privacy_mode: bool,
46    pub disable_external_connections: bool,
47    pub disable_home_directory_access: bool,
48
49    // Compliance settings
50    pub compliance_mode: Option<ComplianceMode>,
51    pub allowed_paths: Vec<String>,
52    pub blocked_paths: Vec<String>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct McpToolFlags {
57    pub enable_find: bool,
58    pub enable_search: bool,
59    pub enable_analyze: bool,
60    pub enable_edit: bool,
61    pub enable_context: bool,
62    pub enable_memory: bool,
63    pub enable_unified_watcher: bool,
64    pub enable_hooks_management: bool,
65    pub enable_sse: bool,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub enum ComplianceMode {
70    None,
71    Enterprise, // Disables most AI features, logging
72    Government, // Maximum restrictions
73    Healthcare, // HIPAA compliance
74    Education,  // FERPA compliance
75    Financial,  // SOC2/PCI compliance
76}
77
78impl Default for FeatureFlags {
79    fn default() -> Self {
80        Self {
81            // Core features - always enabled by default
82            enable_mcp_server: true,
83            enable_classic_tree: true,
84            enable_formatters: true,
85
86            // AI/ML features - can be disabled
87            enable_ai_modes: true,
88            enable_consciousness: true,
89            enable_memory_manager: true,
90            enable_context_absorption: true,
91            enable_smart_search: true,
92
93            // Data collection - respect privacy
94            enable_activity_logging: false, // Opt-in
95            enable_telemetry: false,        // Opt-in
96            enable_file_watching: true,
97            enable_auto_context: true,
98
99            // Interactive features
100            enable_tui: true,
101            enable_hooks: true,
102            enable_tips: true,
103
104            // Advanced features
105            enable_quantum_modes: true,
106            enable_wave_signatures: true,
107            enable_mega_sessions: true,
108            enable_q8_caster: true,
109
110            // MCP tools - all enabled by default
111            mcp_tools: McpToolFlags::default(),
112
113            // Privacy settings
114            privacy_mode: false,
115            disable_external_connections: false,
116            disable_home_directory_access: false,
117
118            // Compliance
119            compliance_mode: None,
120            allowed_paths: vec![],
121            blocked_paths: vec![],
122        }
123    }
124}
125
126impl Default for McpToolFlags {
127    fn default() -> Self {
128        Self {
129            enable_find: true,
130            enable_search: true,
131            enable_analyze: true,
132            enable_edit: true,
133            enable_context: true,
134            enable_memory: true,
135            enable_unified_watcher: true,
136            enable_hooks_management: true,
137            enable_sse: true,
138        }
139    }
140}
141
142impl FeatureFlags {
143    /// Load feature flags from multiple sources (in priority order):
144    /// 1. Environment variables (highest priority)
145    /// 2. Local config file (.st/features.toml)
146    /// 3. System config (/etc/smart-tree/features.toml)
147    /// 4. Default values (lowest priority)
148    pub fn load() -> Result<Self> {
149        let mut flags = Self::default();
150
151        // Try system config
152        if let Ok(system_flags) = Self::load_from_file("/etc/smart-tree/features.toml") {
153            flags = flags.merge(system_flags);
154        }
155
156        // Try user config
157        if let Some(home) = dirs::home_dir() {
158            let user_config = home.join(".st").join("features.toml");
159            if let Ok(user_flags) = Self::load_from_file(user_config) {
160                flags = flags.merge(user_flags);
161            }
162        }
163
164        // Try local config (project-specific)
165        if let Ok(local_flags) = Self::load_from_file(".st/features.toml") {
166            flags = flags.merge(local_flags);
167        }
168
169        // Apply environment variable overrides
170        flags = flags.apply_env_overrides();
171
172        // Apply compliance mode if set
173        if let Some(mode) = flags.compliance_mode.clone() {
174            flags = flags.apply_compliance_mode(&mode);
175        }
176
177        Ok(flags)
178    }
179
180    fn load_from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
181        let content = fs::read_to_string(path)?;
182        Ok(toml::from_str(&content)?)
183    }
184
185    /// Merge with another set of flags (other takes priority)
186    fn merge(self, other: Self) -> Self {
187        // This is simplified - in production you'd merge field by field
188        other
189    }
190
191    /// Apply environment variable overrides
192    fn apply_env_overrides(mut self) -> Self {
193        use std::env;
194
195        // Check for specific feature flags
196        if env::var("ST_DISABLE_MCP").is_ok() {
197            self.enable_mcp_server = false;
198        }
199
200        if env::var("ST_DISABLE_AI").is_ok() {
201            self.enable_ai_modes = false;
202            self.enable_consciousness = false;
203            self.enable_memory_manager = false;
204        }
205
206        if env::var("ST_DISABLE_LOGGING").is_ok() {
207            self.enable_activity_logging = false;
208        }
209
210        if env::var("ST_DISABLE_WATCHING").is_ok() {
211            self.enable_file_watching = false;
212            self.enable_context_absorption = false;
213            self.mcp_tools.enable_unified_watcher = false;
214        }
215
216        if env::var("ST_PRIVACY_MODE").is_ok() {
217            self.privacy_mode = true;
218            self.enable_telemetry = false;
219            self.enable_activity_logging = false;
220            self.disable_external_connections = true;
221        }
222
223        // Compliance mode from environment
224        if let Ok(mode) = env::var("ST_COMPLIANCE_MODE") {
225            self.compliance_mode = match mode.to_lowercase().as_str() {
226                "enterprise" => Some(ComplianceMode::Enterprise),
227                "government" => Some(ComplianceMode::Government),
228                "healthcare" => Some(ComplianceMode::Healthcare),
229                "education" => Some(ComplianceMode::Education),
230                "financial" => Some(ComplianceMode::Financial),
231                _ => None,
232            };
233        }
234
235        self
236    }
237
238    /// Apply compliance mode restrictions
239    fn apply_compliance_mode(mut self, mode: &ComplianceMode) -> Self {
240        match mode {
241            ComplianceMode::Government => {
242                // Maximum restrictions for government use
243                self.enable_consciousness = false;
244                self.enable_memory_manager = false;
245                self.enable_context_absorption = false;
246                self.enable_activity_logging = false;
247                self.enable_telemetry = false;
248                self.enable_file_watching = false;
249                self.enable_auto_context = false;
250                self.enable_hooks = false;
251                self.disable_external_connections = true;
252                self.disable_home_directory_access = true;
253                self.privacy_mode = true;
254
255                // Disable most MCP tools
256                self.mcp_tools.enable_edit = false;
257                self.mcp_tools.enable_memory = false;
258                self.mcp_tools.enable_unified_watcher = false;
259                self.mcp_tools.enable_hooks_management = false;
260            }
261            ComplianceMode::Enterprise => {
262                // Moderate restrictions for enterprise
263                self.enable_consciousness = false;
264                self.enable_memory_manager = false;
265                self.enable_activity_logging = false;
266                self.enable_telemetry = false;
267                self.privacy_mode = true;
268
269                // Disable some MCP tools
270                self.mcp_tools.enable_unified_watcher = false;
271                self.mcp_tools.enable_hooks_management = false;
272            }
273            ComplianceMode::Healthcare => {
274                // HIPAA compliance
275                self.enable_activity_logging = false;
276                self.enable_telemetry = false;
277                self.enable_context_absorption = false;
278                self.privacy_mode = true;
279                self.disable_home_directory_access = true;
280            }
281            ComplianceMode::Education => {
282                // FERPA compliance
283                self.enable_activity_logging = false;
284                self.enable_telemetry = false;
285                self.privacy_mode = true;
286            }
287            ComplianceMode::Financial => {
288                // SOC2/PCI compliance
289                self.enable_activity_logging = true; // Required for audit
290                self.enable_telemetry = false;
291                self.privacy_mode = true;
292                self.enable_context_absorption = false;
293            }
294            ComplianceMode::None => {}
295        }
296
297        self
298    }
299
300    /// Check if a feature is enabled
301    pub fn is_enabled(&self, feature: &str) -> bool {
302        match feature {
303            "mcp" => self.enable_mcp_server,
304            "ai" => self.enable_ai_modes,
305            "consciousness" => self.enable_consciousness,
306            "memory" => self.enable_memory_manager,
307            "absorption" => self.enable_context_absorption,
308            "logging" => self.enable_activity_logging,
309            "watching" => self.enable_file_watching,
310            "hooks" => self.enable_hooks,
311            "tui" => self.enable_tui,
312            _ => true, // Unknown features default to enabled
313        }
314    }
315
316    /// Get filtered MCP tools based on flags
317    pub fn get_enabled_mcp_tools(&self) -> Vec<String> {
318        let mut tools = Vec::new();
319
320        if self.mcp_tools.enable_find {
321            tools.push("find".to_string());
322        }
323        if self.mcp_tools.enable_search {
324            tools.push("search".to_string());
325        }
326        if self.mcp_tools.enable_analyze {
327            tools.push("analyze".to_string());
328        }
329        if self.mcp_tools.enable_edit {
330            tools.push("edit".to_string());
331        }
332        if self.mcp_tools.enable_context {
333            tools.push("context".to_string());
334        }
335        if self.mcp_tools.enable_memory {
336            tools.push("memory".to_string());
337        }
338        if self.mcp_tools.enable_unified_watcher {
339            tools.push("unified_watcher".to_string());
340        }
341        if self.mcp_tools.enable_hooks_management {
342            tools.push("hooks".to_string());
343        }
344        if self.mcp_tools.enable_sse {
345            tools.push("sse".to_string());
346        }
347
348        tools
349    }
350
351    /// Generate a features report
352    pub fn generate_report(&self) -> String {
353        let mut report = String::from("Smart Tree Feature Configuration\n");
354        report.push_str("================================\n\n");
355
356        if let Some(ref mode) = self.compliance_mode {
357            report.push_str(&format!("Compliance Mode: {:?}\n\n", mode));
358        }
359
360        report.push_str("Core Features:\n");
361        report.push_str(&format!("  MCP Server: {}\n", self.enable_mcp_server));
362        report.push_str(&format!("  Classic Tree: {}\n", self.enable_classic_tree));
363        report.push_str(&format!("  Formatters: {}\n\n", self.enable_formatters));
364
365        report.push_str("AI/ML Features:\n");
366        report.push_str(&format!("  AI Modes: {}\n", self.enable_ai_modes));
367        report.push_str(&format!("  Consciousness: {}\n", self.enable_consciousness));
368        report.push_str(&format!(
369            "  Memory Manager: {}\n",
370            self.enable_memory_manager
371        ));
372        report.push_str(&format!(
373            "  Context Absorption: {}\n",
374            self.enable_context_absorption
375        ));
376        report.push_str(&format!("  Smart Search: {}\n\n", self.enable_smart_search));
377
378        report.push_str("Privacy Settings:\n");
379        report.push_str(&format!("  Privacy Mode: {}\n", self.privacy_mode));
380        report.push_str(&format!(
381            "  Activity Logging: {}\n",
382            self.enable_activity_logging
383        ));
384        report.push_str(&format!("  Telemetry: {}\n", self.enable_telemetry));
385        report.push_str(&format!(
386            "  External Connections: {}\n",
387            !self.disable_external_connections
388        ));
389        report.push_str(&format!(
390            "  Home Directory Access: {}\n",
391            !self.disable_home_directory_access
392        ));
393
394        report
395    }
396}
397
398/// Global feature flags instance
399static FEATURES: once_cell::sync::Lazy<FeatureFlags> =
400    once_cell::sync::Lazy::new(|| FeatureFlags::load().unwrap_or_default());
401
402/// Get the global feature flags
403pub fn features() -> &'static FeatureFlags {
404    &FEATURES
405}
406
407/// Check if a feature is enabled (convenience function)
408pub fn is_enabled(feature: &str) -> bool {
409    features().is_enabled(feature)
410}