grok-cli-acp 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.env.example +42 -0
  2. package/.github/workflows/ci.yml +30 -0
  3. package/.github/workflows/rust.yml +22 -0
  4. package/.grok/.env.example +85 -0
  5. package/.grok/COMPLETE_FIX_SUMMARY.md +466 -0
  6. package/.grok/ENV_CONFIG_GUIDE.md +173 -0
  7. package/.grok/QUICK_REFERENCE.md +180 -0
  8. package/.grok/README.md +104 -0
  9. package/.grok/TESTING_GUIDE.md +393 -0
  10. package/CHANGELOG.md +465 -0
  11. package/CODE_REVIEW_SUMMARY.md +414 -0
  12. package/COMPLETE_FIX_SUMMARY.md +415 -0
  13. package/CONFIGURATION.md +489 -0
  14. package/CONTEXT_FILES_GUIDE.md +419 -0
  15. package/CONTRIBUTING.md +55 -0
  16. package/CURSOR_POSITION_FIX.md +206 -0
  17. package/Cargo.toml +88 -0
  18. package/ERROR_HANDLING_REPORT.md +361 -0
  19. package/FINAL_FIX_SUMMARY.md +462 -0
  20. package/FIXES.md +37 -0
  21. package/FIXES_SUMMARY.md +87 -0
  22. package/GROK_API_MIGRATION_SUMMARY.md +111 -0
  23. package/LICENSE +22 -0
  24. package/MIGRATION_TO_GROK_API.md +223 -0
  25. package/README.md +504 -0
  26. package/REVIEW_COMPLETE.md +416 -0
  27. package/REVIEW_QUICK_REFERENCE.md +173 -0
  28. package/SECURITY.md +463 -0
  29. package/SECURITY_AUDIT.md +661 -0
  30. package/SETUP.md +287 -0
  31. package/TESTING_TOOLS.md +88 -0
  32. package/TESTING_TOOL_EXECUTION.md +239 -0
  33. package/TOOL_EXECUTION_FIX.md +491 -0
  34. package/VERIFICATION_CHECKLIST.md +419 -0
  35. package/docs/API.md +74 -0
  36. package/docs/CHAT_LOGGING.md +39 -0
  37. package/docs/CURSOR_FIX_DEMO.md +306 -0
  38. package/docs/ERROR_HANDLING_GUIDE.md +547 -0
  39. package/docs/FILE_OPERATIONS.md +449 -0
  40. package/docs/INTERACTIVE.md +401 -0
  41. package/docs/PROJECT_CREATION_GUIDE.md +570 -0
  42. package/docs/QUICKSTART.md +378 -0
  43. package/docs/QUICK_REFERENCE.md +691 -0
  44. package/docs/RELEASE_NOTES_0.1.2.md +240 -0
  45. package/docs/TOOLS.md +459 -0
  46. package/docs/TOOLS_QUICK_REFERENCE.md +210 -0
  47. package/docs/ZED_INTEGRATION.md +371 -0
  48. package/docs/extensions.md +464 -0
  49. package/docs/settings.md +293 -0
  50. package/examples/extensions/logging-hook/README.md +91 -0
  51. package/examples/extensions/logging-hook/extension.json +22 -0
  52. package/package.json +30 -0
  53. package/scripts/test_acp.py +252 -0
  54. package/scripts/test_acp.sh +143 -0
  55. package/scripts/test_acp_simple.sh +72 -0
  56. package/src/acp/mod.rs +741 -0
  57. package/src/acp/protocol.rs +323 -0
  58. package/src/acp/security.rs +298 -0
  59. package/src/acp/tools.rs +697 -0
  60. package/src/bin/banner_demo.rs +216 -0
  61. package/src/bin/docgen.rs +18 -0
  62. package/src/bin/installer.rs +217 -0
  63. package/src/cli/app.rs +310 -0
  64. package/src/cli/commands/acp.rs +721 -0
  65. package/src/cli/commands/chat.rs +485 -0
  66. package/src/cli/commands/code.rs +513 -0
  67. package/src/cli/commands/config.rs +394 -0
  68. package/src/cli/commands/health.rs +442 -0
  69. package/src/cli/commands/history.rs +421 -0
  70. package/src/cli/commands/mod.rs +14 -0
  71. package/src/cli/commands/settings.rs +1384 -0
  72. package/src/cli/mod.rs +166 -0
  73. package/src/config/mod.rs +2212 -0
  74. package/src/display/ascii_art.rs +139 -0
  75. package/src/display/banner.rs +289 -0
  76. package/src/display/components/input.rs +323 -0
  77. package/src/display/components/mod.rs +2 -0
  78. package/src/display/components/settings_list.rs +306 -0
  79. package/src/display/interactive.rs +1255 -0
  80. package/src/display/mod.rs +62 -0
  81. package/src/display/terminal.rs +42 -0
  82. package/src/display/tips.rs +316 -0
  83. package/src/grok_client_ext.rs +177 -0
  84. package/src/hooks/loader.rs +407 -0
  85. package/src/hooks/mod.rs +158 -0
  86. package/src/lib.rs +174 -0
  87. package/src/main.rs +65 -0
  88. package/src/mcp/client.rs +195 -0
  89. package/src/mcp/config.rs +20 -0
  90. package/src/mcp/mod.rs +6 -0
  91. package/src/mcp/protocol.rs +67 -0
  92. package/src/utils/auth.rs +41 -0
  93. package/src/utils/chat_logger.rs +568 -0
  94. package/src/utils/context.rs +390 -0
  95. package/src/utils/mod.rs +16 -0
  96. package/src/utils/network.rs +320 -0
  97. package/src/utils/rate_limiter.rs +166 -0
  98. package/src/utils/session.rs +73 -0
  99. package/src/utils/shell_permissions.rs +389 -0
  100. package/src/utils/telemetry.rs +41 -0
@@ -0,0 +1,1384 @@
1
+ //! Settings command handler for grok-cli
2
+ //!
3
+ //! Provides an interactive settings interface similar to Gemini CLI's `/settings` command.
4
+ //! Allows users to browse and modify configuration settings in categories.
5
+
6
+ use anyhow::{anyhow, Result};
7
+ use colored::*;
8
+ use std::io::{self, Write};
9
+
10
+ use crate::cli::{confirm, print_error, print_info, print_success, print_warning};
11
+ use crate::config::Config;
12
+ use crate::SettingsAction;
13
+
14
+ /// Setting definition for interactive display
15
+ #[derive(Debug, Clone)]
16
+ pub struct SettingDefinition {
17
+ pub key: String,
18
+ pub label: String,
19
+ pub description: String,
20
+ pub category: String,
21
+ pub setting_type: SettingType,
22
+ pub default_value: String,
23
+ pub requires_restart: bool,
24
+ pub current_value: String,
25
+ }
26
+
27
+ #[derive(Debug, Clone)]
28
+ pub enum SettingType {
29
+ Boolean,
30
+ String,
31
+ Number,
32
+ #[allow(dead_code)]
33
+ Array,
34
+ #[allow(dead_code)]
35
+ Object,
36
+ }
37
+
38
+ /// Handle settings-related commands
39
+ pub async fn handle_settings_action(action: SettingsAction, config: &Config) -> Result<()> {
40
+ match action {
41
+ SettingsAction::Show | SettingsAction::Edit => {
42
+ let settings = get_all_settings(config);
43
+ crate::display::components::settings_list::run_settings_tui(config, settings).await
44
+ }
45
+ SettingsAction::Reset { category } => reset_settings(category).await,
46
+ SettingsAction::Export { path } => export_settings(config, path).await,
47
+ SettingsAction::Import { path } => import_settings(path).await,
48
+ }
49
+ }
50
+
51
+ /// Show interactive settings UI (Legacy - now forwarded to TUI)
52
+ #[allow(dead_code)]
53
+ async fn show_settings_ui(config: &Config) -> Result<()> {
54
+ println!("{}", "⚙️ Grok CLI Settings".cyan().bold());
55
+ println!("{}", "━━━━━━━━━━━━━━━━━━━━━━".cyan());
56
+ println!();
57
+
58
+ let settings = get_all_settings(config);
59
+ let categories = get_categories(&settings);
60
+
61
+ loop {
62
+ println!("{}", "Categories:".green().bold());
63
+ for (i, category) in categories.iter().enumerate() {
64
+ println!(" {}. {}", (i + 1).to_string().yellow(), category);
65
+ }
66
+ println!();
67
+ println!(" {}. Quit", "q".red());
68
+ println!();
69
+
70
+ print!("{} ", "Select category (1-{}, q): ".blue().bold());
71
+ io::stdout().flush()?;
72
+
73
+ let mut input = String::new();
74
+ io::stdin().read_line(&mut input)?;
75
+ let input = input.trim();
76
+
77
+ if input == "q" || input == "quit" {
78
+ break;
79
+ }
80
+
81
+ if let Ok(choice) = input.parse::<usize>() {
82
+ if choice > 0 && choice <= categories.len() {
83
+ let category = &categories[choice - 1];
84
+ show_category_settings(category, &settings, config).await?;
85
+ } else {
86
+ print_warning("Invalid selection. Please try again.");
87
+ }
88
+ } else {
89
+ print_warning("Invalid input. Please enter a number or 'q'.");
90
+ }
91
+ println!();
92
+ }
93
+
94
+ print_info("Settings browser closed.");
95
+ Ok(())
96
+ }
97
+
98
+ /// Show settings for a specific category
99
+ async fn show_category_settings(
100
+ category: &str,
101
+ settings: &[SettingDefinition],
102
+ config: &Config,
103
+ ) -> Result<()> {
104
+ let category_settings: Vec<_> = settings.iter().filter(|s| s.category == category).collect();
105
+
106
+ println!();
107
+ println!("{}", format!("⚙️ {} Settings", category).cyan().bold());
108
+ println!("{}", "━".repeat(50).cyan());
109
+ println!();
110
+
111
+ for setting in &category_settings {
112
+ display_setting(setting);
113
+ println!();
114
+ }
115
+
116
+ loop {
117
+ println!("{}", "Options:".green().bold());
118
+ println!(" 1. Edit a setting");
119
+ println!(" 2. Reset category to defaults");
120
+ println!(" 3. Back to categories");
121
+ println!();
122
+
123
+ print!("{} ", "Choose option (1-3): ".blue().bold());
124
+ io::stdout().flush()?;
125
+
126
+ let mut input = String::new();
127
+ io::stdin().read_line(&mut input)?;
128
+
129
+ match input.trim() {
130
+ "1" => {
131
+ edit_category_setting(&category_settings, config).await?;
132
+ }
133
+ "2" => {
134
+ if confirm(&format!("Reset all {} settings to defaults?", category))? {
135
+ reset_settings(Some(category.to_lowercase())).await?;
136
+ // Note: This won't refresh the current view's values unless we reload config,
137
+ // but for simplicity we just return to category menu which is acceptable.
138
+ break;
139
+ }
140
+ }
141
+ "3" => break,
142
+ _ => print_warning("Invalid selection. Please try again."),
143
+ }
144
+ println!();
145
+ }
146
+
147
+ Ok(())
148
+ }
149
+
150
+ /// Display a single setting
151
+ fn display_setting(setting: &SettingDefinition) {
152
+ println!("{}", setting.label.green().bold());
153
+ println!(" Key: {}", setting.key.yellow());
154
+ println!(
155
+ " Current: {}",
156
+ format_setting_value(&setting.current_value, &setting.setting_type)
157
+ );
158
+ println!(
159
+ " Default: {}",
160
+ format_setting_value(&setting.default_value, &setting.setting_type)
161
+ );
162
+ println!(" Description: {}", setting.description.dimmed());
163
+
164
+ if setting.requires_restart {
165
+ println!(" {}", "⚠️ Requires restart".yellow());
166
+ }
167
+ }
168
+
169
+ /// Format setting value for display
170
+ pub fn format_setting_value(value: &str, setting_type: &SettingType) -> colored::ColoredString {
171
+ match setting_type {
172
+ SettingType::Boolean => {
173
+ if value == "true" {
174
+ "✓ Enabled".green()
175
+ } else {
176
+ "✗ Disabled".red()
177
+ }
178
+ }
179
+ SettingType::String => {
180
+ if value.is_empty() {
181
+ "\"\" (empty)".dimmed()
182
+ } else {
183
+ format!("\"{}\"", value).cyan()
184
+ }
185
+ }
186
+ SettingType::Number => value.blue(),
187
+ SettingType::Array => {
188
+ if value == "[]" {
189
+ "[] (empty)".dimmed()
190
+ } else {
191
+ value.magenta()
192
+ }
193
+ }
194
+ SettingType::Object => {
195
+ if value == "{{}}" {
196
+ "{{}} (empty)".dimmed()
197
+ } else {
198
+ value.magenta()
199
+ }
200
+ }
201
+ }
202
+ }
203
+
204
+ /// Edit a setting in a category
205
+ pub async fn edit_category_setting(settings: &[&SettingDefinition], config: &Config) -> Result<()> {
206
+ println!();
207
+ println!("{}", "Select setting to edit:".green().bold());
208
+
209
+ for (i, setting) in settings.iter().enumerate() {
210
+ println!(" {}. {}", (i + 1).to_string().yellow(), setting.label);
211
+ }
212
+ println!();
213
+
214
+ print!("{} ", "Select setting (1-{}): ".blue().bold());
215
+ io::stdout().flush()?;
216
+
217
+ let mut input = String::new();
218
+ io::stdin().read_line(&mut input)?;
219
+
220
+ if let Ok(choice) = input.trim().parse::<usize>() {
221
+ if choice > 0 && choice <= settings.len() {
222
+ let setting = settings[choice - 1];
223
+ edit_single_setting(setting, config).await?;
224
+ } else {
225
+ print_warning("Invalid selection.");
226
+ }
227
+ }
228
+
229
+ Ok(())
230
+ }
231
+
232
+ /// Edit a single setting
233
+ pub async fn edit_single_setting(setting: &SettingDefinition, config: &Config) -> Result<()> {
234
+ println!();
235
+ println!("{}", format!("Editing: {}", setting.label).green().bold());
236
+ println!(
237
+ "Current value: {}",
238
+ format_setting_value(&setting.current_value, &setting.setting_type)
239
+ );
240
+ println!("Description: {}", setting.description.dimmed());
241
+ println!();
242
+
243
+ let new_value = match setting.setting_type {
244
+ SettingType::Boolean => edit_boolean_setting(setting)?,
245
+ SettingType::String => edit_string_setting(setting)?,
246
+ SettingType::Number => edit_number_setting(setting)?,
247
+ SettingType::Array => edit_array_setting(setting)?,
248
+ SettingType::Object => {
249
+ print_warning("Object settings must be edited manually in the config file.");
250
+ return Ok(());
251
+ }
252
+ };
253
+
254
+ if new_value != setting.current_value {
255
+ // Apply the setting
256
+ let mut updated_config = config.clone();
257
+ updated_config
258
+ .set_value(&setting.key, &new_value)
259
+ .map_err(|e| anyhow!("Failed to set value: {}", e))?;
260
+
261
+ // Validate the change
262
+ updated_config.validate()?;
263
+
264
+ // Save the config
265
+ updated_config.save(None).await?;
266
+
267
+ print_success(&format!("Updated {} = {}", setting.key, new_value));
268
+
269
+ if setting.requires_restart {
270
+ print_warning("⚠️ This setting requires a restart to take effect.");
271
+ }
272
+ } else {
273
+ print_info("No changes made.");
274
+ }
275
+
276
+ Ok(())
277
+ }
278
+
279
+ /// Edit a boolean setting
280
+ fn edit_boolean_setting(setting: &SettingDefinition) -> Result<String> {
281
+ let _current = setting.current_value == "true";
282
+
283
+ println!("Options:");
284
+ println!(" 1. Enable (true)");
285
+ println!(" 2. Disable (false)");
286
+ println!(" 3. Keep current value");
287
+ println!();
288
+
289
+ print!("{} ", "Choose option (1-3): ".blue().bold());
290
+ io::stdout().flush()?;
291
+
292
+ let mut input = String::new();
293
+ io::stdin().read_line(&mut input)?;
294
+
295
+ match input.trim() {
296
+ "1" => Ok("true".to_string()),
297
+ "2" => Ok("false".to_string()),
298
+ "3" => Ok(setting.current_value.clone()),
299
+ _ => {
300
+ print_warning("Invalid selection, keeping current value.");
301
+ Ok(setting.current_value.clone())
302
+ }
303
+ }
304
+ }
305
+
306
+ /// Edit a string setting
307
+ fn edit_string_setting(setting: &SettingDefinition) -> Result<String> {
308
+ print!("Enter new value (press Enter to keep current): ");
309
+ io::stdout().flush()?;
310
+
311
+ let mut input = String::new();
312
+ io::stdin().read_line(&mut input)?;
313
+ let input = input.trim();
314
+
315
+ if input.is_empty() {
316
+ Ok(setting.current_value.clone())
317
+ } else {
318
+ Ok(input.to_string())
319
+ }
320
+ }
321
+
322
+ /// Edit a number setting
323
+ fn edit_number_setting(setting: &SettingDefinition) -> Result<String> {
324
+ print!("Enter new value (press Enter to keep current): ");
325
+ io::stdout().flush()?;
326
+
327
+ let mut input = String::new();
328
+ io::stdin().read_line(&mut input)?;
329
+ let input = input.trim();
330
+
331
+ if input.is_empty() {
332
+ Ok(setting.current_value.clone())
333
+ } else {
334
+ // Validate it's a number
335
+ if input.parse::<f64>().is_ok() {
336
+ Ok(input.to_string())
337
+ } else {
338
+ print_warning("Invalid number format, keeping current value.");
339
+ Ok(setting.current_value.clone())
340
+ }
341
+ }
342
+ }
343
+
344
+ /// Edit an array setting
345
+ fn edit_array_setting(setting: &SettingDefinition) -> Result<String> {
346
+ println!("Array editing:");
347
+ println!("Enter comma-separated values (e.g., 'value1,value2,value3')");
348
+ println!("Press Enter to keep current value");
349
+ print!("New values: ");
350
+ io::stdout().flush()?;
351
+
352
+ let mut input = String::new();
353
+ io::stdin().read_line(&mut input)?;
354
+ let input = input.trim();
355
+
356
+ if input.is_empty() {
357
+ Ok(setting.current_value.clone())
358
+ } else {
359
+ // Convert comma-separated values to JSON array format
360
+ let values: Vec<String> = input
361
+ .split(',')
362
+ .map(|s| format!("\"{}\"", s.trim()))
363
+ .collect();
364
+ Ok(format!(
365
+ "[{}]
366
+ ",
367
+ values.join(",")
368
+ ))
369
+ }
370
+ }
371
+
372
+ /// Launch interactive settings editor
373
+ async fn edit_settings_interactive(config: &Config) -> Result<()> {
374
+ println!("{}", "🔧 Interactive Settings Editor".cyan().bold());
375
+ println!("{}", "Press Ctrl+C at any time to exit".dimmed());
376
+ println!();
377
+
378
+ show_settings_ui(config).await
379
+ }
380
+
381
+ /// Reset settings to defaults
382
+ async fn reset_settings(category: Option<String>) -> Result<()> {
383
+ match category {
384
+ Some(cat) => {
385
+ print_info(&format!("Resetting {} settings to defaults...", cat));
386
+
387
+ if !confirm(&format!(
388
+ "This will reset all {} settings to their default values. Continue?",
389
+ cat
390
+ ))? {
391
+ print_info("Reset cancelled.");
392
+ return Ok(());
393
+ }
394
+
395
+ // Load default config and save only the specified category
396
+ let default_config = Config::default();
397
+ let mut current_config = Config::load(None).await?;
398
+
399
+ // Reset the specific category
400
+ match cat.to_lowercase().as_str() {
401
+ "general" => current_config.general = default_config.general,
402
+ "ui" => current_config.ui = default_config.ui,
403
+ "model" => current_config.model = default_config.model,
404
+ "context" => current_config.context = default_config.context,
405
+ "tools" => current_config.tools = default_config.tools,
406
+ "security" => current_config.security = default_config.security,
407
+ "experimental" => current_config.experimental = default_config.experimental,
408
+ "acp" => current_config.acp = default_config.acp,
409
+ "network" => current_config.network = default_config.network,
410
+ "logging" => current_config.logging = default_config.logging,
411
+ _ => {
412
+ print_error(&format!("Unknown category: {}", cat));
413
+ return Err(anyhow!("Unknown category: {}", cat));
414
+ }
415
+ }
416
+
417
+ current_config.save(None).await?;
418
+ print_success(&format!("{} settings reset to defaults.", cat));
419
+ }
420
+ None => {
421
+ print_info("Resetting ALL settings to defaults...");
422
+
423
+ if !confirm("This will reset ALL settings to their default values. Continue?")? {
424
+ print_info("Reset cancelled.");
425
+ return Ok(());
426
+ }
427
+
428
+ Config::init(true).await?;
429
+ print_success("All settings reset to defaults.");
430
+ }
431
+ }
432
+
433
+ Ok(())
434
+ }
435
+
436
+ /// Export settings to a file
437
+ async fn export_settings(config: &Config, path: Option<String>) -> Result<()> {
438
+ let export_path = path.unwrap_or_else(|| "grok-settings-export.toml".to_string());
439
+
440
+ print_info(&format!("Exporting settings to: {}", export_path));
441
+
442
+ let toml_content =
443
+ toml::to_string_pretty(config).map_err(|e| anyhow!("Failed to serialize config: {}", e))?;
444
+
445
+ std::fs::write(&export_path, toml_content)
446
+ .map_err(|e| anyhow!("Failed to write export file: {}", e))?;
447
+
448
+ print_success(&format!("Settings exported to: {}", export_path));
449
+ Ok(())
450
+ }
451
+
452
+ /// Import settings from a file
453
+ async fn import_settings(path: String) -> Result<()> {
454
+ print_info(&format!("Importing settings from: {}", path));
455
+
456
+ if !std::path::Path::new(&path).exists() {
457
+ return Err(anyhow!("Import file not found: {}", path));
458
+ }
459
+
460
+ let content =
461
+ std::fs::read_to_string(&path).map_err(|e| anyhow!("Failed to read import file: {}", e))?;
462
+
463
+ let imported_config: Config =
464
+ toml::from_str(&content).map_err(|e| anyhow!("Failed to parse import file: {}", e))?;
465
+
466
+ // Validate imported config
467
+ imported_config.validate()?;
468
+
469
+ if confirm("This will replace your current settings. Continue?")? {
470
+ imported_config.save(None).await?;
471
+ print_success("Settings imported successfully.");
472
+ } else {
473
+ print_info("Import cancelled.");
474
+ }
475
+
476
+ Ok(())
477
+ }
478
+
479
+ /// Get all settings definitions
480
+ pub fn get_all_settings(config: &Config) -> Vec<SettingDefinition> {
481
+ let mut settings = Vec::new();
482
+
483
+ // General settings
484
+ settings.extend(vec![
485
+ SettingDefinition {
486
+ key: "general.preview_features".to_string(),
487
+ label: "Preview Features".to_string(),
488
+ description: "Enable preview features (e.g., preview models)".to_string(),
489
+ category: "General".to_string(),
490
+ setting_type: SettingType::Boolean,
491
+ default_value: "false".to_string(),
492
+ requires_restart: false,
493
+ current_value: config.general.preview_features.to_string(),
494
+ },
495
+ SettingDefinition {
496
+ key: "general.vim_mode".to_string(),
497
+ label: "Vim Mode".to_string(),
498
+ description: "Enable Vim keybindings".to_string(),
499
+ category: "General".to_string(),
500
+ setting_type: SettingType::Boolean,
501
+ default_value: "false".to_string(),
502
+ requires_restart: true,
503
+ current_value: config.general.vim_mode.to_string(),
504
+ },
505
+ SettingDefinition {
506
+ key: "general.disable_auto_update".to_string(),
507
+ label: "Disable Auto Update".to_string(),
508
+ description: "Disable automatic updates".to_string(),
509
+ category: "General".to_string(),
510
+ setting_type: SettingType::Boolean,
511
+ default_value: "false".to_string(),
512
+ requires_restart: false,
513
+ current_value: config.general.disable_auto_update.to_string(),
514
+ },
515
+ SettingDefinition {
516
+ key: "general.disable_update_nag".to_string(),
517
+ label: "Disable Update Nag".to_string(),
518
+ description: "Hide update notification messages".to_string(),
519
+ category: "General".to_string(),
520
+ setting_type: SettingType::Boolean,
521
+ default_value: "false".to_string(),
522
+ requires_restart: false,
523
+ current_value: config.general.disable_update_nag.to_string(),
524
+ },
525
+ SettingDefinition {
526
+ key: "general.enable_prompt_completion".to_string(),
527
+ label: "Prompt Completion".to_string(),
528
+ description: "Enable AI-powered prompt completion while typing".to_string(),
529
+ category: "General".to_string(),
530
+ setting_type: SettingType::Boolean,
531
+ default_value: "false".to_string(),
532
+ requires_restart: false,
533
+ current_value: config.general.enable_prompt_completion.to_string(),
534
+ },
535
+ SettingDefinition {
536
+ key: "general.retry_fetch_errors".to_string(),
537
+ label: "Retry Fetch Errors".to_string(),
538
+ description: "Automatically retry failed network requests".to_string(),
539
+ category: "General".to_string(),
540
+ setting_type: SettingType::Boolean,
541
+ default_value: "false".to_string(),
542
+ requires_restart: false,
543
+ current_value: config.general.retry_fetch_errors.to_string(),
544
+ },
545
+ SettingDefinition {
546
+ key: "general.debug_keystroke_logging".to_string(),
547
+ label: "Debug Keystroke".to_string(),
548
+ description: "Log keystrokes for debugging purposes".to_string(),
549
+ category: "General".to_string(),
550
+ setting_type: SettingType::Boolean,
551
+ default_value: "false".to_string(),
552
+ requires_restart: false,
553
+ current_value: config.general.debug_keystroke_logging.to_string(),
554
+ },
555
+ ]);
556
+
557
+ // UI settings
558
+ settings.extend(vec![
559
+ SettingDefinition {
560
+ key: "ui.theme".to_string(),
561
+ label: "Theme".to_string(),
562
+ description: "Color theme for the UI".to_string(),
563
+ category: "UI".to_string(),
564
+ setting_type: SettingType::String,
565
+ default_value: "default".to_string(),
566
+ requires_restart: false,
567
+ current_value: config.ui.theme.clone(),
568
+ },
569
+ SettingDefinition {
570
+ key: "ui.colors".to_string(),
571
+ label: "Enable Colors".to_string(),
572
+ description: "Enable colored output in terminal".to_string(),
573
+ category: "UI".to_string(),
574
+ setting_type: SettingType::Boolean,
575
+ default_value: "true".to_string(),
576
+ requires_restart: false,
577
+ current_value: config.ui.colors.to_string(),
578
+ },
579
+ SettingDefinition {
580
+ key: "ui.progress_bars".to_string(),
581
+ label: "Progress Bars".to_string(),
582
+ description: "Show progress indicators during operations".to_string(),
583
+ category: "UI".to_string(),
584
+ setting_type: SettingType::Boolean,
585
+ default_value: "true".to_string(),
586
+ requires_restart: false,
587
+ current_value: config.ui.progress_bars.to_string(),
588
+ },
589
+ SettingDefinition {
590
+ key: "ui.verbose_errors".to_string(),
591
+ label: "Verbose Errors".to_string(),
592
+ description: "Display detailed error information".to_string(),
593
+ category: "UI".to_string(),
594
+ setting_type: SettingType::Boolean,
595
+ default_value: "false".to_string(),
596
+ requires_restart: false,
597
+ current_value: config.ui.verbose_errors.to_string(),
598
+ },
599
+ SettingDefinition {
600
+ key: "ui.terminal_width".to_string(),
601
+ label: "Terminal Width".to_string(),
602
+ description: "Terminal width override (0 = auto-detect)".to_string(),
603
+ category: "UI".to_string(),
604
+ setting_type: SettingType::Number,
605
+ default_value: "0".to_string(),
606
+ requires_restart: false,
607
+ current_value: config.ui.terminal_width.to_string(),
608
+ },
609
+ SettingDefinition {
610
+ key: "ui.unicode".to_string(),
611
+ label: "Unicode Support".to_string(),
612
+ description: "Enable Unicode characters and emojis".to_string(),
613
+ category: "UI".to_string(),
614
+ setting_type: SettingType::Boolean,
615
+ default_value: "true".to_string(),
616
+ requires_restart: false,
617
+ current_value: config.ui.unicode.to_string(),
618
+ },
619
+ SettingDefinition {
620
+ key: "ui.hide_window_title".to_string(),
621
+ label: "Hide Window Title".to_string(),
622
+ description: "Hide the window title bar".to_string(),
623
+ category: "UI".to_string(),
624
+ setting_type: SettingType::Boolean,
625
+ default_value: "false".to_string(),
626
+ requires_restart: false,
627
+ current_value: config.ui.hide_window_title.to_string(),
628
+ },
629
+ SettingDefinition {
630
+ key: "ui.show_status_in_title".to_string(),
631
+ label: "Show Status in Title".to_string(),
632
+ description: "Show Grok CLI status in terminal title".to_string(),
633
+ category: "UI".to_string(),
634
+ setting_type: SettingType::Boolean,
635
+ default_value: "false".to_string(),
636
+ requires_restart: false,
637
+ current_value: config.ui.show_status_in_title.to_string(),
638
+ },
639
+ SettingDefinition {
640
+ key: "ui.hide_tips".to_string(),
641
+ label: "Hide Tips".to_string(),
642
+ description: "Hide helpful tips in the UI".to_string(),
643
+ category: "UI".to_string(),
644
+ setting_type: SettingType::Boolean,
645
+ default_value: "false".to_string(),
646
+ requires_restart: false,
647
+ current_value: config.ui.hide_tips.to_string(),
648
+ },
649
+ SettingDefinition {
650
+ key: "ui.hide_banner".to_string(),
651
+ label: "Hide Banner".to_string(),
652
+ description: "Hide the application banner".to_string(),
653
+ category: "UI".to_string(),
654
+ setting_type: SettingType::Boolean,
655
+ default_value: "false".to_string(),
656
+ requires_restart: false,
657
+ current_value: config.ui.hide_banner.to_string(),
658
+ },
659
+ SettingDefinition {
660
+ key: "ui.hide_context_summary".to_string(),
661
+ label: "Hide Context Summary".to_string(),
662
+ description: "Hide context summary above input".to_string(),
663
+ category: "UI".to_string(),
664
+ setting_type: SettingType::Boolean,
665
+ default_value: "false".to_string(),
666
+ requires_restart: false,
667
+ current_value: config.ui.hide_context_summary.to_string(),
668
+ },
669
+ SettingDefinition {
670
+ key: "ui.hide_footer".to_string(),
671
+ label: "Hide Footer".to_string(),
672
+ description: "Hide the status footer".to_string(),
673
+ category: "UI".to_string(),
674
+ setting_type: SettingType::Boolean,
675
+ default_value: "false".to_string(),
676
+ requires_restart: false,
677
+ current_value: config.ui.hide_footer.to_string(),
678
+ },
679
+ SettingDefinition {
680
+ key: "ui.show_memory_usage".to_string(),
681
+ label: "Show Memory Usage".to_string(),
682
+ description: "Display memory usage information".to_string(),
683
+ category: "UI".to_string(),
684
+ setting_type: SettingType::Boolean,
685
+ default_value: "false".to_string(),
686
+ requires_restart: false,
687
+ current_value: config.ui.show_memory_usage.to_string(),
688
+ },
689
+ SettingDefinition {
690
+ key: "ui.show_line_numbers".to_string(),
691
+ label: "Show Line Numbers".to_string(),
692
+ description: "Show line numbers in the chat".to_string(),
693
+ category: "UI".to_string(),
694
+ setting_type: SettingType::Boolean,
695
+ default_value: "true".to_string(),
696
+ requires_restart: false,
697
+ current_value: config.ui.show_line_numbers.to_string(),
698
+ },
699
+ SettingDefinition {
700
+ key: "ui.show_citations".to_string(),
701
+ label: "Show Citations".to_string(),
702
+ description: "Show citations for generated content".to_string(),
703
+ category: "UI".to_string(),
704
+ setting_type: SettingType::Boolean,
705
+ default_value: "false".to_string(),
706
+ requires_restart: false,
707
+ current_value: config.ui.show_citations.to_string(),
708
+ },
709
+ SettingDefinition {
710
+ key: "ui.show_model_info_in_chat".to_string(),
711
+ label: "Show Model Info".to_string(),
712
+ description: "Display model name in chat responses".to_string(),
713
+ category: "UI".to_string(),
714
+ setting_type: SettingType::Boolean,
715
+ default_value: "false".to_string(),
716
+ requires_restart: false,
717
+ current_value: config.ui.show_model_info_in_chat.to_string(),
718
+ },
719
+ SettingDefinition {
720
+ key: "ui.use_full_width".to_string(),
721
+ label: "Use Full Width".to_string(),
722
+ description: "Use the entire width of the terminal for output".to_string(),
723
+ category: "UI".to_string(),
724
+ setting_type: SettingType::Boolean,
725
+ default_value: "true".to_string(),
726
+ requires_restart: false,
727
+ current_value: config.ui.use_full_width.to_string(),
728
+ },
729
+ SettingDefinition {
730
+ key: "ui.use_alternate_buffer".to_string(),
731
+ label: "Use Alternate Buffer".to_string(),
732
+ description: "Use alternate screen buffer (preserves history)".to_string(),
733
+ category: "UI".to_string(),
734
+ setting_type: SettingType::Boolean,
735
+ default_value: "false".to_string(),
736
+ requires_restart: false,
737
+ current_value: config.ui.use_alternate_buffer.to_string(),
738
+ },
739
+ SettingDefinition {
740
+ key: "ui.incremental_rendering".to_string(),
741
+ label: "Incremental Rendering".to_string(),
742
+ description: "Enable incremental text rendering".to_string(),
743
+ category: "UI".to_string(),
744
+ setting_type: SettingType::Boolean,
745
+ default_value: "false".to_string(),
746
+ requires_restart: false,
747
+ current_value: config.ui.incremental_rendering.to_string(),
748
+ },
749
+ SettingDefinition {
750
+ key: "ui.accessibility.disable_loading_phrases".to_string(),
751
+ label: "Disable Loading Phrases".to_string(),
752
+ description: "Disable witty loading phrases".to_string(),
753
+ category: "UI".to_string(),
754
+ setting_type: SettingType::Boolean,
755
+ default_value: "false".to_string(),
756
+ requires_restart: false,
757
+ current_value: config.ui.accessibility.disable_loading_phrases.to_string(),
758
+ },
759
+ SettingDefinition {
760
+ key: "ui.accessibility.screen_reader".to_string(),
761
+ label: "Screen Reader Mode".to_string(),
762
+ description: "Optimize output for screen readers".to_string(),
763
+ category: "UI".to_string(),
764
+ setting_type: SettingType::Boolean,
765
+ default_value: "false".to_string(),
766
+ requires_restart: false,
767
+ current_value: config.ui.accessibility.screen_reader.to_string(),
768
+ },
769
+ SettingDefinition {
770
+ key: "ui.footer.hide_cwd".to_string(),
771
+ label: "Hide Footer CWD".to_string(),
772
+ description: "Hide current working directory in footer".to_string(),
773
+ category: "UI".to_string(),
774
+ setting_type: SettingType::Boolean,
775
+ default_value: "false".to_string(),
776
+ requires_restart: false,
777
+ current_value: config.ui.footer.hide_cwd.to_string(),
778
+ },
779
+ SettingDefinition {
780
+ key: "ui.footer.hide_sandbox_status".to_string(),
781
+ label: "Hide Footer Sandbox".to_string(),
782
+ description: "Hide sandbox status indicator".to_string(),
783
+ category: "UI".to_string(),
784
+ setting_type: SettingType::Boolean,
785
+ default_value: "false".to_string(),
786
+ requires_restart: false,
787
+ current_value: config.ui.footer.hide_sandbox_status.to_string(),
788
+ },
789
+ SettingDefinition {
790
+ key: "ui.footer.hide_model_info".to_string(),
791
+ label: "Hide Footer Model Info".to_string(),
792
+ description: "Hide model information in footer".to_string(),
793
+ category: "UI".to_string(),
794
+ setting_type: SettingType::Boolean,
795
+ default_value: "false".to_string(),
796
+ requires_restart: false,
797
+ current_value: config.ui.footer.hide_model_info.to_string(),
798
+ },
799
+ SettingDefinition {
800
+ key: "ui.footer.hide_context_percentage".to_string(),
801
+ label: "Hide Context Percentage".to_string(),
802
+ description: "Hide context usage percentage".to_string(),
803
+ category: "UI".to_string(),
804
+ setting_type: SettingType::Boolean,
805
+ default_value: "true".to_string(),
806
+ requires_restart: false,
807
+ current_value: config.ui.footer.hide_context_percentage.to_string(),
808
+ },
809
+ ]);
810
+
811
+ // Model settings
812
+ settings.extend(vec![
813
+ SettingDefinition {
814
+ key: "default_model".to_string(),
815
+ label: "Default Model".to_string(),
816
+ description: "Default model to use for requests".to_string(),
817
+ category: "Model".to_string(),
818
+ setting_type: SettingType::String,
819
+ default_value: "grok-3".to_string(),
820
+ requires_restart: false,
821
+ current_value: config.default_model.clone(),
822
+ },
823
+ SettingDefinition {
824
+ key: "default_temperature".to_string(),
825
+ label: "Default Temperature".to_string(),
826
+ description: "Default temperature for responses (0.0-2.0)".to_string(),
827
+ category: "Model".to_string(),
828
+ setting_type: SettingType::Number,
829
+ default_value: "0.7".to_string(),
830
+ requires_restart: false,
831
+ current_value: config.default_temperature.to_string(),
832
+ },
833
+ SettingDefinition {
834
+ key: "default_max_tokens".to_string(),
835
+ label: "Default Max Tokens".to_string(),
836
+ description: "Default maximum tokens per response".to_string(),
837
+ category: "Model".to_string(),
838
+ setting_type: SettingType::Number,
839
+ default_value: "4096".to_string(),
840
+ requires_restart: false,
841
+ current_value: config.default_max_tokens.to_string(),
842
+ },
843
+ SettingDefinition {
844
+ key: "model.max_session_turns".to_string(),
845
+ label: "Max Session Turns".to_string(),
846
+ description: "Maximum conversation turns (-1 = unlimited)".to_string(),
847
+ category: "Model".to_string(),
848
+ setting_type: SettingType::Number,
849
+ default_value: "-1".to_string(),
850
+ requires_restart: false,
851
+ current_value: config.model.max_session_turns.to_string(),
852
+ },
853
+ SettingDefinition {
854
+ key: "model.compression_threshold".to_string(),
855
+ label: "Compression Threshold".to_string(),
856
+ description: "Context compression threshold (0.1-1.0)".to_string(),
857
+ category: "Model".to_string(),
858
+ setting_type: SettingType::Number,
859
+ default_value: "0.2".to_string(),
860
+ requires_restart: false,
861
+ current_value: config.model.compression_threshold.to_string(),
862
+ },
863
+ SettingDefinition {
864
+ key: "model.skip_next_speaker_check".to_string(),
865
+ label: "Skip Speaker Check".to_string(),
866
+ description: "Skip next speaker validation".to_string(),
867
+ category: "Model".to_string(),
868
+ setting_type: SettingType::Boolean,
869
+ default_value: "true".to_string(),
870
+ requires_restart: false,
871
+ current_value: config.model.skip_next_speaker_check.to_string(),
872
+ },
873
+ ]);
874
+
875
+ // Context Settings
876
+ settings.extend(vec![
877
+ SettingDefinition {
878
+ key: "context.discovery_max_dirs".to_string(),
879
+ label: "Discovery Max Dirs".to_string(),
880
+ description: "Maximum directories to search for context".to_string(),
881
+ category: "Context".to_string(),
882
+ setting_type: SettingType::Number,
883
+ default_value: "200".to_string(),
884
+ requires_restart: false,
885
+ current_value: config.context.discovery_max_dirs.to_string(),
886
+ },
887
+ SettingDefinition {
888
+ key: "context.load_memory_from_include_directories".to_string(),
889
+ label: "Load Memory from Includes".to_string(),
890
+ description: "Load memory from included directories".to_string(),
891
+ category: "Context".to_string(),
892
+ setting_type: SettingType::Boolean,
893
+ default_value: "false".to_string(),
894
+ requires_restart: false,
895
+ current_value: config
896
+ .context
897
+ .load_memory_from_include_directories
898
+ .to_string(),
899
+ },
900
+ SettingDefinition {
901
+ key: "context.file_filtering.respect_git_ignore".to_string(),
902
+ label: "Respect .gitignore".to_string(),
903
+ description: "Respect .gitignore files".to_string(),
904
+ category: "Context".to_string(),
905
+ setting_type: SettingType::Boolean,
906
+ default_value: "true".to_string(),
907
+ requires_restart: false,
908
+ current_value: config.context.file_filtering.respect_git_ignore.to_string(),
909
+ },
910
+ SettingDefinition {
911
+ key: "context.file_filtering.respect_grok_ignore".to_string(),
912
+ label: "Respect .grokignore".to_string(),
913
+ description: "Respect .grokignore files".to_string(),
914
+ category: "Context".to_string(),
915
+ setting_type: SettingType::Boolean,
916
+ default_value: "true".to_string(),
917
+ requires_restart: false,
918
+ current_value: config
919
+ .context
920
+ .file_filtering
921
+ .respect_grok_ignore
922
+ .to_string(),
923
+ },
924
+ SettingDefinition {
925
+ key: "context.file_filtering.enable_recursive_file_search".to_string(),
926
+ label: "Recursive File Search".to_string(),
927
+ description: "Enable recursive file search".to_string(),
928
+ category: "Context".to_string(),
929
+ setting_type: SettingType::Boolean,
930
+ default_value: "true".to_string(),
931
+ requires_restart: false,
932
+ current_value: config
933
+ .context
934
+ .file_filtering
935
+ .enable_recursive_file_search
936
+ .to_string(),
937
+ },
938
+ SettingDefinition {
939
+ key: "context.file_filtering.disable_fuzzy_search".to_string(),
940
+ label: "Disable Fuzzy Search".to_string(),
941
+ description: "Disable fuzzy file matching".to_string(),
942
+ category: "Context".to_string(),
943
+ setting_type: SettingType::Boolean,
944
+ default_value: "false".to_string(),
945
+ requires_restart: false,
946
+ current_value: config
947
+ .context
948
+ .file_filtering
949
+ .disable_fuzzy_search
950
+ .to_string(),
951
+ },
952
+ ]);
953
+
954
+ // Tools Settings
955
+ settings.extend(vec![
956
+ SettingDefinition {
957
+ key: "tools.shell.enable_interactive_shell".to_string(),
958
+ label: "Interactive Shell".to_string(),
959
+ description: "Enable interactive shell mode".to_string(),
960
+ category: "Tools".to_string(),
961
+ setting_type: SettingType::Boolean,
962
+ default_value: "true".to_string(),
963
+ requires_restart: false,
964
+ current_value: config.tools.shell.enable_interactive_shell.to_string(),
965
+ },
966
+ SettingDefinition {
967
+ key: "tools.shell.show_color".to_string(),
968
+ label: "Shell Colors".to_string(),
969
+ description: "Show colors in shell output".to_string(),
970
+ category: "Tools".to_string(),
971
+ setting_type: SettingType::Boolean,
972
+ default_value: "false".to_string(),
973
+ requires_restart: false,
974
+ current_value: config.tools.shell.show_color.to_string(),
975
+ },
976
+ SettingDefinition {
977
+ key: "tools.auto_accept".to_string(),
978
+ label: "Auto Accept Tools".to_string(),
979
+ description: "Automatically accept safe tool executions".to_string(),
980
+ category: "Tools".to_string(),
981
+ setting_type: SettingType::Boolean,
982
+ default_value: "false".to_string(),
983
+ requires_restart: false,
984
+ current_value: config.tools.auto_accept.to_string(),
985
+ },
986
+ SettingDefinition {
987
+ key: "tools.use_ripgrep".to_string(),
988
+ label: "Use Ripgrep".to_string(),
989
+ description: "Use ripgrep for faster file searches".to_string(),
990
+ category: "Tools".to_string(),
991
+ setting_type: SettingType::Boolean,
992
+ default_value: "true".to_string(),
993
+ requires_restart: false,
994
+ current_value: config.tools.use_ripgrep.to_string(),
995
+ },
996
+ SettingDefinition {
997
+ key: "tools.enable_tool_output_truncation".to_string(),
998
+ label: "Truncate Tool Output".to_string(),
999
+ description: "Truncate large tool outputs".to_string(),
1000
+ category: "Tools".to_string(),
1001
+ setting_type: SettingType::Boolean,
1002
+ default_value: "true".to_string(),
1003
+ requires_restart: false,
1004
+ current_value: config.tools.enable_tool_output_truncation.to_string(),
1005
+ },
1006
+ SettingDefinition {
1007
+ key: "tools.truncate_tool_output_threshold".to_string(),
1008
+ label: "Truncation Threshold".to_string(),
1009
+ description: "Truncation threshold in characters".to_string(),
1010
+ category: "Tools".to_string(),
1011
+ setting_type: SettingType::Number,
1012
+ default_value: "10000".to_string(),
1013
+ requires_restart: false,
1014
+ current_value: config.tools.truncate_tool_output_threshold.to_string(),
1015
+ },
1016
+ SettingDefinition {
1017
+ key: "tools.truncate_tool_output_lines".to_string(),
1018
+ label: "Truncation Lines".to_string(),
1019
+ description: "Lines to keep when truncating".to_string(),
1020
+ category: "Tools".to_string(),
1021
+ setting_type: SettingType::Number,
1022
+ default_value: "100".to_string(),
1023
+ requires_restart: false,
1024
+ current_value: config.tools.truncate_tool_output_lines.to_string(),
1025
+ },
1026
+ SettingDefinition {
1027
+ key: "tools.enable_message_bus_integration".to_string(),
1028
+ label: "Message Bus".to_string(),
1029
+ description: "Enable message bus integration".to_string(),
1030
+ category: "Tools".to_string(),
1031
+ setting_type: SettingType::Boolean,
1032
+ default_value: "true".to_string(),
1033
+ requires_restart: false,
1034
+ current_value: config.tools.enable_message_bus_integration.to_string(),
1035
+ },
1036
+ ]);
1037
+
1038
+ // Security settings
1039
+ settings.extend(vec![
1040
+ SettingDefinition {
1041
+ key: "security.disable_yolo_mode".to_string(),
1042
+ label: "Disable YOLO Mode".to_string(),
1043
+ description: "Disable YOLO mode even if flagged".to_string(),
1044
+ category: "Security".to_string(),
1045
+ setting_type: SettingType::Boolean,
1046
+ default_value: "false".to_string(),
1047
+ requires_restart: false,
1048
+ current_value: config.security.disable_yolo_mode.to_string(),
1049
+ },
1050
+ SettingDefinition {
1051
+ key: "security.enable_permanent_tool_approval".to_string(),
1052
+ label: "Permanent Approval".to_string(),
1053
+ description: "Allow permanent tool approvals".to_string(),
1054
+ category: "Security".to_string(),
1055
+ setting_type: SettingType::Boolean,
1056
+ default_value: "false".to_string(),
1057
+ requires_restart: false,
1058
+ current_value: config.security.enable_permanent_tool_approval.to_string(),
1059
+ },
1060
+ SettingDefinition {
1061
+ key: "security.block_git_extensions".to_string(),
1062
+ label: "Block Git Extensions".to_string(),
1063
+ description: "Block Git-based extensions".to_string(),
1064
+ category: "Security".to_string(),
1065
+ setting_type: SettingType::Boolean,
1066
+ default_value: "false".to_string(),
1067
+ requires_restart: false,
1068
+ current_value: config.security.block_git_extensions.to_string(),
1069
+ },
1070
+ SettingDefinition {
1071
+ key: "security.folder_trust.enabled".to_string(),
1072
+ label: "Folder Trust".to_string(),
1073
+ description: "Enable folder trust system".to_string(),
1074
+ category: "Security".to_string(),
1075
+ setting_type: SettingType::Boolean,
1076
+ default_value: "false".to_string(),
1077
+ requires_restart: false,
1078
+ current_value: config.security.folder_trust.enabled.to_string(),
1079
+ },
1080
+ SettingDefinition {
1081
+ key: "security.environment_variable_redaction.enabled".to_string(),
1082
+ label: "Env Var Redaction".to_string(),
1083
+ description: "Enable env var redaction".to_string(),
1084
+ category: "Security".to_string(),
1085
+ setting_type: SettingType::Boolean,
1086
+ default_value: "false".to_string(),
1087
+ requires_restart: false,
1088
+ current_value: config
1089
+ .security
1090
+ .environment_variable_redaction
1091
+ .enabled
1092
+ .to_string(),
1093
+ },
1094
+ ]);
1095
+
1096
+ // Experimental settings
1097
+ settings.extend(vec![
1098
+ SettingDefinition {
1099
+ key: "experimental.enable_agents".to_string(),
1100
+ label: "Enable Agents".to_string(),
1101
+ description: "Enable experimental agent features".to_string(),
1102
+ category: "Experimental".to_string(),
1103
+ setting_type: SettingType::Boolean,
1104
+ default_value: "false".to_string(),
1105
+ requires_restart: false,
1106
+ current_value: config.experimental.enable_agents.to_string(),
1107
+ },
1108
+ SettingDefinition {
1109
+ key: "experimental.extension_management".to_string(),
1110
+ label: "Extension Management".to_string(),
1111
+ description: "Enable extension management".to_string(),
1112
+ category: "Experimental".to_string(),
1113
+ setting_type: SettingType::Boolean,
1114
+ default_value: "false".to_string(),
1115
+ requires_restart: false,
1116
+ current_value: config.experimental.extension_management.to_string(),
1117
+ },
1118
+ SettingDefinition {
1119
+ key: "experimental.jit_context".to_string(),
1120
+ label: "JIT Context".to_string(),
1121
+ description: "Enable just-in-time context loading".to_string(),
1122
+ category: "Experimental".to_string(),
1123
+ setting_type: SettingType::Boolean,
1124
+ default_value: "false".to_string(),
1125
+ requires_restart: false,
1126
+ current_value: config.experimental.jit_context.to_string(),
1127
+ },
1128
+ SettingDefinition {
1129
+ key: "experimental.codebase_investigator_settings.enabled".to_string(),
1130
+ label: "Codebase Investigator".to_string(),
1131
+ description: "Enable codebase investigator".to_string(),
1132
+ category: "Experimental".to_string(),
1133
+ setting_type: SettingType::Boolean,
1134
+ default_value: "true".to_string(),
1135
+ requires_restart: false,
1136
+ current_value: config
1137
+ .experimental
1138
+ .codebase_investigator_settings
1139
+ .enabled
1140
+ .to_string(),
1141
+ },
1142
+ SettingDefinition {
1143
+ key: "experimental.codebase_investigator_settings.max_num_turns".to_string(),
1144
+ label: "Investigator Max Turns".to_string(),
1145
+ description: "Max investigator turns".to_string(),
1146
+ category: "Experimental".to_string(),
1147
+ setting_type: SettingType::Number,
1148
+ default_value: "10".to_string(),
1149
+ requires_restart: false,
1150
+ current_value: config
1151
+ .experimental
1152
+ .codebase_investigator_settings
1153
+ .max_num_turns
1154
+ .to_string(),
1155
+ },
1156
+ ]);
1157
+
1158
+ // ACP settings
1159
+ settings.extend(vec![
1160
+ SettingDefinition {
1161
+ key: "acp.enabled".to_string(),
1162
+ label: "ACP Enabled".to_string(),
1163
+ description: "Enable Agent Client Protocol".to_string(),
1164
+ category: "ACP".to_string(),
1165
+ setting_type: SettingType::Boolean,
1166
+ default_value: "true".to_string(),
1167
+ requires_restart: true,
1168
+ current_value: config.acp.enabled.to_string(),
1169
+ },
1170
+ SettingDefinition {
1171
+ key: "acp.bind_host".to_string(),
1172
+ label: "ACP Bind Host".to_string(),
1173
+ description: "ACP server bind address".to_string(),
1174
+ category: "ACP".to_string(),
1175
+ setting_type: SettingType::String,
1176
+ default_value: "127.0.0.1".to_string(),
1177
+ requires_restart: true,
1178
+ current_value: config.acp.bind_host.clone(),
1179
+ },
1180
+ SettingDefinition {
1181
+ key: "acp.default_port".to_string(),
1182
+ label: "ACP Default Port".to_string(),
1183
+ description: "Default ACP server port".to_string(),
1184
+ category: "ACP".to_string(),
1185
+ setting_type: SettingType::Number,
1186
+ default_value: "".to_string(), // None
1187
+ requires_restart: true,
1188
+ current_value: config
1189
+ .acp
1190
+ .default_port
1191
+ .map(|p| p.to_string())
1192
+ .unwrap_or_default(),
1193
+ },
1194
+ SettingDefinition {
1195
+ key: "acp.protocol_version".to_string(),
1196
+ label: "ACP Protocol Version".to_string(),
1197
+ description: "ACP protocol version".to_string(),
1198
+ category: "ACP".to_string(),
1199
+ setting_type: SettingType::String,
1200
+ default_value: "1.0".to_string(),
1201
+ requires_restart: true,
1202
+ current_value: config.acp.protocol_version.clone(),
1203
+ },
1204
+ SettingDefinition {
1205
+ key: "acp.dev_mode".to_string(),
1206
+ label: "ACP Dev Mode".to_string(),
1207
+ description: "Enable development mode".to_string(),
1208
+ category: "ACP".to_string(),
1209
+ setting_type: SettingType::Boolean,
1210
+ default_value: "false".to_string(),
1211
+ requires_restart: true,
1212
+ current_value: config.acp.dev_mode.to_string(),
1213
+ },
1214
+ ]);
1215
+
1216
+ // Network settings
1217
+ settings.extend(vec![
1218
+ SettingDefinition {
1219
+ key: "network.starlink_optimizations".to_string(),
1220
+ label: "Starlink Optimizations".to_string(),
1221
+ description: "Enable Starlink satellite optimizations".to_string(),
1222
+ category: "Network".to_string(),
1223
+ setting_type: SettingType::Boolean,
1224
+ default_value: "false".to_string(),
1225
+ requires_restart: false,
1226
+ current_value: config.network.starlink_optimizations.to_string(),
1227
+ },
1228
+ SettingDefinition {
1229
+ key: "network.base_retry_delay".to_string(),
1230
+ label: "Base Retry Delay".to_string(),
1231
+ description: "Base retry delay in seconds".to_string(),
1232
+ category: "Network".to_string(),
1233
+ setting_type: SettingType::Number,
1234
+ default_value: "1".to_string(),
1235
+ requires_restart: false,
1236
+ current_value: config.network.base_retry_delay.to_string(),
1237
+ },
1238
+ SettingDefinition {
1239
+ key: "network.max_retry_delay".to_string(),
1240
+ label: "Max Retry Delay".to_string(),
1241
+ description: "Maximum retry delay in seconds".to_string(),
1242
+ category: "Network".to_string(),
1243
+ setting_type: SettingType::Number,
1244
+ default_value: "30".to_string(),
1245
+ requires_restart: false,
1246
+ current_value: config.network.max_retry_delay.to_string(),
1247
+ },
1248
+ SettingDefinition {
1249
+ key: "network.health_monitoring".to_string(),
1250
+ label: "Health Monitoring".to_string(),
1251
+ description: "Enable network health monitoring".to_string(),
1252
+ category: "Network".to_string(),
1253
+ setting_type: SettingType::Boolean,
1254
+ default_value: "true".to_string(),
1255
+ requires_restart: false,
1256
+ current_value: config.network.health_monitoring.to_string(),
1257
+ },
1258
+ SettingDefinition {
1259
+ key: "network.connect_timeout".to_string(),
1260
+ label: "Connect Timeout".to_string(),
1261
+ description: "Connection timeout in seconds".to_string(),
1262
+ category: "Network".to_string(),
1263
+ setting_type: SettingType::Number,
1264
+ default_value: "10".to_string(),
1265
+ requires_restart: false,
1266
+ current_value: config.network.connect_timeout.to_string(),
1267
+ },
1268
+ SettingDefinition {
1269
+ key: "network.read_timeout".to_string(),
1270
+ label: "Read Timeout".to_string(),
1271
+ description: "Read timeout in seconds".to_string(),
1272
+ category: "Network".to_string(),
1273
+ setting_type: SettingType::Number,
1274
+ default_value: "30".to_string(),
1275
+ requires_restart: false,
1276
+ current_value: config.network.read_timeout.to_string(),
1277
+ },
1278
+ ]);
1279
+
1280
+ // Logging settings
1281
+ settings.extend(vec![
1282
+ SettingDefinition {
1283
+ key: "logging.level".to_string(),
1284
+ label: "Log Level".to_string(),
1285
+ description: "Log level (trace/debug/info/warn/error)".to_string(),
1286
+ category: "Logging".to_string(),
1287
+ setting_type: SettingType::String,
1288
+ default_value: "info".to_string(),
1289
+ requires_restart: true,
1290
+ current_value: config.logging.level.clone(),
1291
+ },
1292
+ SettingDefinition {
1293
+ key: "logging.file_logging".to_string(),
1294
+ label: "File Logging".to_string(),
1295
+ description: "Enable logging to file".to_string(),
1296
+ category: "Logging".to_string(),
1297
+ setting_type: SettingType::Boolean,
1298
+ default_value: "false".to_string(),
1299
+ requires_restart: true,
1300
+ current_value: config.logging.file_logging.to_string(),
1301
+ },
1302
+ SettingDefinition {
1303
+ key: "logging.max_file_size_mb".to_string(),
1304
+ label: "Max Log Size (MB)".to_string(),
1305
+ description: "Maximum log file size in MB".to_string(),
1306
+ category: "Logging".to_string(),
1307
+ setting_type: SettingType::Number,
1308
+ default_value: "10".to_string(),
1309
+ requires_restart: true,
1310
+ current_value: config.logging.max_file_size_mb.to_string(),
1311
+ },
1312
+ SettingDefinition {
1313
+ key: "logging.rotation_count".to_string(),
1314
+ label: "Log Rotation Count".to_string(),
1315
+ description: "Number of rotated log files to keep".to_string(),
1316
+ category: "Logging".to_string(),
1317
+ setting_type: SettingType::Number,
1318
+ default_value: "5".to_string(),
1319
+ requires_restart: true,
1320
+ current_value: config.logging.rotation_count.to_string(),
1321
+ },
1322
+ ]);
1323
+
1324
+ settings
1325
+ }
1326
+
1327
+ /// Get unique categories from settings
1328
+ fn get_categories(settings: &[SettingDefinition]) -> Vec<String> {
1329
+ let mut categories: Vec<String> = settings
1330
+ .iter()
1331
+ .map(|s| s.category.clone())
1332
+ .collect::<std::collections::HashSet<_>>()
1333
+ .into_iter()
1334
+ .collect();
1335
+
1336
+ categories.sort();
1337
+ categories
1338
+ }
1339
+
1340
+ #[cfg(test)]
1341
+ mod tests {
1342
+ use super::*;
1343
+
1344
+ #[test]
1345
+ fn test_format_setting_value() {
1346
+ assert_eq!(
1347
+ format_setting_value("true", &SettingType::Boolean).to_string(),
1348
+ "✓ Enabled".green().to_string()
1349
+ );
1350
+ assert_eq!(
1351
+ format_setting_value("false", &SettingType::Boolean).to_string(),
1352
+ "✗ Disabled".red().to_string()
1353
+ );
1354
+ }
1355
+
1356
+ #[test]
1357
+ fn test_get_categories() {
1358
+ let settings = vec![
1359
+ SettingDefinition {
1360
+ key: "test1".to_string(),
1361
+ label: "Test 1".to_string(),
1362
+ description: "Test".to_string(),
1363
+ category: "UI".to_string(),
1364
+ setting_type: SettingType::Boolean,
1365
+ default_value: "false".to_string(),
1366
+ requires_restart: false,
1367
+ current_value: "true".to_string(),
1368
+ },
1369
+ SettingDefinition {
1370
+ key: "test2".to_string(),
1371
+ label: "Test 2".to_string(),
1372
+ description: "Test".to_string(),
1373
+ category: "General".to_string(),
1374
+ setting_type: SettingType::String,
1375
+ default_value: "default".to_string(),
1376
+ requires_restart: false,
1377
+ current_value: "custom".to_string(),
1378
+ },
1379
+ ];
1380
+
1381
+ let categories = get_categories(&settings);
1382
+ assert_eq!(categories, vec!["General", "UI"]);
1383
+ }
1384
+ }