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,826 @@
1
+ """
2
+ Agent Discovery and Management Interface for Omnimancer.
3
+
4
+ This module provides CLI commands for managing custom agents including
5
+ listing, searching, loading, activation, and detailed inspection.
6
+ """
7
+
8
+ import logging
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+ from typing import Any, Dict, List, Optional
12
+
13
+ from rich.box import ROUNDED, SIMPLE
14
+ from rich.columns import Columns
15
+ from rich.console import Console
16
+ from rich.panel import Panel
17
+ from rich.progress import Progress, SpinnerColumn, TextColumn
18
+ from rich.prompt import Confirm
19
+ from rich.table import Table
20
+ from rich.text import Text
21
+
22
+ from ..core.agent.agent_config import (
23
+ AgentRepository,
24
+ CustomAgentConfig,
25
+ CustomAgentStatus,
26
+ )
27
+ from ..core.agent.persona import PersonaCategory
28
+ from .agent_wizard import AgentCreationWizard
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ class AgentManager:
34
+ """Manager for custom agent discovery and operations."""
35
+
36
+ def __init__(
37
+ self,
38
+ console: Optional[Console] = None,
39
+ repository: Optional[AgentRepository] = None,
40
+ ):
41
+ """
42
+ Initialize agent manager.
43
+
44
+ Args:
45
+ console: Rich console for output
46
+ repository: Agent repository for storage operations
47
+ """
48
+ self.console = console or Console()
49
+ self.repository = repository or AgentRepository()
50
+
51
+ # Cache for active agent
52
+ self._active_agent: Optional[CustomAgentConfig] = None
53
+ self._session_stats: Dict[str, Any] = {
54
+ "agents_loaded": 0,
55
+ "agents_activated": 0,
56
+ "session_start": datetime.now(),
57
+ }
58
+
59
+ async def list_agents(
60
+ self,
61
+ category: Optional[str] = None,
62
+ status: Optional[str] = None,
63
+ show_details: bool = False,
64
+ sort_by: str = "name",
65
+ ) -> List[CustomAgentConfig]:
66
+ """
67
+ List all custom agents with optional filtering.
68
+
69
+ Args:
70
+ category: Filter by category (optional)
71
+ status: Filter by status (optional)
72
+ show_details: Show detailed information
73
+ sort_by: Sort field (name, created_at, last_used, usage_count)
74
+
75
+ Returns:
76
+ List of matching agent configurations
77
+ """
78
+ try:
79
+ with Progress(
80
+ SpinnerColumn(),
81
+ TextColumn("[progress.description]{task.description}"),
82
+ console=self.console,
83
+ transient=True,
84
+ ) as progress:
85
+ task = progress.add_task("Loading agents...", total=None)
86
+
87
+ # Get all agents
88
+ all_agents = self.repository.list_all()
89
+
90
+ # Apply filters
91
+ filtered_agents = all_agents
92
+
93
+ if category:
94
+ try:
95
+ cat_filter = PersonaCategory(category.lower())
96
+ filtered_agents = [
97
+ a for a in filtered_agents if a.category == cat_filter
98
+ ]
99
+ except ValueError:
100
+ self.console.print(f"[red]Invalid category: {category}[/red]")
101
+ return []
102
+
103
+ if status:
104
+ try:
105
+ status_filter = CustomAgentStatus(status.lower())
106
+ filtered_agents = [
107
+ a for a in filtered_agents if a.status == status_filter
108
+ ]
109
+ except ValueError:
110
+ self.console.print(f"[red]Invalid status: {status}[/red]")
111
+ return []
112
+
113
+ # Sort agents
114
+ filtered_agents = self._sort_agents(filtered_agents, sort_by)
115
+
116
+ progress.update(task, completed=100)
117
+
118
+ # Display results
119
+ if not filtered_agents:
120
+ self.console.print(
121
+ "[yellow]No agents found matching the criteria.[/yellow]"
122
+ )
123
+ return []
124
+
125
+ if show_details:
126
+ self._show_detailed_agent_list(filtered_agents)
127
+ else:
128
+ self._show_summary_agent_list(filtered_agents)
129
+
130
+ return filtered_agents
131
+
132
+ except Exception as e:
133
+ self.console.print(f"[red]Error listing agents: {e}[/red]")
134
+ logger.error(f"Agent listing error: {e}", exc_info=True)
135
+ return []
136
+
137
+ async def show_agent(self, identifier: str) -> Optional[CustomAgentConfig]:
138
+ """
139
+ Show detailed information about a specific agent.
140
+
141
+ Args:
142
+ identifier: Agent ID or name
143
+
144
+ Returns:
145
+ Agent configuration if found
146
+ """
147
+ try:
148
+ # Try to find by ID first, then by name
149
+ agent = self.repository.get(identifier)
150
+ if not agent:
151
+ agent = self.repository.get_by_name(identifier)
152
+
153
+ if not agent:
154
+ self.console.print(f"[red]Agent '{identifier}' not found.[/red]")
155
+ return None
156
+
157
+ self._show_agent_details(agent)
158
+ return agent
159
+
160
+ except Exception as e:
161
+ self.console.print(f"[red]Error showing agent: {e}[/red]")
162
+ logger.error(f"Agent show error: {e}", exc_info=True)
163
+ return None
164
+
165
+ async def search_agents(
166
+ self, query: str, fields: Optional[List[str]] = None
167
+ ) -> List[CustomAgentConfig]:
168
+ """
169
+ Search agents by query string.
170
+
171
+ Args:
172
+ query: Search query
173
+ fields: Fields to search in (name, description, capabilities)
174
+
175
+ Returns:
176
+ List of matching agents
177
+ """
178
+ try:
179
+ if not query.strip():
180
+ self.console.print("[red]Search query cannot be empty.[/red]")
181
+ return []
182
+
183
+ with Progress(
184
+ SpinnerColumn(),
185
+ TextColumn("[progress.description]{task.description}"),
186
+ console=self.console,
187
+ transient=True,
188
+ ) as progress:
189
+ task = progress.add_task(f"Searching for '{query}'...", total=None)
190
+
191
+ results = self.repository.search(query, fields)
192
+
193
+ progress.update(task, completed=100)
194
+
195
+ if not results:
196
+ self.console.print(
197
+ f"[yellow]No agents found matching '{query}'.[/yellow]"
198
+ )
199
+ return []
200
+
201
+ self.console.print(
202
+ f"\\n[green]Found {len(results)} agent(s) matching '{query}':[/green]"
203
+ )
204
+ self._show_summary_agent_list(results)
205
+
206
+ return results
207
+
208
+ except Exception as e:
209
+ self.console.print(f"[red]Error searching agents: {e}[/red]")
210
+ logger.error(f"Agent search error: {e}", exc_info=True)
211
+ return []
212
+
213
+ async def load_agent(
214
+ self, identifier: str, activate: bool = True
215
+ ) -> Optional[CustomAgentConfig]:
216
+ """
217
+ Load an agent configuration.
218
+
219
+ Args:
220
+ identifier: Agent ID or name
221
+ activate: Whether to activate the agent for current session
222
+
223
+ Returns:
224
+ Loaded agent configuration
225
+ """
226
+ try:
227
+ # Find the agent
228
+ agent = self.repository.get(identifier)
229
+ if not agent:
230
+ agent = self.repository.get_by_name(identifier)
231
+
232
+ if not agent:
233
+ self.console.print(f"[red]Agent '{identifier}' not found.[/red]")
234
+ return None
235
+
236
+ # Check if agent is in usable state
237
+ if agent.status not in [
238
+ CustomAgentStatus.ACTIVE,
239
+ CustomAgentStatus.DRAFT,
240
+ ]:
241
+ self.console.print(
242
+ f"[yellow]Warning: Agent status is '{agent.status.value}'.[/yellow]"
243
+ )
244
+ if not Confirm.ask("Load anyway?", default=False):
245
+ return None
246
+
247
+ # Load the agent
248
+ with Progress(
249
+ SpinnerColumn(),
250
+ TextColumn("[progress.description]{task.description}"),
251
+ console=self.console,
252
+ transient=True,
253
+ ) as progress:
254
+ task = progress.add_task(f"Loading agent '{agent.name}'...", total=None)
255
+
256
+ # Update usage statistics
257
+ agent.increment_usage()
258
+ self.repository.update(agent)
259
+
260
+ if activate:
261
+ self._active_agent = agent
262
+ self._session_stats["agents_activated"] += 1
263
+
264
+ self._session_stats["agents_loaded"] += 1
265
+
266
+ progress.update(task, completed=100)
267
+
268
+ self.console.print(
269
+ f"[green]✅ Agent '{agent.name}' loaded successfully![/green]"
270
+ )
271
+
272
+ if activate:
273
+ self.console.print(
274
+ f"[cyan]🚀 Agent '{agent.name}' is now active for this session.[/cyan]"
275
+ )
276
+ self._show_agent_summary(agent)
277
+
278
+ return agent
279
+
280
+ except Exception as e:
281
+ self.console.print(f"[red]Error loading agent: {e}[/red]")
282
+ logger.error(f"Agent loading error: {e}", exc_info=True)
283
+ return None
284
+
285
+ async def delete_agent(self, identifier: str, confirm: bool = True) -> bool:
286
+ """
287
+ Delete an agent configuration.
288
+
289
+ Args:
290
+ identifier: Agent ID or name
291
+ confirm: Whether to ask for confirmation
292
+
293
+ Returns:
294
+ True if deleted successfully
295
+ """
296
+ try:
297
+ # Find the agent
298
+ agent = self.repository.get(identifier)
299
+ if not agent:
300
+ agent = self.repository.get_by_name(identifier)
301
+
302
+ if not agent:
303
+ self.console.print(f"[red]Agent '{identifier}' not found.[/red]")
304
+ return False
305
+
306
+ # Show agent info before deletion
307
+ self.console.print(f"\\n[yellow]Agent to delete:[/yellow]")
308
+ self._show_agent_summary(agent)
309
+
310
+ # Confirm deletion
311
+ if confirm:
312
+ if not Confirm.ask(
313
+ f"\\n[red]Are you sure you want to delete '{agent.name}'?[/red]",
314
+ default=False,
315
+ ):
316
+ self.console.print("[yellow]Deletion cancelled.[/yellow]")
317
+ return False
318
+
319
+ # Check if agent is currently active
320
+ if self._active_agent and self._active_agent.id == agent.id:
321
+ self.console.print(
322
+ "[yellow]Warning: Deleting currently active agent.[/yellow]"
323
+ )
324
+ self._active_agent = None
325
+
326
+ # Delete the agent
327
+ success = self.repository.delete(agent.id)
328
+
329
+ if success:
330
+ self.console.print(
331
+ f"[green]✅ Agent '{agent.name}' deleted successfully.[/green]"
332
+ )
333
+ else:
334
+ self.console.print(
335
+ f"[red]❌ Failed to delete agent '{agent.name}'.[/red]"
336
+ )
337
+
338
+ return success
339
+
340
+ except Exception as e:
341
+ self.console.print(f"[red]Error deleting agent: {e}[/red]")
342
+ logger.error(f"Agent deletion error: {e}", exc_info=True)
343
+ return False
344
+
345
+ async def create_agent(
346
+ self, interactive: bool = True
347
+ ) -> Optional[CustomAgentConfig]:
348
+ """
349
+ Create a new agent using the wizard.
350
+
351
+ Args:
352
+ interactive: Whether to use interactive wizard
353
+
354
+ Returns:
355
+ Created agent configuration
356
+ """
357
+ try:
358
+ if interactive:
359
+ wizard = AgentCreationWizard(
360
+ console=self.console, repository=self.repository
361
+ )
362
+ return await wizard.run_wizard()
363
+ else:
364
+ # Quick creation - would need additional parameters
365
+ self.console.print(
366
+ "[yellow]Non-interactive creation not yet implemented.[/yellow]"
367
+ )
368
+ return None
369
+
370
+ except Exception as e:
371
+ self.console.print(f"[red]Error creating agent: {e}[/red]")
372
+ logger.error(f"Agent creation error: {e}", exc_info=True)
373
+ return None
374
+
375
+ async def clone_agent(
376
+ self, identifier: str, new_name: str
377
+ ) -> Optional[CustomAgentConfig]:
378
+ """
379
+ Clone an existing agent with a new name.
380
+
381
+ Args:
382
+ identifier: Source agent ID or name
383
+ new_name: Name for the cloned agent
384
+
385
+ Returns:
386
+ Cloned agent configuration
387
+ """
388
+ try:
389
+ # Find source agent
390
+ source_agent = self.repository.get(identifier)
391
+ if not source_agent:
392
+ source_agent = self.repository.get_by_name(identifier)
393
+
394
+ if not source_agent:
395
+ self.console.print(f"[red]Source agent '{identifier}' not found.[/red]")
396
+ return None
397
+
398
+ # Check name availability
399
+ if self.repository.get_by_name(new_name):
400
+ self.console.print(
401
+ f"[red]Agent named '{new_name}' already exists.[/red]"
402
+ )
403
+ return None
404
+
405
+ # Create clone
406
+ import uuid
407
+
408
+ cloned_config = CustomAgentConfig(
409
+ id=str(uuid.uuid4()),
410
+ name=new_name,
411
+ description=f"Clone of {source_agent.name}\\n{source_agent.description}",
412
+ category=source_agent.category,
413
+ base_template_id=source_agent.base_template_id,
414
+ model_settings=source_agent.model_settings,
415
+ context_parameters=source_agent.context_parameters,
416
+ behavior_rules=source_agent.behavior_rules,
417
+ capabilities=source_agent.capabilities.copy(),
418
+ enabled_tools=source_agent.enabled_tools.copy(),
419
+ security_overrides=source_agent.security_overrides,
420
+ agent_overrides=source_agent.agent_overrides,
421
+ status=CustomAgentStatus.DRAFT, # Start as draft
422
+ )
423
+
424
+ # Save cloned agent
425
+ saved_clone = self.repository.create(cloned_config)
426
+
427
+ self.console.print(
428
+ f"[green]✅ Agent '{new_name}' cloned from '{source_agent.name}'![/green]"
429
+ )
430
+ self._show_agent_summary(saved_clone)
431
+
432
+ return saved_clone
433
+
434
+ except Exception as e:
435
+ self.console.print(f"[red]Error cloning agent: {e}[/red]")
436
+ logger.error(f"Agent cloning error: {e}", exc_info=True)
437
+ return None
438
+
439
+ async def export_agent(
440
+ self, identifier: str, export_path: Optional[Path] = None
441
+ ) -> bool:
442
+ """
443
+ Export an agent configuration to file.
444
+
445
+ Args:
446
+ identifier: Agent ID or name
447
+ export_path: Export file path (optional)
448
+
449
+ Returns:
450
+ True if exported successfully
451
+ """
452
+ try:
453
+ # Find the agent
454
+ agent = self.repository.get(identifier)
455
+ if not agent:
456
+ agent = self.repository.get_by_name(identifier)
457
+
458
+ if not agent:
459
+ self.console.print(f"[red]Agent '{identifier}' not found.[/red]")
460
+ return False
461
+
462
+ # Determine export path
463
+ if not export_path:
464
+ safe_name = "".join(
465
+ c if c.isalnum() or c in "-_" else "_" for c in agent.name
466
+ )
467
+ export_path = Path.cwd() / f"{safe_name}_agent.json"
468
+
469
+ # Export the agent
470
+ success = self.repository.export_config(agent.id, export_path)
471
+
472
+ if success:
473
+ self.console.print(
474
+ f"[green]✅ Agent '{agent.name}' exported to {export_path}[/green]"
475
+ )
476
+ else:
477
+ self.console.print(
478
+ f"[red]❌ Failed to export agent '{agent.name}'.[/red]"
479
+ )
480
+
481
+ return success
482
+
483
+ except Exception as e:
484
+ self.console.print(f"[red]Error exporting agent: {e}[/red]")
485
+ logger.error(f"Agent export error: {e}", exc_info=True)
486
+ return False
487
+
488
+ async def import_agent(
489
+ self, import_path: Path, new_name: Optional[str] = None
490
+ ) -> Optional[CustomAgentConfig]:
491
+ """
492
+ Import an agent configuration from file.
493
+
494
+ Args:
495
+ import_path: Import file path
496
+ new_name: Optional new name to avoid conflicts
497
+
498
+ Returns:
499
+ Imported agent configuration
500
+ """
501
+ try:
502
+ if not import_path.exists():
503
+ self.console.print(f"[red]Import file not found: {import_path}[/red]")
504
+ return None
505
+
506
+ # Import the agent
507
+ imported_agent = self.repository.import_config(import_path, new_id=True)
508
+
509
+ if not imported_agent:
510
+ self.console.print(
511
+ f"[red]Failed to import agent from {import_path}[/red]"
512
+ )
513
+ return None
514
+
515
+ # Rename if requested
516
+ if new_name:
517
+ imported_agent.name = new_name
518
+ imported_agent = self.repository.update(imported_agent)
519
+
520
+ self.console.print(
521
+ f"[green]✅ Agent '{imported_agent.name}' imported successfully![/green]"
522
+ )
523
+ self._show_agent_summary(imported_agent)
524
+
525
+ return imported_agent
526
+
527
+ except Exception as e:
528
+ self.console.print(f"[red]Error importing agent: {e}[/red]")
529
+ logger.error(f"Agent import error: {e}", exc_info=True)
530
+ return None
531
+
532
+ def get_active_agent(self) -> Optional[CustomAgentConfig]:
533
+ """Get the currently active agent."""
534
+ return self._active_agent
535
+
536
+ def show_statistics(self):
537
+ """Show agent repository and session statistics."""
538
+ try:
539
+ # Repository statistics
540
+ repo_stats = self.repository.get_statistics()
541
+
542
+ # Create statistics display
543
+ stats_table = Table(show_header=False, box=SIMPLE)
544
+ stats_table.add_column("Metric", style="cyan")
545
+ stats_table.add_column("Value", style="white")
546
+
547
+ # Repository stats
548
+ stats_table.add_row("Total Agents", str(repo_stats["total_agents"]))
549
+
550
+ if repo_stats["status_distribution"]:
551
+ for status, count in repo_stats["status_distribution"].items():
552
+ stats_table.add_row(f" {status.title()}", str(count))
553
+
554
+ stats_table.add_row(
555
+ "Storage Location", str(repo_stats["storage_directory"])
556
+ )
557
+
558
+ # Session stats
559
+ stats_table.add_row("", "") # Separator
560
+ stats_table.add_row(
561
+ "Session Started",
562
+ self._session_stats["session_start"].strftime("%Y-%m-%d %H:%M"),
563
+ )
564
+ stats_table.add_row(
565
+ "Agents Loaded", str(self._session_stats["agents_loaded"])
566
+ )
567
+ stats_table.add_row(
568
+ "Agents Activated",
569
+ str(self._session_stats["agents_activated"]),
570
+ )
571
+
572
+ if self._active_agent:
573
+ stats_table.add_row("Active Agent", self._active_agent.name)
574
+
575
+ self.console.print(
576
+ Panel(stats_table, title="📊 Agent Statistics", box=ROUNDED)
577
+ )
578
+
579
+ except Exception as e:
580
+ self.console.print(f"[red]Error showing statistics: {e}[/red]")
581
+ logger.error(f"Statistics display error: {e}", exc_info=True)
582
+
583
+ def _show_summary_agent_list(self, agents: List[CustomAgentConfig]):
584
+ """Display agents in summary table format."""
585
+ table = Table(show_header=True, header_style="bold magenta")
586
+ table.add_column("Name", style="cyan")
587
+ table.add_column("Category", style="yellow")
588
+ table.add_column("Model", style="green")
589
+ table.add_column("Status", style="white")
590
+ table.add_column("Tools", style="blue")
591
+ table.add_column("Last Used", style="dim")
592
+
593
+ for agent in agents:
594
+ # Format model info
595
+ model_info = f"{agent.model_settings.provider_type.value}:{agent.model_settings.model_name}"
596
+ if len(model_info) > 20:
597
+ model_info = model_info[:17] + "..."
598
+
599
+ # Format status with emoji
600
+ status_emoji = {
601
+ CustomAgentStatus.ACTIVE: "✅",
602
+ CustomAgentStatus.DRAFT: "📝",
603
+ CustomAgentStatus.DISABLED: "❌",
604
+ CustomAgentStatus.ARCHIVED: "📦",
605
+ CustomAgentStatus.ERROR: "⚠️",
606
+ }
607
+ status_display = (
608
+ f"{status_emoji.get(agent.status, '')} {agent.status.value}"
609
+ )
610
+
611
+ # Format tools count
612
+ tools_count = f"{len(agent.enabled_tools)} tools"
613
+
614
+ # Format last used
615
+ if agent.metadata.last_used:
616
+ last_used = agent.metadata.last_used.strftime("%m-%d %H:%M")
617
+ else:
618
+ last_used = "Never"
619
+
620
+ table.add_row(
621
+ agent.name[:25] + ("..." if len(agent.name) > 25 else ""),
622
+ agent.category.value.title(),
623
+ model_info,
624
+ status_display,
625
+ tools_count,
626
+ last_used,
627
+ )
628
+
629
+ self.console.print(table)
630
+ self.console.print(f"\\n[dim]Showing {len(agents)} agent(s)[/dim]")
631
+
632
+ def _show_detailed_agent_list(self, agents: List[CustomAgentConfig]):
633
+ """Display agents with detailed information."""
634
+ for i, agent in enumerate(agents):
635
+ if i > 0:
636
+ self.console.print() # Spacing between agents
637
+
638
+ self._show_agent_summary(agent)
639
+
640
+ def _show_agent_details(self, agent: CustomAgentConfig):
641
+ """Show comprehensive details for a single agent."""
642
+ # Header
643
+ header = f"🤖 {agent.name}"
644
+ if agent.description:
645
+ header += f" - {agent.description}"
646
+
647
+ self.console.print(Panel(header, style="bold cyan", box=ROUNDED))
648
+
649
+ # Basic information table
650
+ info_table = Table(show_header=False, box=None)
651
+ info_table.add_column("Field", style="cyan", width=20)
652
+ info_table.add_column("Value", style="white")
653
+
654
+ info_table.add_row("ID", agent.id)
655
+ info_table.add_row("Category", agent.category.value.title())
656
+ info_table.add_row("Status", f"{agent.status.value.title()}")
657
+ info_table.add_row("Created", agent.created_at.strftime("%Y-%m-%d %H:%M:%S"))
658
+ info_table.add_row(
659
+ "Last Modified", agent.updated_at.strftime("%Y-%m-%d %H:%M:%S")
660
+ )
661
+ info_table.add_row("Usage Count", str(agent.metadata.usage_count))
662
+
663
+ if agent.metadata.last_used:
664
+ info_table.add_row(
665
+ "Last Used",
666
+ agent.metadata.last_used.strftime("%Y-%m-%d %H:%M:%S"),
667
+ )
668
+
669
+ if agent.base_template_id:
670
+ info_table.add_row("Base Template", agent.base_template_id)
671
+
672
+ self.console.print(Panel(info_table, title="📋 Basic Information", box=ROUNDED))
673
+
674
+ # Model configuration
675
+ model_table = Table(show_header=False, box=None)
676
+ model_table.add_column("Field", style="cyan", width=20)
677
+ model_table.add_column("Value", style="white")
678
+
679
+ model_table.add_row("Provider", agent.model_settings.provider_type.value)
680
+ model_table.add_row("Model", agent.model_settings.model_name)
681
+ model_table.add_row("Temperature", str(agent.model_settings.temperature))
682
+
683
+ if agent.model_settings.max_tokens:
684
+ model_table.add_row("Max Tokens", str(agent.model_settings.max_tokens))
685
+
686
+ self.console.print(
687
+ Panel(model_table, title="🧠 Model Configuration", box=ROUNDED)
688
+ )
689
+
690
+ # Tools and capabilities
691
+ tools_text = "\\n".join(
692
+ [
693
+ f"• {tool.value.replace('_', ' ').title()}"
694
+ for tool in agent.enabled_tools
695
+ ]
696
+ )
697
+ caps_text = "\\n".join(
698
+ [f"• {cap.value.replace('_', ' ').title()}" for cap in agent.capabilities]
699
+ )
700
+
701
+ tools_panel = Panel(
702
+ tools_text or "[dim]None[/dim]",
703
+ title="🔧 Enabled Tools",
704
+ box=ROUNDED,
705
+ )
706
+ caps_panel = Panel(
707
+ caps_text or "[dim]None[/dim]",
708
+ title="⭐ Capabilities",
709
+ box=ROUNDED,
710
+ )
711
+
712
+ self.console.print(Columns([tools_panel, caps_panel], equal=True))
713
+
714
+ # Context and behavior
715
+ context_table = Table(show_header=False, box=None)
716
+ context_table.add_column("Field", style="cyan", width=20)
717
+ context_table.add_column("Value", style="white")
718
+
719
+ context_table.add_row(
720
+ "Context Window",
721
+ f"{agent.context_parameters.context_window_size:,} tokens",
722
+ )
723
+ context_table.add_row(
724
+ "Memory Enabled",
725
+ "✅" if agent.context_parameters.conversation_memory else "❌",
726
+ )
727
+
728
+ if agent.context_parameters.conversation_memory:
729
+ context_table.add_row(
730
+ "Memory Limit",
731
+ f"{agent.context_parameters.memory_limit} messages",
732
+ )
733
+
734
+ context_table.add_row(
735
+ "Response Format", agent.context_parameters.response_format
736
+ )
737
+ context_table.add_row("Safety Level", agent.behavior_rules.safety_level.title())
738
+ context_table.add_row(
739
+ "Reasoning Style",
740
+ agent.behavior_rules.reasoning_style.replace("_", " ").title(),
741
+ )
742
+ context_table.add_row(
743
+ "Creativity Level", agent.behavior_rules.creativity_level.title()
744
+ )
745
+
746
+ self.console.print(
747
+ Panel(context_table, title="💭 Context & Behavior", box=ROUNDED)
748
+ )
749
+
750
+ # System prompt (if present)
751
+ if agent.context_parameters.system_prompt:
752
+ prompt_preview = agent.context_parameters.system_prompt[:300]
753
+ if len(agent.context_parameters.system_prompt) > 300:
754
+ prompt_preview += "\\n\\n[dim]... (truncated)[/dim]"
755
+
756
+ self.console.print(
757
+ Panel(
758
+ prompt_preview,
759
+ title="📝 System Prompt (Preview)",
760
+ box=ROUNDED,
761
+ )
762
+ )
763
+
764
+ def _show_agent_summary(self, agent: CustomAgentConfig):
765
+ """Show a concise summary of an agent."""
766
+ summary_text = Text.assemble(
767
+ f"🤖 ",
768
+ ("", "bold cyan"),
769
+ f"{agent.name}",
770
+ ("", "bold cyan"),
771
+ f" ({agent.category.value}) - ",
772
+ f"{agent.model_settings.provider_type.value}:{agent.model_settings.model_name}",
773
+ f"\\n {len(agent.enabled_tools)} tools, {len(agent.capabilities)} capabilities",
774
+ f" • Status: {agent.status.value}",
775
+ )
776
+
777
+ self.console.print(Panel(summary_text, box=SIMPLE))
778
+
779
+ def _sort_agents(
780
+ self, agents: List[CustomAgentConfig], sort_by: str
781
+ ) -> List[CustomAgentConfig]:
782
+ """Sort agents by specified field."""
783
+ sort_functions = {
784
+ "name": lambda a: a.name.lower(),
785
+ "created_at": lambda a: a.created_at,
786
+ "updated_at": lambda a: a.updated_at,
787
+ "last_used": lambda a: a.metadata.last_used or datetime.min,
788
+ "usage_count": lambda a: a.metadata.usage_count,
789
+ "category": lambda a: a.category.value,
790
+ }
791
+
792
+ if sort_by not in sort_functions:
793
+ logger.warning(f"Unknown sort field: {sort_by}, using name")
794
+ sort_by = "name"
795
+
796
+ return sorted(agents, key=sort_functions[sort_by])
797
+
798
+
799
+ # Utility functions for easy integration
800
+
801
+
802
+ def create_agent_manager(console: Optional[Console] = None) -> AgentManager:
803
+ """Create and initialize an agent manager."""
804
+ return AgentManager(console=console)
805
+
806
+
807
+ async def list_available_agents(
808
+ console: Optional[Console] = None,
809
+ ) -> List[CustomAgentConfig]:
810
+ """Quick function to list all available agents."""
811
+ manager = create_agent_manager(console)
812
+ return await manager.list_agents()
813
+
814
+
815
+ async def load_agent_by_name(
816
+ name: str, console: Optional[Console] = None
817
+ ) -> Optional[CustomAgentConfig]:
818
+ """Quick function to load an agent by name."""
819
+ manager = create_agent_manager(console)
820
+ return await manager.load_agent(name)
821
+
822
+
823
+ def get_agent_statistics(console: Optional[Console] = None) -> Dict[str, Any]:
824
+ """Get agent repository statistics."""
825
+ repository = AgentRepository()
826
+ return repository.get_statistics()