1use anyhow::Result;
5use serde::{Deserialize, Serialize};
6use std::fs;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct FeatureFlags {
12 pub enable_mcp_server: bool,
14 pub enable_classic_tree: bool,
15 pub enable_formatters: bool,
16
17 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 pub enable_activity_logging: bool,
26 pub enable_telemetry: bool,
27 pub enable_file_watching: bool,
28 pub enable_auto_context: bool,
29
30 pub enable_tui: bool,
32 pub enable_hooks: bool,
33 pub enable_tips: bool,
34
35 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 pub mcp_tools: McpToolFlags,
43
44 pub privacy_mode: bool,
46 pub disable_external_connections: bool,
47 pub disable_home_directory_access: bool,
48
49 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, Government, Healthcare, Education, Financial, }
77
78impl Default for FeatureFlags {
79 fn default() -> Self {
80 Self {
81 enable_mcp_server: true,
83 enable_classic_tree: true,
84 enable_formatters: true,
85
86 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 enable_activity_logging: false, enable_telemetry: false, enable_file_watching: true,
97 enable_auto_context: true,
98
99 enable_tui: true,
101 enable_hooks: true,
102 enable_tips: true,
103
104 enable_quantum_modes: true,
106 enable_wave_signatures: true,
107 enable_mega_sessions: true,
108 enable_q8_caster: true,
109
110 mcp_tools: McpToolFlags::default(),
112
113 privacy_mode: false,
115 disable_external_connections: false,
116 disable_home_directory_access: false,
117
118 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 pub fn load() -> Result<Self> {
149 let mut flags = Self::default();
150
151 if let Ok(system_flags) = Self::load_from_file("/etc/smart-tree/features.toml") {
153 flags = flags.merge(system_flags);
154 }
155
156 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 if let Ok(local_flags) = Self::load_from_file(".st/features.toml") {
166 flags = flags.merge(local_flags);
167 }
168
169 flags = flags.apply_env_overrides();
171
172 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 fn merge(self, other: Self) -> Self {
187 other
189 }
190
191 fn apply_env_overrides(mut self) -> Self {
193 use std::env;
194
195 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 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 fn apply_compliance_mode(mut self, mode: &ComplianceMode) -> Self {
240 match mode {
241 ComplianceMode::Government => {
242 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 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 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 self.mcp_tools.enable_unified_watcher = false;
271 self.mcp_tools.enable_hooks_management = false;
272 }
273 ComplianceMode::Healthcare => {
274 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 self.enable_activity_logging = false;
284 self.enable_telemetry = false;
285 self.privacy_mode = true;
286 }
287 ComplianceMode::Financial => {
288 self.enable_activity_logging = true; 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 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, }
314 }
315
316 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 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
398static FEATURES: once_cell::sync::Lazy<FeatureFlags> =
400 once_cell::sync::Lazy::new(|| FeatureFlags::load().unwrap_or_default());
401
402pub fn features() -> &'static FeatureFlags {
404 &FEATURES
405}
406
407pub fn is_enabled(feature: &str) -> bool {
409 features().is_enabled(feature)
410}