omnimancer-cli 0.1.0__py3-none-any.whl

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 (126) hide show
  1. omnimancer/__init__.py +42 -0
  2. omnimancer/__main__.py +9 -0
  3. omnimancer/cli/__init__.py +8 -0
  4. omnimancer/cli/agent_manager.py +826 -0
  5. omnimancer/cli/agent_wizard.py +944 -0
  6. omnimancer/cli/approval_formatter.py +734 -0
  7. omnimancer/cli/approval_integration.py +734 -0
  8. omnimancer/cli/approval_prompt.py +1286 -0
  9. omnimancer/cli/batch_approval_display.py +617 -0
  10. omnimancer/cli/batch_approval_filters.py +729 -0
  11. omnimancer/cli/batch_operation_monitor.py +775 -0
  12. omnimancer/cli/commands/performance_cmd.py +624 -0
  13. omnimancer/cli/commands.py +757 -0
  14. omnimancer/cli/completion_handler.py +281 -0
  15. omnimancer/cli/config_setup_wizard.py +783 -0
  16. omnimancer/cli/dynamic_commands.py +371 -0
  17. omnimancer/cli/handlers/__init__.py +18 -0
  18. omnimancer/cli/handlers/agent_handler.py +865 -0
  19. omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
  20. omnimancer/cli/handlers/custom_agent_handler.py +352 -0
  21. omnimancer/cli/handlers/permissions_handler.py +1609 -0
  22. omnimancer/cli/interface.py +6695 -0
  23. omnimancer/cli/performance_dashboard.py +612 -0
  24. omnimancer/core/__init__.py +34 -0
  25. omnimancer/core/agent/__init__.py +256 -0
  26. omnimancer/core/agent/agent_config.py +715 -0
  27. omnimancer/core/agent/agent_switcher.py +668 -0
  28. omnimancer/core/agent/approval_context.py +460 -0
  29. omnimancer/core/agent/approval_dialog.py +902 -0
  30. omnimancer/core/agent/approval_interface.py +628 -0
  31. omnimancer/core/agent/approval_manager.py +760 -0
  32. omnimancer/core/agent/cli_integration.py +470 -0
  33. omnimancer/core/agent/config.py +845 -0
  34. omnimancer/core/agent/custom_agent_integration.py +654 -0
  35. omnimancer/core/agent/diff_renderer.py +706 -0
  36. omnimancer/core/agent/file_content_display.py +503 -0
  37. omnimancer/core/agent/file_modification_workflow.py +658 -0
  38. omnimancer/core/agent/file_system_manager.py +1790 -0
  39. omnimancer/core/agent/input_handler.py +614 -0
  40. omnimancer/core/agent/metrics_collector.py +771 -0
  41. omnimancer/core/agent/optimization_engine.py +729 -0
  42. omnimancer/core/agent/performance_monitor.py +717 -0
  43. omnimancer/core/agent/persona.py +870 -0
  44. omnimancer/core/agent/persona_validator.py +672 -0
  45. omnimancer/core/agent/program_executor.py +748 -0
  46. omnimancer/core/agent/proposed_changes_integration.py +663 -0
  47. omnimancer/core/agent/read_before_write_errors.py +492 -0
  48. omnimancer/core/agent/read_before_write_ui.py +647 -0
  49. omnimancer/core/agent/rich_renderer.py +763 -0
  50. omnimancer/core/agent/status_config.py +238 -0
  51. omnimancer/core/agent/status_core.py +446 -0
  52. omnimancer/core/agent/status_integrated_engine.py +540 -0
  53. omnimancer/core/agent/status_manager.py +878 -0
  54. omnimancer/core/agent/status_ui_components.py +535 -0
  55. omnimancer/core/agent/template_applicator.py +548 -0
  56. omnimancer/core/agent/terminal_status_listener.py +394 -0
  57. omnimancer/core/agent/token_tracker.py +679 -0
  58. omnimancer/core/agent/types.py +60 -0
  59. omnimancer/core/agent/web_client.py +745 -0
  60. omnimancer/core/agent/workflow_orchestrator.py +826 -0
  61. omnimancer/core/agent_engine.py +1625 -0
  62. omnimancer/core/agent_mode_manager.py +706 -0
  63. omnimancer/core/agent_progress_ui.py +545 -0
  64. omnimancer/core/chat_manager.py +141 -0
  65. omnimancer/core/config_manager.py +2199 -0
  66. omnimancer/core/config_migration.py +772 -0
  67. omnimancer/core/config_provider.py +1974 -0
  68. omnimancer/core/config_security_validator.py +393 -0
  69. omnimancer/core/config_validator.py +768 -0
  70. omnimancer/core/conversation_manager.py +352 -0
  71. omnimancer/core/engine.py +787 -0
  72. omnimancer/core/enhanced_input.py +523 -0
  73. omnimancer/core/env_loader.py +83 -0
  74. omnimancer/core/fallback_manager.py +568 -0
  75. omnimancer/core/health_monitor.py +462 -0
  76. omnimancer/core/history_manager.py +462 -0
  77. omnimancer/core/mcp_integration_layer.py +878 -0
  78. omnimancer/core/models.py +1889 -0
  79. omnimancer/core/provider_capabilities.py +403 -0
  80. omnimancer/core/provider_initializer.py +450 -0
  81. omnimancer/core/provider_registry.py +432 -0
  82. omnimancer/core/security/__init__.py +16 -0
  83. omnimancer/core/security/approval_workflow.py +406 -0
  84. omnimancer/core/security/audit_logger.py +511 -0
  85. omnimancer/core/security/permission_controller.py +511 -0
  86. omnimancer/core/security/sandbox_manager.py +379 -0
  87. omnimancer/core/security/security_manager.py +530 -0
  88. omnimancer/core/setup_wizard.py +345 -0
  89. omnimancer/core/setup_wizard_core.py +449 -0
  90. omnimancer/core/setup_wizard_guide_generator.py +376 -0
  91. omnimancer/core/setup_wizard_provider_setup.py +454 -0
  92. omnimancer/core/setup_wizard_ui.py +317 -0
  93. omnimancer/core/setup_wizard_validation.py +371 -0
  94. omnimancer/core/signal_handler.py +174 -0
  95. omnimancer/mcp/__init__.py +11 -0
  96. omnimancer/mcp/client.py +725 -0
  97. omnimancer/mcp/manager.py +649 -0
  98. omnimancer/providers/__init__.py +24 -0
  99. omnimancer/providers/azure.py +608 -0
  100. omnimancer/providers/base.py +262 -0
  101. omnimancer/providers/bedrock.py +711 -0
  102. omnimancer/providers/claude.py +426 -0
  103. omnimancer/providers/claude_code.py +406 -0
  104. omnimancer/providers/cohere.py +412 -0
  105. omnimancer/providers/factory.py +988 -0
  106. omnimancer/providers/gemini.py +708 -0
  107. omnimancer/providers/mistral.py +554 -0
  108. omnimancer/providers/ollama.py +510 -0
  109. omnimancer/providers/openai.py +362 -0
  110. omnimancer/providers/openrouter.py +709 -0
  111. omnimancer/providers/perplexity.py +476 -0
  112. omnimancer/providers/vertex.py +617 -0
  113. omnimancer/providers/xai.py +484 -0
  114. omnimancer/ui/__init__.py +6 -0
  115. omnimancer/ui/cancellation_handler.py +235 -0
  116. omnimancer/ui/progress_indicator.py +318 -0
  117. omnimancer/utils/__init__.py +22 -0
  118. omnimancer/utils/error_handler.py +589 -0
  119. omnimancer/utils/errors.py +667 -0
  120. omnimancer/utils/retry.py +238 -0
  121. omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
  122. omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
  123. omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
  124. omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
  125. omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  126. omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,783 @@
1
+ """
2
+ Configuration Setup Wizard for Omnimancer CLI.
3
+
4
+ This module provides an interactive command-line configuration wizard
5
+ that uses the simplified configuration interface to guide users through
6
+ setting up Omnimancer with minimal complexity.
7
+ """
8
+
9
+ import logging
10
+ import time
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ from rich.console import Console
14
+ from rich.markdown import Markdown
15
+ from rich.panel import Panel
16
+ from rich.progress import (
17
+ BarColumn,
18
+ Progress,
19
+ SpinnerColumn,
20
+ TaskProgressColumn,
21
+ TextColumn,
22
+ )
23
+ from rich.prompt import Confirm, Prompt
24
+ from rich.table import Table
25
+
26
+ from ..core.config_manager import ConfigManager
27
+ from ..core.config_provider import (
28
+ ConfigurationContext,
29
+ ConfigurationProvider,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class ConfigSetupWizard:
36
+ """
37
+ Interactive configuration setup wizard for Omnimancer CLI.
38
+
39
+ Provides a user-friendly interface for configuring Omnimancer using
40
+ the simplified configuration system with templates and guided setup.
41
+ """
42
+
43
+ def __init__(self, config_manager: Optional[ConfigManager] = None):
44
+ """
45
+ Initialize the configuration setup wizard.
46
+
47
+ Args:
48
+ config_manager: Optional ConfigManager instance
49
+ """
50
+ self.console = Console()
51
+ self.config_manager = config_manager or ConfigManager()
52
+ self.config_provider = ConfigurationProvider(self.config_manager)
53
+
54
+ self.user_answers = {}
55
+ self.current_step = 0
56
+
57
+ def run_initial_setup(self) -> bool:
58
+ """
59
+ Run the initial setup wizard for new users.
60
+
61
+ Returns:
62
+ True if setup completed successfully, False otherwise
63
+ """
64
+ try:
65
+ self._show_welcome_screen()
66
+
67
+ if self._check_existing_configuration():
68
+ return self._handle_existing_configuration()
69
+
70
+ return self._run_fresh_setup()
71
+
72
+ except KeyboardInterrupt:
73
+ self.console.print("\n[yellow]Setup cancelled by user.[/yellow]")
74
+ return False
75
+ except Exception as e:
76
+ logger.error(f"Setup wizard failed: {e}")
77
+ self.console.print(f"\n[red]Setup failed: {str(e)}[/red]")
78
+ return False
79
+
80
+ def _show_welcome_screen(self) -> None:
81
+ """Display the welcome screen."""
82
+ welcome_text = """
83
+ # Welcome to Omnimancer Setup! 🚀
84
+
85
+ Omnimancer is your unified CLI for multiple AI language models. This setup wizard will help you configure it quickly and easily.
86
+
87
+ ## What we'll set up:
88
+ - **AI Providers**: Choose which AI services you want to use
89
+ - **API Keys**: Securely add your credentials
90
+ - **Templates**: Pick optimized settings for your use case
91
+ - **Tools**: Enable helpful features and capabilities
92
+
93
+ The entire process takes just **5-10 minutes** and you can change everything later.
94
+ """
95
+
96
+ panel = Panel(
97
+ Markdown(welcome_text),
98
+ title="🤖 Omnimancer Configuration Wizard",
99
+ border_style="blue",
100
+ padding=(1, 2),
101
+ )
102
+ self.console.print(panel)
103
+
104
+ if not Confirm.ask("Ready to get started?", default=True):
105
+ raise KeyboardInterrupt()
106
+
107
+ def _check_existing_configuration(self) -> bool:
108
+ """Check if there's an existing configuration."""
109
+ try:
110
+ config = self.config_manager.get_config()
111
+ return len(config.providers) > 0
112
+ except:
113
+ return False
114
+
115
+ def _handle_existing_configuration(self) -> bool:
116
+ """Handle existing configuration options."""
117
+ self.console.print("\n[yellow]Existing Configuration Found[/yellow]")
118
+ self.console.print(
119
+ "You already have Omnimancer configured. What would you like to do?"
120
+ )
121
+
122
+ options = [
123
+ "Keep current settings and exit",
124
+ "Add more providers to existing configuration",
125
+ "Migrate to a simpler template-based configuration",
126
+ "Start fresh with new configuration",
127
+ ]
128
+
129
+ for i, option in enumerate(options, 1):
130
+ self.console.print(f" {i}. {option}")
131
+
132
+ choice = Prompt.ask(
133
+ "Choose an option", choices=["1", "2", "3", "4"], default="1"
134
+ )
135
+
136
+ if choice == "1":
137
+ self.console.print("[green]✓[/green] Keeping existing configuration")
138
+ return True
139
+ elif choice == "2":
140
+ return self._add_providers_to_existing()
141
+ elif choice == "3":
142
+ return self._migrate_existing_configuration()
143
+ else:
144
+ return self._start_fresh_configuration()
145
+
146
+ def _add_providers_to_existing(self) -> bool:
147
+ """Add new providers to existing configuration."""
148
+ self.console.print("\n[blue]Adding New Providers[/blue]")
149
+
150
+ # Get current providers
151
+ current_config = self.config_manager.get_config()
152
+ current_providers = set(current_config.providers.keys())
153
+
154
+ # Show available providers not yet configured
155
+ all_providers = self.config_provider.get_simple_mode_providers()
156
+ available_providers = [
157
+ p for p in all_providers if p.name not in current_providers
158
+ ]
159
+
160
+ if not available_providers:
161
+ self.console.print(
162
+ "[yellow]All supported providers are already configured.[/yellow]"
163
+ )
164
+ return True
165
+
166
+ self.console.print("Available providers to add:")
167
+ for i, provider in enumerate(available_providers, 1):
168
+ self.console.print(
169
+ f" {i}. {provider.display_name} - {provider.description}"
170
+ )
171
+
172
+ selections = Prompt.ask(
173
+ "Which providers would you like to add? (comma-separated numbers)"
174
+ ).split(",")
175
+
176
+ # Process selections and add providers
177
+ for selection in selections:
178
+ try:
179
+ idx = int(selection.strip()) - 1
180
+ if 0 <= idx < len(available_providers):
181
+ provider = available_providers[idx]
182
+ self._configure_single_provider(provider)
183
+ except (ValueError, IndexError):
184
+ continue
185
+
186
+ self.config_manager.save_config()
187
+ self.console.print("[green]✓[/green] New providers added successfully!")
188
+ return True
189
+
190
+ def _migrate_existing_configuration(self) -> bool:
191
+ """Migrate existing configuration to a template."""
192
+ self.console.print("\n[blue]Configuration Migration[/blue]")
193
+
194
+ with self.console.status("[bold green]Analyzing your current configuration..."):
195
+ analysis = (
196
+ self.config_provider.migration_helper.analyze_current_configuration()
197
+ )
198
+
199
+ # Show analysis results
200
+ self._show_migration_analysis(analysis)
201
+
202
+ if not analysis.recommended_template:
203
+ self.console.print(
204
+ "[yellow]No suitable template found for migration.[/yellow]"
205
+ )
206
+ return self._run_fresh_setup()
207
+
208
+ if not Confirm.ask(
209
+ f"Migrate to '{analysis.recommended_template}' template?",
210
+ default=True,
211
+ ):
212
+ return self._run_fresh_setup()
213
+
214
+ # Create and execute migration plan
215
+ with self.console.status("[bold green]Creating migration plan..."):
216
+ migration_plan = (
217
+ self.config_provider.migration_helper.create_migration_plan(
218
+ analysis.recommended_template, preserve_customizations=True
219
+ )
220
+ )
221
+
222
+ self._show_migration_plan(migration_plan)
223
+
224
+ if Confirm.ask("Execute this migration plan?", default=True):
225
+ return self._execute_migration(
226
+ migration_plan, analysis.recommended_template
227
+ )
228
+
229
+ return self._run_fresh_setup()
230
+
231
+ def _show_migration_analysis(self, analysis) -> None:
232
+ """Show migration analysis results."""
233
+ table = Table(title="Configuration Analysis")
234
+ table.add_column("Aspect", style="cyan")
235
+ table.add_column("Current State", style="magenta")
236
+
237
+ table.add_row("Complexity Level", analysis.current_complexity.title())
238
+ table.add_row("Recommended Template", analysis.recommended_template or "None")
239
+ table.add_row("Confidence", f"{analysis.confidence:.0%}")
240
+ table.add_row("Estimated Time", analysis.estimated_time)
241
+
242
+ self.console.print(table)
243
+
244
+ if analysis.simplification_opportunities:
245
+ self.console.print("\n[green]Simplification Opportunities:[/green]")
246
+ for opportunity in analysis.simplification_opportunities:
247
+ self.console.print(f" • {opportunity}")
248
+
249
+ if analysis.potential_issues:
250
+ self.console.print("\n[yellow]Potential Issues:[/yellow]")
251
+ for issue in analysis.potential_issues:
252
+ self.console.print(f" ⚠ {issue}")
253
+
254
+ def _show_migration_plan(self, migration_plan) -> None:
255
+ """Show migration plan details."""
256
+ self.console.print(
257
+ f"\n[blue]Migration Plan[/blue] ({migration_plan.estimated_duration})"
258
+ )
259
+
260
+ for step in migration_plan.steps:
261
+ step_num = step["step"]
262
+ title = step["title"]
263
+ description = step["description"]
264
+ time_est = step.get("estimated_time", "")
265
+
266
+ self.console.print(f" {step_num}. **{title}** ({time_est})")
267
+ self.console.print(f" {description}")
268
+
269
+ if "details" in step:
270
+ for detail in step["details"][:2]: # Show first 2 details
271
+ self.console.print(f" • {detail}")
272
+
273
+ risk_color = {"low": "green", "medium": "yellow", "high": "red"}.get(
274
+ migration_plan.risk_level, "white"
275
+ )
276
+ self.console.print(
277
+ f"\nRisk Level: [{risk_color}]{migration_plan.risk_level.title()}[/{risk_color}]"
278
+ )
279
+
280
+ def _execute_migration(self, migration_plan, template_name: str) -> bool:
281
+ """Execute the migration plan."""
282
+ self.console.print("\n[blue]Executing Migration...[/blue]")
283
+
284
+ with Progress(
285
+ SpinnerColumn(),
286
+ TextColumn("[progress.description]{task.description}"),
287
+ BarColumn(),
288
+ TaskProgressColumn(),
289
+ console=self.console,
290
+ ) as progress:
291
+
292
+ task = progress.add_task(
293
+ "Migrating configuration...", total=len(migration_plan.steps)
294
+ )
295
+
296
+ success, messages = self.config_provider.migration_helper.execute_migration(
297
+ migration_plan, template_name
298
+ )
299
+
300
+ for i, step in enumerate(migration_plan.steps):
301
+ progress.update(
302
+ task, advance=1, description=f"Step {i+1}: {step['title']}"
303
+ )
304
+ time.sleep(0.5) # Show progress
305
+
306
+ # Show results
307
+ for message in messages:
308
+ if message.startswith("✓"):
309
+ self.console.print(f"[green]{message}[/green]")
310
+ elif message.startswith("⚠"):
311
+ self.console.print(f"[yellow]{message}[/yellow]")
312
+ elif message.startswith("✗"):
313
+ self.console.print(f"[red]{message}[/red]")
314
+ else:
315
+ self.console.print(message)
316
+
317
+ return success
318
+
319
+ def _start_fresh_configuration(self) -> bool:
320
+ """Start fresh configuration, removing existing."""
321
+ if Confirm.ask(
322
+ "This will replace your current configuration. Continue?",
323
+ default=False,
324
+ ):
325
+ # Backup current config
326
+ backup_path = self.config_manager.backup_config()
327
+ self.console.print(
328
+ f"[green]✓[/green] Current configuration backed up to: {backup_path}"
329
+ )
330
+
331
+ # Clear current configuration
332
+ self.config_manager.reset_config()
333
+ return self._run_fresh_setup()
334
+
335
+ return False
336
+
337
+ def _run_fresh_setup(self) -> bool:
338
+ """Run the complete fresh setup wizard."""
339
+ self.console.print("\n[blue]Starting Fresh Configuration[/blue]")
340
+
341
+ steps = self.config_provider.get_setup_wizard_steps()
342
+
343
+ for step in steps:
344
+ self.current_step = step["step"]
345
+
346
+ if not self._execute_wizard_step(step):
347
+ return False
348
+
349
+ # Generate final configuration
350
+ return self._finalize_configuration()
351
+
352
+ def _execute_wizard_step(self, step: Dict[str, Any]) -> bool:
353
+ """Execute a single wizard step."""
354
+ step_type = step["type"]
355
+
356
+ self.console.print(f"\n[blue]Step {step['step']}: {step['title']}[/blue]")
357
+ self.console.print(step["description"])
358
+
359
+ if step_type == "welcome":
360
+ return self._handle_welcome_step(step)
361
+ elif step_type == "single_choice":
362
+ return self._handle_single_choice_step(step)
363
+ elif step_type == "provider_selection":
364
+ return self._handle_provider_selection_step(step)
365
+ elif step_type == "api_keys":
366
+ return self._handle_api_keys_step(step)
367
+ elif step_type == "feature_selection":
368
+ return self._handle_feature_selection_step(step)
369
+ elif step_type == "review":
370
+ return self._handle_review_step(step)
371
+ else:
372
+ self.console.print(f"[red]Unknown step type: {step_type}[/red]")
373
+ return True # Continue anyway
374
+
375
+ def _handle_welcome_step(self, step: Dict[str, Any]) -> bool:
376
+ """Handle welcome step."""
377
+ content = step.get("content", {})
378
+ features = content.get("features", [])
379
+
380
+ self.console.print(f"\n{content.get('message', '')}")
381
+ self.console.print(
382
+ f"Estimated time: {content.get('estimated_time', 'Unknown')}"
383
+ )
384
+
385
+ if features:
386
+ self.console.print("\nWhat we'll configure:")
387
+ for feature in features:
388
+ self.console.print(f" • {feature}")
389
+
390
+ return Confirm.ask("\nReady to continue?", default=True)
391
+
392
+ def _handle_single_choice_step(self, step: Dict[str, Any]) -> bool:
393
+ """Handle single choice selection step."""
394
+ option = step.get("option", {})
395
+ choices = option.get("choices", [])
396
+
397
+ if not choices:
398
+ return True
399
+
400
+ # Display choices
401
+ self.console.print(f"\n{option.get('display_name', 'Please choose:')}")
402
+ for i, choice in enumerate(choices, 1):
403
+ label = choice.get("label", choice.get("value", ""))
404
+ description = choice.get("description", "")
405
+ self.console.print(f" {i}. **{label}**")
406
+ if description:
407
+ self.console.print(f" {description}")
408
+
409
+ help_text = step.get("help_text", "")
410
+ if help_text:
411
+ self.console.print(f"\n[dim]{help_text}[/dim]")
412
+
413
+ # Get user selection
414
+ choice_nums = [str(i) for i in range(1, len(choices) + 1)]
415
+ default_idx = next(
416
+ (
417
+ i
418
+ for i, choice in enumerate(choices)
419
+ if choice.get("value") == option.get("default_value")
420
+ ),
421
+ 0,
422
+ )
423
+
424
+ selection = Prompt.ask(
425
+ "Your choice", choices=choice_nums, default=str(default_idx + 1)
426
+ )
427
+
428
+ selected_choice = choices[int(selection) - 1]
429
+ self.user_answers[option["key"]] = selected_choice["value"]
430
+
431
+ return True
432
+
433
+ def _handle_provider_selection_step(self, step: Dict[str, Any]) -> bool:
434
+ """Handle provider selection step."""
435
+ content = step.get("content", {})
436
+ providers = content.get("providers", [])
437
+
438
+ self.console.print(f"\n{content.get('message', '')}")
439
+
440
+ # Show available providers
441
+ table = Table(title="Available AI Providers")
442
+ table.add_column("Provider", style="cyan")
443
+ table.add_column("Description", style="white")
444
+ table.add_column("Setup", style="yellow")
445
+ table.add_column("Recommended", style="green")
446
+
447
+ for i, provider_info in enumerate(providers):
448
+ provider = provider_info["provider"]
449
+ recommended = "✓" if provider_info.get("recommended", False) else ""
450
+ setup_difficulty = provider_info.get("setup_difficulty", "Easy")
451
+
452
+ table.add_row(
453
+ f"{i+1}. {provider['display_name']}",
454
+ (
455
+ provider["description"][:60] + "..."
456
+ if len(provider["description"]) > 60
457
+ else provider["description"]
458
+ ),
459
+ setup_difficulty,
460
+ recommended,
461
+ )
462
+
463
+ self.console.print(table)
464
+
465
+ # Get selections
466
+ selections = Prompt.ask(
467
+ "Which providers would you like to enable? (comma-separated numbers, or 'r' for recommended only)",
468
+ default="r",
469
+ )
470
+
471
+ if selections.lower() == "r":
472
+ # Enable recommended providers
473
+ for provider_info in providers:
474
+ if provider_info.get("recommended", False):
475
+ provider = provider_info["provider"]
476
+ self.user_answers[f"enable_{provider['name']}"] = True
477
+ else:
478
+ # Enable selected providers
479
+ for selection in selections.split(","):
480
+ try:
481
+ idx = int(selection.strip()) - 1
482
+ if 0 <= idx < len(providers):
483
+ provider = providers[idx]["provider"]
484
+ self.user_answers[f"enable_{provider['name']}"] = True
485
+ except (ValueError, IndexError):
486
+ continue
487
+
488
+ return True
489
+
490
+ def _handle_api_keys_step(self, step: Dict[str, Any]) -> bool:
491
+ """Handle API keys input step."""
492
+ content = step.get("content", {})
493
+ self.console.print(f"\n{content.get('message', '')}")
494
+
495
+ # Get enabled providers that need API keys
496
+ enabled_providers = [
497
+ key.replace("enable_", "")
498
+ for key, value in self.user_answers.items()
499
+ if key.startswith("enable_") and value
500
+ ]
501
+
502
+ providers_needing_keys = [
503
+ p for p in enabled_providers if p not in ["claude-code", "ollama"]
504
+ ]
505
+
506
+ if not providers_needing_keys:
507
+ self.console.print(
508
+ "[green]✓[/green] No API keys needed for your selected providers!"
509
+ )
510
+ return True
511
+
512
+ for provider in providers_needing_keys:
513
+ self._get_api_key_for_provider(provider)
514
+
515
+ return True
516
+
517
+ def _get_api_key_for_provider(self, provider_name: str) -> None:
518
+ """Get API key for a specific provider."""
519
+ instructions = self.config_provider.get_provider_setup_instructions(
520
+ provider_name
521
+ )
522
+
523
+ # Show setup instructions
524
+ panel_content = f"**{instructions['title']}**\n\n"
525
+ panel_content += "Steps to get your API key:\n"
526
+ for i, step in enumerate(instructions["steps"], 1):
527
+ panel_content += f"{i}. {step}\n"
528
+
529
+ panel_content += f"\nExpected format: `{instructions['api_key_format']}`\n"
530
+ panel_content += f"Cost: {instructions['cost_info']}"
531
+
532
+ panel = Panel(
533
+ Markdown(panel_content),
534
+ title=f"🔑 {provider_name.title()} Setup",
535
+ border_style="blue",
536
+ padding=(1, 2),
537
+ )
538
+ self.console.print(panel)
539
+
540
+ # Get API key
541
+ api_key = Prompt.ask(
542
+ f"Enter your {provider_name} API key (or 'skip' to configure later)",
543
+ password=True,
544
+ default="",
545
+ )
546
+
547
+ if api_key and api_key.lower() != "skip":
548
+ self.user_answers[f"{provider_name}_api_key"] = api_key
549
+ self.console.print(f"[green]✓[/green] {provider_name} API key saved")
550
+ else:
551
+ self.console.print(
552
+ f"[yellow]⚠[/yellow] {provider_name} will be disabled until API key is added"
553
+ )
554
+ self.user_answers[f"enable_{provider_name}"] = False
555
+
556
+ def _handle_feature_selection_step(self, step: Dict[str, Any]) -> bool:
557
+ """Handle feature selection step."""
558
+ content = step.get("content", {})
559
+ features = content.get("features", [])
560
+
561
+ self.console.print(f"\n{content.get('message', '')}")
562
+
563
+ for feature in features:
564
+ key = feature["key"]
565
+ name = feature["name"]
566
+ description = feature["description"]
567
+ recommended = feature.get("recommended", False)
568
+
569
+ rec_text = " [green](Recommended)[/green]" if recommended else ""
570
+ self.console.print(f"\n**{name}**{rec_text}")
571
+ self.console.print(f" {description}")
572
+
573
+ enable = Confirm.ask(f"Enable {name}?", default=recommended)
574
+ self.user_answers[key] = enable
575
+
576
+ return True
577
+
578
+ def _handle_review_step(self, step: Dict[str, Any]) -> bool:
579
+ """Handle configuration review step."""
580
+ content = step.get("content", {})
581
+ self.console.print(f"\n{content.get('message', '')}")
582
+
583
+ # Show configuration preview
584
+ preview = self.config_provider.get_configuration_preview(self.user_answers)
585
+ self._show_configuration_preview(preview)
586
+
587
+ if not preview.get("setup_complete", False):
588
+ self.console.print("\n[red]Configuration is incomplete:[/red]")
589
+ for issue in preview.get("missing_items", []):
590
+ self.console.print(f" • {issue}")
591
+
592
+ if not Confirm.ask(
593
+ "Continue with incomplete configuration?", default=False
594
+ ):
595
+ return False
596
+
597
+ return Confirm.ask("Save this configuration?", default=True)
598
+
599
+ def _show_configuration_preview(self, preview: Dict[str, Any]) -> None:
600
+ """Show configuration preview."""
601
+ table = Table(title="Configuration Preview")
602
+ table.add_column("Setting", style="cyan")
603
+ table.add_column("Value", style="white")
604
+
605
+ table.add_row("Use Case", preview.get("use_case", "general").title())
606
+ table.add_row(
607
+ "Enabled Providers",
608
+ ", ".join(preview.get("enabled_providers", [])),
609
+ )
610
+ table.add_row(
611
+ "Estimated Cost", preview.get("estimated_monthly_cost", "Unknown")
612
+ )
613
+ table.add_row("Features", ", ".join(preview.get("features_enabled", [])))
614
+
615
+ status_color = "green" if preview.get("setup_complete", False) else "yellow"
616
+ status_text = (
617
+ "Complete" if preview.get("setup_complete", False) else "Incomplete"
618
+ )
619
+ table.add_row("Status", f"[{status_color}]{status_text}[/{status_color}]")
620
+
621
+ self.console.print(table)
622
+
623
+ def _finalize_configuration(self) -> bool:
624
+ """Generate and save the final configuration."""
625
+ self.console.print("\n[blue]Finalizing Configuration...[/blue]")
626
+
627
+ with Progress(
628
+ SpinnerColumn(),
629
+ TextColumn("[progress.description]{task.description}"),
630
+ console=self.console,
631
+ ) as progress:
632
+
633
+ task = progress.add_task("Generating configuration...", total=None)
634
+
635
+ # Generate configuration
636
+ success, config_dict, messages = (
637
+ self.config_provider.generate_simple_configuration(self.user_answers)
638
+ )
639
+ progress.update(task, description="Saving configuration...")
640
+
641
+ if success:
642
+ progress.update(task, description="Validating configuration...")
643
+ time.sleep(1) # Show progress
644
+
645
+ # Validate final configuration
646
+ is_valid, errors = self.config_provider.validate_simple_answers(
647
+ self.user_answers
648
+ )
649
+
650
+ if is_valid:
651
+ progress.update(task, description="Configuration complete!")
652
+ time.sleep(1)
653
+
654
+ # Show results
655
+ if success:
656
+ self.console.print(
657
+ "\n[green]✅ Configuration completed successfully![/green]"
658
+ )
659
+
660
+ for message in messages:
661
+ self.console.print(f" • {message}")
662
+
663
+ # Show next steps
664
+ self._show_next_steps()
665
+ return True
666
+ else:
667
+ self.console.print("\n[red]❌ Configuration failed.[/red]")
668
+ for message in messages:
669
+ self.console.print(f" • {message}")
670
+ return False
671
+
672
+ def _show_next_steps(self) -> None:
673
+ """Show next steps after successful configuration."""
674
+ next_steps_text = """
675
+ # 🎉 You're all set!
676
+
677
+ Omnimancer is now configured and ready to use. Here's what you can do next:
678
+
679
+ ## Try it out:
680
+ ```bash
681
+ omnimancer "Hello! How are you today?"
682
+ ```
683
+
684
+ ## Check your configuration:
685
+ ```bash
686
+ omnimancer config show
687
+ ```
688
+
689
+ ## Switch between providers:
690
+ ```bash
691
+ omnimancer --provider claude "Write me a Python function"
692
+ omnimancer --provider openai "Help me debug this code"
693
+ ```
694
+
695
+ ## Get help:
696
+ ```bash
697
+ omnimancer --help
698
+ ```
699
+
700
+ ## Advanced features:
701
+ - Use `omnimancer config advanced` to access all settings
702
+ - Visit our documentation for tips and tricks
703
+ - Join our community for support and updates
704
+ """
705
+
706
+ panel = Panel(
707
+ Markdown(next_steps_text),
708
+ title="🚀 Ready to Chat!",
709
+ border_style="green",
710
+ padding=(1, 2),
711
+ )
712
+ self.console.print(panel)
713
+
714
+ def _configure_single_provider(self, provider) -> None:
715
+ """Configure a single provider."""
716
+ self.console.print(f"\nConfiguring {provider.display_name}...")
717
+
718
+ if provider.name not in ["claude-code", "ollama"]:
719
+ self._get_api_key_for_provider(provider.name)
720
+
721
+ # Add to current configuration
722
+ # This would integrate with the actual config manager
723
+ self.console.print(f"[green]✓[/green] {provider.display_name} configured")
724
+
725
+
726
+ def run_config_setup_wizard(
727
+ config_manager: Optional[ConfigManager] = None,
728
+ ) -> bool:
729
+ """
730
+ Run the configuration setup wizard.
731
+
732
+ Args:
733
+ config_manager: Optional ConfigManager instance
734
+
735
+ Returns:
736
+ True if setup completed successfully, False otherwise
737
+ """
738
+ wizard = ConfigSetupWizard(config_manager)
739
+ return wizard.run_initial_setup()
740
+
741
+
742
+ def run_quick_setup(use_case: str = "general", providers: List[str] = None) -> bool:
743
+ """
744
+ Run a quick setup with minimal prompts.
745
+
746
+ Args:
747
+ use_case: Primary use case for the configuration
748
+ providers: List of provider names to enable
749
+
750
+ Returns:
751
+ True if setup completed successfully, False otherwise
752
+ """
753
+ console = Console()
754
+ ConfigManager()
755
+ simple_interface = create_simple_config_interface()
756
+
757
+ console.print(f"[blue]Quick Setup for {use_case.title()} Use Case[/blue]")
758
+
759
+ # Get recommended configuration
760
+ context = ConfigurationContext(use_case=use_case)
761
+ recommendation = simple_interface.config_mode_manager.get_recommended_configuration(
762
+ context
763
+ )
764
+
765
+ console.print(f"Using template: {recommendation.template_name}")
766
+
767
+ # Apply template
768
+ success, messages = (
769
+ simple_interface.config_mode_manager.apply_template_configuration(
770
+ recommendation.template_name
771
+ )
772
+ )
773
+
774
+ if success:
775
+ console.print("[green]✅ Quick setup completed![/green]")
776
+ for message in messages:
777
+ console.print(f" • {message}")
778
+ return True
779
+ else:
780
+ console.print("[red]❌ Quick setup failed.[/red]")
781
+ for message in messages:
782
+ console.print(f" • {message}")
783
+ return False