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,944 @@
1
+ """
2
+ Agent Creation Wizard for Omnimancer.
3
+
4
+ Interactive CLI wizard that guides users through creating custom agent
5
+ configurations step-by-step with validation and real-time feedback.
6
+ """
7
+
8
+ import logging
9
+ from typing import List, Optional, Set
10
+
11
+ from rich.align import Align
12
+ from rich.box import ROUNDED
13
+ from rich.console import Console
14
+ from rich.panel import Panel
15
+ from rich.prompt import Confirm, FloatPrompt, IntPrompt, Prompt
16
+ from rich.table import Table
17
+ from rich.text import Text
18
+
19
+ from ..core.agent.agent_config import (
20
+ AgentRepository,
21
+ AgentTool,
22
+ BehaviorRules,
23
+ ContextParameters,
24
+ CustomAgentConfig,
25
+ CustomAgentStatus,
26
+ CustomAgentValidator,
27
+ ModelSettings,
28
+ )
29
+ from ..core.agent.config import ProviderType
30
+ from ..core.agent.persona import PersonaCapability, PersonaCategory
31
+ from ..core.models import ConfigTemplateManager
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class AgentCreationWizard:
37
+ """Interactive wizard for creating custom agents."""
38
+
39
+ def __init__(
40
+ self,
41
+ console: Optional[Console] = None,
42
+ template_manager: Optional[ConfigTemplateManager] = None,
43
+ repository: Optional[AgentRepository] = None,
44
+ ):
45
+ """
46
+ Initialize the agent creation wizard.
47
+
48
+ Args:
49
+ console: Rich console for output
50
+ template_manager: Configuration template manager
51
+ repository: Agent repository for storage
52
+ """
53
+ self.console = console or Console()
54
+ self.template_manager = template_manager or ConfigTemplateManager()
55
+ self.repository = repository or AgentRepository()
56
+ self.validator = CustomAgentValidator()
57
+
58
+ # Wizard state
59
+ self.current_config: Optional[CustomAgentConfig] = None
60
+ self.step_history: List[str] = []
61
+
62
+ async def run_wizard(
63
+ self, base_config: Optional[CustomAgentConfig] = None
64
+ ) -> Optional[CustomAgentConfig]:
65
+ """
66
+ Run the complete agent creation wizard.
67
+
68
+ Args:
69
+ base_config: Optional base configuration to start with
70
+
71
+ Returns:
72
+ Created agent configuration or None if cancelled
73
+ """
74
+ try:
75
+ self._show_welcome()
76
+
77
+ # Initialize with base config or create new
78
+ self.current_config = base_config or CustomAgentConfig()
79
+
80
+ # Run wizard steps
81
+ if not await self._step_basic_info():
82
+ return None
83
+
84
+ if not await self._step_select_template():
85
+ return None
86
+
87
+ if not await self._step_model_settings():
88
+ return None
89
+
90
+ if not await self._step_tools_capabilities():
91
+ return None
92
+
93
+ if not await self._step_context_parameters():
94
+ return None
95
+
96
+ if not await self._step_behavior_rules():
97
+ return None
98
+
99
+ if not await self._step_review_and_save():
100
+ return None
101
+
102
+ return self.current_config
103
+
104
+ except KeyboardInterrupt:
105
+ self.console.print("\\n[yellow]Wizard cancelled by user.[/yellow]")
106
+ return None
107
+ except Exception as e:
108
+ self.console.print(f"\\n[red]Wizard error: {e}[/red]")
109
+ logger.error(f"Agent creation wizard error: {e}", exc_info=True)
110
+ return None
111
+
112
+ def _show_welcome(self):
113
+ """Show welcome message and instructions."""
114
+ welcome_text = Text.assemble(
115
+ "Welcome to the ",
116
+ ("Agent Creation Wizard", "bold cyan"),
117
+ "!\\n\\n",
118
+ "This wizard will guide you through creating a custom agent configuration.\\n",
119
+ "You can press ",
120
+ ("Ctrl+C", "bold"),
121
+ " at any time to cancel.\\n\\n",
122
+ "Let's get started! 🚀",
123
+ )
124
+
125
+ panel = Panel(
126
+ Align.center(welcome_text),
127
+ title="🤖 Custom Agent Creator",
128
+ box=ROUNDED,
129
+ padding=(1, 2),
130
+ )
131
+
132
+ self.console.print(panel)
133
+ self.console.print()
134
+
135
+ async def _step_basic_info(self) -> bool:
136
+ """Step 1: Collect basic agent information."""
137
+ self.console.rule("📝 Step 1: Basic Information")
138
+
139
+ # Agent name
140
+ while True:
141
+ name = Prompt.ask(
142
+ "Enter a name for your agent",
143
+ default=(
144
+ self.current_config.name
145
+ if self.current_config.name != "Untitled Agent"
146
+ else ""
147
+ ),
148
+ ).strip()
149
+
150
+ if not name:
151
+ self.console.print("[red]Agent name cannot be empty.[/red]")
152
+ continue
153
+
154
+ if len(name) > 100:
155
+ self.console.print(
156
+ "[red]Agent name too long (max 100 characters).[/red]"
157
+ )
158
+ continue
159
+
160
+ # Check for name conflicts
161
+ existing = self.repository.get_by_name(name)
162
+ if existing:
163
+ self.console.print(
164
+ f"[yellow]An agent named '{name}' already exists.[/yellow]"
165
+ )
166
+ if not Confirm.ask(
167
+ "Do you want to use a different name?", default=True
168
+ ):
169
+ continue
170
+
171
+ self.current_config.name = name
172
+ break
173
+
174
+ # Description
175
+ description = Prompt.ask(
176
+ "Enter a description for your agent (optional)",
177
+ default=self.current_config.description,
178
+ ).strip()
179
+
180
+ if len(description) > 500:
181
+ self.console.print(
182
+ "[yellow]Description truncated to 500 characters.[/yellow]"
183
+ )
184
+ description = description[:500]
185
+
186
+ self.current_config.description = description
187
+
188
+ # Category
189
+ self.console.print("\\nSelect a category for your agent:")
190
+ categories = list(PersonaCategory)
191
+ for i, category in enumerate(categories, 1):
192
+ self.console.print(f" {i}. {category.value.title()}")
193
+
194
+ while True:
195
+ try:
196
+ choice = IntPrompt.ask(
197
+ "Category",
198
+ default=categories.index(self.current_config.category) + 1,
199
+ show_default=True,
200
+ )
201
+ if 1 <= choice <= len(categories):
202
+ self.current_config.category = categories[choice - 1]
203
+ break
204
+ else:
205
+ self.console.print(
206
+ f"[red]Please enter a number between 1 and {len(categories)}.[/red]"
207
+ )
208
+ except (ValueError, EOFError):
209
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
210
+
211
+ self.step_history.append("basic_info")
212
+ return True
213
+
214
+ async def _step_select_template(self) -> bool:
215
+ """Step 2: Select base template (optional)."""
216
+ self.console.rule("🎯 Step 2: Base Template")
217
+
218
+ self.console.print(
219
+ "You can base your agent on an existing template for easier setup."
220
+ )
221
+
222
+ use_template = Confirm.ask(
223
+ "Would you like to use a base template?", default=False
224
+ )
225
+
226
+ if not use_template:
227
+ self.current_config.base_template_id = None
228
+ self.step_history.append("template_skipped")
229
+ return True
230
+
231
+ # Get available templates
232
+ try:
233
+ templates = self.template_manager.list_templates()
234
+
235
+ if not templates:
236
+ self.console.print(
237
+ "[yellow]No templates available. Continuing without template.[/yellow]"
238
+ )
239
+ self.current_config.base_template_id = None
240
+ self.step_history.append("no_templates")
241
+ return True
242
+
243
+ self.console.print("\\nAvailable templates:")
244
+ for i, template in enumerate(templates, 1):
245
+ description = template.description[:60] + (
246
+ "..." if len(template.description) > 60 else ""
247
+ )
248
+ self.console.print(f" {i}. {template.name} - {description}")
249
+
250
+ while True:
251
+ try:
252
+ choice = IntPrompt.ask(
253
+ "Select template (0 for none)",
254
+ default=0,
255
+ show_default=True,
256
+ )
257
+
258
+ if choice == 0:
259
+ self.current_config.base_template_id = None
260
+ break
261
+ elif 1 <= choice <= len(templates):
262
+ selected_template = templates[choice - 1]
263
+ self.current_config.base_template_id = selected_template.id
264
+
265
+ # Inherit from template
266
+ self.current_config.inherit_from_template(self.template_manager)
267
+
268
+ self.console.print(
269
+ f"[green]Selected template: {selected_template.name}[/green]"
270
+ )
271
+ break
272
+ else:
273
+ self.console.print(
274
+ f"[red]Please enter a number between 0 and {len(templates)}.[/red]"
275
+ )
276
+ except (ValueError, EOFError):
277
+ self.console.print(
278
+ "[red]Invalid input. Please enter a number.[/red]"
279
+ )
280
+
281
+ except Exception as e:
282
+ self.console.print(f"[red]Error loading templates: {e}[/red]")
283
+ self.current_config.base_template_id = None
284
+
285
+ self.step_history.append("template_selected")
286
+ return True
287
+
288
+ async def _step_model_settings(self) -> bool:
289
+ """Step 3: Configure model settings."""
290
+ self.console.rule("🧠 Step 3: Model Configuration")
291
+
292
+ current_settings = self.current_config.model_settings
293
+
294
+ # Provider selection
295
+ self.console.print("\\nSelect AI provider:")
296
+ providers = list(ProviderType)
297
+ for i, provider in enumerate(providers, 1):
298
+ self.console.print(f" {i}. {provider.value}")
299
+
300
+ while True:
301
+ try:
302
+ choice = IntPrompt.ask(
303
+ "Provider",
304
+ default=providers.index(current_settings.provider_type) + 1,
305
+ show_default=True,
306
+ )
307
+ if 1 <= choice <= len(providers):
308
+ provider_type = providers[choice - 1]
309
+ break
310
+ else:
311
+ self.console.print(
312
+ f"[red]Please enter a number between 1 and {len(providers)}.[/red]"
313
+ )
314
+ except (ValueError, EOFError):
315
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
316
+
317
+ # Model name
318
+ model_suggestions = self._get_model_suggestions(provider_type)
319
+ if model_suggestions:
320
+ self.console.print(f"\\nSuggested models for {provider_type.value}:")
321
+ for suggestion in model_suggestions[:5]: # Show top 5
322
+ self.console.print(f" • {suggestion}")
323
+
324
+ model_name = Prompt.ask(
325
+ "Model name", default=current_settings.model_name
326
+ ).strip()
327
+
328
+ if not model_name:
329
+ model_name = self._get_default_model(provider_type)
330
+ self.console.print(f"[yellow]Using default model: {model_name}[/yellow]")
331
+
332
+ # Temperature
333
+ while True:
334
+ try:
335
+ temperature = FloatPrompt.ask(
336
+ "Temperature (0.0 = deterministic, 2.0 = very creative)",
337
+ default=current_settings.temperature,
338
+ show_default=True,
339
+ )
340
+ if 0.0 <= temperature <= 2.0:
341
+ break
342
+ else:
343
+ self.console.print(
344
+ "[red]Temperature must be between 0.0 and 2.0.[/red]"
345
+ )
346
+ except (ValueError, EOFError):
347
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
348
+
349
+ # Max tokens (optional)
350
+ max_tokens = None
351
+ if Confirm.ask("Set maximum response length?", default=False):
352
+ while True:
353
+ try:
354
+ max_tokens = IntPrompt.ask(
355
+ "Maximum tokens",
356
+ default=current_settings.max_tokens or 4096,
357
+ show_default=True,
358
+ )
359
+ if max_tokens > 0:
360
+ break
361
+ else:
362
+ self.console.print(
363
+ "[red]Maximum tokens must be positive.[/red]"
364
+ )
365
+ except (ValueError, EOFError):
366
+ self.console.print(
367
+ "[red]Invalid input. Please enter a number.[/red]"
368
+ )
369
+
370
+ # Update model settings
371
+ self.current_config.model_settings = ModelSettings(
372
+ provider_type=provider_type,
373
+ model_name=model_name,
374
+ temperature=temperature,
375
+ max_tokens=max_tokens,
376
+ )
377
+
378
+ self.step_history.append("model_configured")
379
+ return True
380
+
381
+ async def _step_tools_capabilities(self) -> bool:
382
+ """Step 4: Select tools and capabilities."""
383
+ self.console.rule("🔧 Step 4: Tools and Capabilities")
384
+
385
+ # Tools selection
386
+ self.console.print("\\nSelect tools to enable for your agent:")
387
+ all_tools = list(AgentTool)
388
+ current_tools = self.current_config.enabled_tools
389
+
390
+ tool_table = Table(show_header=True, header_style="bold magenta")
391
+ tool_table.add_column("Tool", style="cyan")
392
+ tool_table.add_column("Description", style="white")
393
+ tool_table.add_column("Enabled", style="green")
394
+
395
+ tool_descriptions = {
396
+ AgentTool.FILE_OPERATIONS: "Read, write, and manage files",
397
+ AgentTool.PROGRAM_EXECUTOR: "Execute system commands and programs",
398
+ AgentTool.WEB_CLIENT: "Make web requests and browse content",
399
+ AgentTool.MCP_TOOLS: "Model Context Protocol integration",
400
+ AgentTool.SEARCH: "Search capabilities and information retrieval",
401
+ AgentTool.CODE_ANALYSIS: "Analyze and understand code structure",
402
+ AgentTool.SYSTEM_INFO: "Access system information and diagnostics",
403
+ }
404
+
405
+ for tool in all_tools:
406
+ description = tool_descriptions.get(tool, "Advanced tool functionality")
407
+ enabled = "✅" if tool in current_tools else "❌"
408
+ tool_table.add_row(
409
+ tool.value.replace("_", " ").title(), description, enabled
410
+ )
411
+
412
+ self.console.print(tool_table)
413
+
414
+ # Interactive tool selection
415
+ selected_tools = set(current_tools)
416
+
417
+ while True:
418
+ self.console.print("\\nTool selection:")
419
+ self.console.print("1. Toggle individual tools")
420
+ self.console.print("2. Select preset configuration")
421
+ self.console.print("3. Continue with current selection")
422
+
423
+ choice = Prompt.ask("Choose option", choices=["1", "2", "3"], default="3")
424
+
425
+ if choice == "1":
426
+ selected_tools = await self._interactive_tool_selection(
427
+ all_tools, selected_tools
428
+ )
429
+ elif choice == "2":
430
+ selected_tools = await self._preset_tool_selection()
431
+ else:
432
+ break
433
+
434
+ self.current_config.enabled_tools = selected_tools
435
+
436
+ # Capabilities selection
437
+ self.console.print("\\nSelect capabilities for your agent:")
438
+ all_capabilities = list(PersonaCapability)
439
+ current_capabilities = self.current_config.capabilities
440
+
441
+ # Smart capability suggestions based on selected tools
442
+ suggested_capabilities = self._suggest_capabilities_for_tools(selected_tools)
443
+
444
+ if suggested_capabilities:
445
+ self.console.print(
446
+ "\\n[cyan]Suggested capabilities based on selected tools:[/cyan]"
447
+ )
448
+ for cap in suggested_capabilities:
449
+ self.console.print(f" • {cap.value.replace('_', ' ').title()}")
450
+
451
+ if Confirm.ask("Use suggested capabilities?", default=True):
452
+ self.current_config.capabilities = suggested_capabilities
453
+ else:
454
+ self.current_config.capabilities = (
455
+ await self._interactive_capability_selection(
456
+ all_capabilities, current_capabilities
457
+ )
458
+ )
459
+ else:
460
+ self.current_config.capabilities = (
461
+ await self._interactive_capability_selection(
462
+ all_capabilities, current_capabilities
463
+ )
464
+ )
465
+
466
+ self.step_history.append("tools_capabilities_configured")
467
+ return True
468
+
469
+ async def _step_context_parameters(self) -> bool:
470
+ """Step 5: Configure context and behavior parameters."""
471
+ self.console.rule("💭 Step 5: Context Configuration")
472
+
473
+ current_params = self.current_config.context_parameters
474
+
475
+ # System prompt
476
+ self.console.print("\\nConfigure the system prompt for your agent:")
477
+ self.console.print("This defines the agent's personality and behavior.")
478
+
479
+ current_prompt = current_params.system_prompt
480
+ if current_prompt:
481
+ self.console.print(f"\\nCurrent prompt preview:")
482
+ preview = current_prompt[:200] + (
483
+ "..." if len(current_prompt) > 200 else ""
484
+ )
485
+ self.console.print(Panel(preview, box=ROUNDED))
486
+
487
+ if Confirm.ask("Edit system prompt?", default=bool(current_prompt)):
488
+ # For simplicity, use basic input. In a full implementation,
489
+ # this could open an editor or provide multi-line input
490
+ system_prompt = Prompt.ask("System prompt", default=current_prompt).strip()
491
+ else:
492
+ system_prompt = current_prompt
493
+
494
+ # Context window size
495
+ context_size = IntPrompt.ask(
496
+ "Context window size (tokens)",
497
+ default=current_params.context_window_size,
498
+ show_default=True,
499
+ )
500
+
501
+ if context_size < 1000:
502
+ self.console.print(
503
+ "[yellow]Warning: Very small context window may limit functionality.[/yellow]"
504
+ )
505
+
506
+ # Memory settings
507
+ conversation_memory = Confirm.ask(
508
+ "Enable conversation memory?",
509
+ default=current_params.conversation_memory,
510
+ )
511
+
512
+ memory_limit = current_params.memory_limit
513
+ if conversation_memory:
514
+ memory_limit = IntPrompt.ask(
515
+ "Memory limit (number of messages to remember)",
516
+ default=current_params.memory_limit,
517
+ show_default=True,
518
+ )
519
+
520
+ # Response format
521
+ self.console.print("\\nResponse format options:")
522
+ self.console.print("1. Default - Natural conversational responses")
523
+ self.console.print("2. JSON - Structured JSON responses")
524
+ self.console.print("3. Markdown - Markdown-formatted responses")
525
+
526
+ format_choices = ["default", "json", "markdown"]
527
+ format_choice = Prompt.ask(
528
+ "Response format", choices=["1", "2", "3"], default="1"
529
+ )
530
+
531
+ response_format = format_choices[int(format_choice) - 1]
532
+
533
+ # Update context parameters
534
+ self.current_config.context_parameters = ContextParameters(
535
+ system_prompt=system_prompt,
536
+ context_window_size=context_size,
537
+ conversation_memory=conversation_memory,
538
+ memory_limit=memory_limit,
539
+ response_format=response_format,
540
+ )
541
+
542
+ self.step_history.append("context_configured")
543
+ return True
544
+
545
+ async def _step_behavior_rules(self) -> bool:
546
+ """Step 6: Configure behavior rules and constraints."""
547
+ self.console.rule("⚙️ Step 6: Behavior Rules")
548
+
549
+ current_rules = self.current_config.behavior_rules
550
+
551
+ # Safety level
552
+ self.console.print("\\nSelect safety level:")
553
+ self.console.print("1. Strict - Maximum safety, conservative behavior")
554
+ self.console.print("2. Standard - Balanced safety and functionality")
555
+ self.console.print("3. Permissive - Minimal restrictions, maximum freedom")
556
+
557
+ safety_levels = ["strict", "standard", "permissive"]
558
+ safety_choice = Prompt.ask("Safety level", choices=["1", "2", "3"], default="2")
559
+ safety_level = safety_levels[int(safety_choice) - 1]
560
+
561
+ # Reasoning style
562
+ self.console.print("\\nSelect reasoning style:")
563
+ self.console.print("1. Concise - Brief, to-the-point responses")
564
+ self.console.print("2. Balanced - Moderate detail level")
565
+ self.console.print("3. Verbose - Detailed explanations")
566
+ self.console.print("4. Step-by-step - Explicit reasoning process")
567
+
568
+ reasoning_styles = ["concise", "balanced", "verbose", "step_by_step"]
569
+ reasoning_choice = Prompt.ask(
570
+ "Reasoning style", choices=["1", "2", "3", "4"], default="2"
571
+ )
572
+ reasoning_style = reasoning_styles[int(reasoning_choice) - 1]
573
+
574
+ # Creativity level
575
+ self.console.print("\\nSelect creativity level:")
576
+ self.console.print("1. Low - Conservative, predictable responses")
577
+ self.console.print("2. Medium - Balanced creativity")
578
+ self.console.print("3. High - Creative, exploratory responses")
579
+
580
+ creativity_levels = ["low", "medium", "high"]
581
+ creativity_choice = Prompt.ask(
582
+ "Creativity level", choices=["1", "2", "3"], default="2"
583
+ )
584
+ creativity_level = creativity_levels[int(creativity_choice) - 1]
585
+
586
+ # Confirmation requirements
587
+ require_confirmation = []
588
+
589
+ self.console.print("\\nConfigure operations that require user confirmation:")
590
+ confirmation_options = [
591
+ ("file_delete", "File deletion operations"),
592
+ ("system_commands", "System command execution"),
593
+ ("web_requests", "External web requests"),
594
+ ("code_execution", "Code execution"),
595
+ ("data_modification", "Data modification operations"),
596
+ ]
597
+
598
+ for option, description in confirmation_options:
599
+ if Confirm.ask(
600
+ f"Require confirmation for {description}?",
601
+ default=option in current_rules.require_confirmation_for,
602
+ ):
603
+ require_confirmation.append(option)
604
+
605
+ # Update behavior rules
606
+ self.current_config.behavior_rules = BehaviorRules(
607
+ require_confirmation_for=require_confirmation,
608
+ reasoning_style=reasoning_style,
609
+ creativity_level=creativity_level,
610
+ safety_level=safety_level,
611
+ custom_restrictions=current_rules.custom_restrictions, # Keep existing custom restrictions
612
+ )
613
+
614
+ self.step_history.append("behavior_configured")
615
+ return True
616
+
617
+ async def _step_review_and_save(self) -> bool:
618
+ """Step 7: Review configuration and save."""
619
+ self.console.rule("📋 Step 7: Review and Save")
620
+
621
+ # Show configuration summary
622
+ self._show_configuration_summary()
623
+
624
+ # Validation
625
+ try:
626
+ self.validator.validate_config(self.current_config)
627
+ self.console.print("[green]✅ Configuration is valid![/green]")
628
+ except ValueError as e:
629
+ self.console.print(f"[red]❌ Configuration validation failed: {e}[/red]")
630
+ if not Confirm.ask("Continue anyway?", default=False):
631
+ return False
632
+
633
+ # Final confirmation
634
+ if not Confirm.ask("\\nSave this agent configuration?", default=True):
635
+ return False
636
+
637
+ # Save the configuration
638
+ try:
639
+ self.current_config.status = CustomAgentStatus.ACTIVE
640
+ saved_config = self.repository.create(self.current_config)
641
+
642
+ self.console.print(
643
+ f"\\n[green]✅ Agent '{saved_config.name}' created successfully![/green]"
644
+ )
645
+ self.console.print(f"[cyan]Agent ID: {saved_config.id}[/cyan]")
646
+
647
+ return True
648
+
649
+ except Exception as e:
650
+ self.console.print(f"[red]❌ Failed to save agent: {e}[/red]")
651
+ return False
652
+
653
+ def _show_configuration_summary(self):
654
+ """Display a summary of the current configuration."""
655
+ config = self.current_config
656
+
657
+ # Basic info
658
+ basic_table = Table(show_header=False, box=None)
659
+ basic_table.add_column("Field", style="cyan")
660
+ basic_table.add_column("Value", style="white")
661
+
662
+ basic_table.add_row("Name", config.name)
663
+ basic_table.add_row("Description", config.description or "[dim]None[/dim]")
664
+ basic_table.add_row("Category", config.category.value.title())
665
+ basic_table.add_row(
666
+ "Base Template", config.base_template_id or "[dim]None[/dim]"
667
+ )
668
+
669
+ self.console.print(Panel(basic_table, title="Basic Information", box=ROUNDED))
670
+
671
+ # Model settings
672
+ model_table = Table(show_header=False, box=None)
673
+ model_table.add_column("Field", style="cyan")
674
+ model_table.add_column("Value", style="white")
675
+
676
+ model_table.add_row("Provider", config.model_settings.provider_type.value)
677
+ model_table.add_row("Model", config.model_settings.model_name)
678
+ model_table.add_row("Temperature", str(config.model_settings.temperature))
679
+ model_table.add_row(
680
+ "Max Tokens",
681
+ (
682
+ str(config.model_settings.max_tokens)
683
+ if config.model_settings.max_tokens
684
+ else "[dim]Unlimited[/dim]"
685
+ ),
686
+ )
687
+
688
+ self.console.print(Panel(model_table, title="Model Configuration", box=ROUNDED))
689
+
690
+ # Tools and capabilities
691
+ tools_text = ", ".join(
692
+ [tool.value.replace("_", " ").title() for tool in config.enabled_tools]
693
+ )
694
+ caps_text = ", ".join(
695
+ [cap.value.replace("_", " ").title() for cap in config.capabilities]
696
+ )
697
+
698
+ tools_table = Table(show_header=False, box=None)
699
+ tools_table.add_column("Field", style="cyan")
700
+ tools_table.add_column("Value", style="white")
701
+
702
+ tools_table.add_row("Tools", tools_text or "[dim]None[/dim]")
703
+ tools_table.add_row("Capabilities", caps_text or "[dim]None[/dim]")
704
+
705
+ self.console.print(
706
+ Panel(tools_table, title="Tools and Capabilities", box=ROUNDED)
707
+ )
708
+
709
+ async def _interactive_tool_selection(
710
+ self, all_tools: List[AgentTool], current_selection: Set[AgentTool]
711
+ ) -> Set[AgentTool]:
712
+ """Interactive tool selection interface."""
713
+ selected = set(current_selection)
714
+
715
+ while True:
716
+ self.console.print("\\nCurrent tool selection:")
717
+ for i, tool in enumerate(all_tools, 1):
718
+ status = "✅" if tool in selected else "❌"
719
+ self.console.print(
720
+ f" {i}. {status} {tool.value.replace('_', ' ').title()}"
721
+ )
722
+
723
+ self.console.print(f"\\n {len(all_tools) + 1}. Done")
724
+
725
+ try:
726
+ choice = IntPrompt.ask(
727
+ "Toggle tool (enter number)",
728
+ default=len(all_tools) + 1,
729
+ show_default=False,
730
+ )
731
+
732
+ if choice == len(all_tools) + 1:
733
+ break
734
+ elif 1 <= choice <= len(all_tools):
735
+ tool = all_tools[choice - 1]
736
+ if tool in selected:
737
+ selected.remove(tool)
738
+ else:
739
+ selected.add(tool)
740
+ else:
741
+ self.console.print(
742
+ f"[red]Please enter a number between 1 and {len(all_tools) + 1}.[/red]"
743
+ )
744
+ except (ValueError, EOFError):
745
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
746
+
747
+ return selected
748
+
749
+ async def _preset_tool_selection(self) -> Set[AgentTool]:
750
+ """Tool selection from presets."""
751
+ presets = {
752
+ "minimal": {AgentTool.FILE_OPERATIONS},
753
+ "standard": {
754
+ AgentTool.FILE_OPERATIONS,
755
+ AgentTool.PROGRAM_EXECUTOR,
756
+ AgentTool.SYSTEM_INFO,
757
+ },
758
+ "web_enabled": {
759
+ AgentTool.FILE_OPERATIONS,
760
+ AgentTool.PROGRAM_EXECUTOR,
761
+ AgentTool.WEB_CLIENT,
762
+ AgentTool.SEARCH,
763
+ },
764
+ "full_featured": set(AgentTool),
765
+ }
766
+
767
+ self.console.print("\\nTool presets:")
768
+ preset_names = list(presets.keys())
769
+ for i, name in enumerate(preset_names, 1):
770
+ tool_list = ", ".join(
771
+ [t.value.replace("_", " ").title() for t in presets[name]]
772
+ )
773
+ self.console.print(f" {i}. {name.title()}: {tool_list}")
774
+
775
+ while True:
776
+ try:
777
+ choice = IntPrompt.ask(
778
+ "Select preset", default=2, show_default=True # Standard
779
+ )
780
+ if 1 <= choice <= len(preset_names):
781
+ return presets[preset_names[choice - 1]]
782
+ else:
783
+ self.console.print(
784
+ f"[red]Please enter a number between 1 and {len(preset_names)}.[/red]"
785
+ )
786
+ except (ValueError, EOFError):
787
+ self.console.print("[red]Invalid input. Please enter a number.[/red]")
788
+
789
+ async def _interactive_capability_selection(
790
+ self,
791
+ all_capabilities: List[PersonaCapability],
792
+ current_selection: Set[PersonaCapability],
793
+ ) -> Set[PersonaCapability]:
794
+ """Interactive capability selection interface."""
795
+ # Group capabilities for easier selection
796
+ capability_groups = {
797
+ "Core": [
798
+ PersonaCapability.GENERAL_PURPOSE,
799
+ PersonaCapability.REASONING,
800
+ PersonaCapability.ANALYSIS,
801
+ ],
802
+ "Development": [
803
+ PersonaCapability.CODE_GENERATION,
804
+ PersonaCapability.FILE_OPERATIONS,
805
+ PersonaCapability.TOOL_CALLING,
806
+ ],
807
+ "Research": [
808
+ PersonaCapability.RESEARCH,
809
+ PersonaCapability.WEB_SEARCH,
810
+ PersonaCapability.LARGE_CONTEXT,
811
+ ],
812
+ "Creative": [
813
+ PersonaCapability.CREATIVE_WRITING,
814
+ PersonaCapability.HIGH_TEMPERATURE,
815
+ ],
816
+ "Performance": [
817
+ PersonaCapability.FAST_RESPONSE,
818
+ PersonaCapability.COST_EFFICIENT,
819
+ PersonaCapability.BALANCED,
820
+ ],
821
+ }
822
+
823
+ selected = set(current_selection)
824
+
825
+ # Show grouped capabilities
826
+ for group_name, group_caps in capability_groups.items():
827
+ self.console.print(f"\\n[bold]{group_name} Capabilities:[/bold]")
828
+ for cap in group_caps:
829
+ if cap in all_capabilities:
830
+ status = "✅" if cap in selected else "❌"
831
+ self.console.print(
832
+ f" {status} {cap.value.replace('_', ' ').title()}"
833
+ )
834
+
835
+ # Simple yes/no for each group
836
+ for group_name, group_caps in capability_groups.items():
837
+ available_caps = [cap for cap in group_caps if cap in all_capabilities]
838
+ if not available_caps:
839
+ continue
840
+
841
+ current_count = len([cap for cap in available_caps if cap in selected])
842
+
843
+ if Confirm.ask(
844
+ f"\\nEnable {group_name.lower()} capabilities? (currently {current_count}/{len(available_caps)})",
845
+ default=current_count > 0,
846
+ ):
847
+ selected.update(available_caps)
848
+ else:
849
+ selected.difference_update(available_caps)
850
+
851
+ return selected
852
+
853
+ def _suggest_capabilities_for_tools(
854
+ self, tools: Set[AgentTool]
855
+ ) -> Set[PersonaCapability]:
856
+ """Suggest capabilities based on selected tools."""
857
+ suggestions = {PersonaCapability.GENERAL_PURPOSE}
858
+
859
+ if AgentTool.FILE_OPERATIONS in tools:
860
+ suggestions.add(PersonaCapability.FILE_OPERATIONS)
861
+ suggestions.add(PersonaCapability.CODE_GENERATION)
862
+
863
+ if AgentTool.WEB_CLIENT in tools or AgentTool.SEARCH in tools:
864
+ suggestions.add(PersonaCapability.WEB_SEARCH)
865
+ suggestions.add(PersonaCapability.RESEARCH)
866
+
867
+ if AgentTool.PROGRAM_EXECUTOR in tools:
868
+ suggestions.add(PersonaCapability.TOOL_CALLING)
869
+
870
+ if AgentTool.CODE_ANALYSIS in tools:
871
+ suggestions.add(PersonaCapability.ANALYSIS)
872
+ suggestions.add(PersonaCapability.REASONING)
873
+
874
+ return suggestions
875
+
876
+ def _get_model_suggestions(self, provider_type: ProviderType) -> List[str]:
877
+ """Get model suggestions for a provider."""
878
+ suggestions = {
879
+ ProviderType.ANTHROPIC: [
880
+ "claude-3-opus-20240229",
881
+ "claude-3-sonnet-20240229",
882
+ "claude-3-haiku-20240307",
883
+ ],
884
+ ProviderType.OPENAI: [
885
+ "gpt-4",
886
+ "gpt-4-turbo",
887
+ "gpt-3.5-turbo",
888
+ "gpt-4o",
889
+ ],
890
+ ProviderType.GOOGLE: [
891
+ "gemini-pro",
892
+ "gemini-pro-vision",
893
+ "gemini-1.5-pro",
894
+ ],
895
+ ProviderType.MISTRAL: [
896
+ "mistral-large",
897
+ "mistral-medium",
898
+ "mistral-small",
899
+ ],
900
+ }
901
+
902
+ return suggestions.get(provider_type, [])
903
+
904
+ def _get_default_model(self, provider_type: ProviderType) -> str:
905
+ """Get default model for a provider."""
906
+ defaults = {
907
+ ProviderType.ANTHROPIC: "claude-3-haiku-20240307",
908
+ ProviderType.OPENAI: "gpt-3.5-turbo",
909
+ ProviderType.GOOGLE: "gemini-pro",
910
+ ProviderType.MISTRAL: "mistral-medium",
911
+ }
912
+
913
+ return defaults.get(provider_type, "default-model")
914
+
915
+
916
+ # Utility functions for easy integration
917
+
918
+
919
+ async def create_agent_wizard(
920
+ console: Optional[Console] = None,
921
+ ) -> AgentCreationWizard:
922
+ """Create and initialize an agent creation wizard."""
923
+ return AgentCreationWizard(console=console)
924
+
925
+
926
+ async def quick_agent_creation(
927
+ name: str,
928
+ provider: str = "anthropic",
929
+ model: str = "claude-3-haiku-20240307",
930
+ ) -> Optional[CustomAgentConfig]:
931
+ """Quick agent creation with minimal configuration."""
932
+ try:
933
+ provider_type = ProviderType(provider.lower())
934
+ except ValueError:
935
+ provider_type = ProviderType.ANTHROPIC
936
+
937
+ config = CustomAgentConfig(
938
+ name=name,
939
+ model_settings=ModelSettings(provider_type=provider_type, model_name=model),
940
+ status=CustomAgentStatus.ACTIVE,
941
+ )
942
+
943
+ repository = AgentRepository()
944
+ return repository.create(config)