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.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|