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,394 @@
1
+ """
2
+ Terminal Status Listener for Omnimancer Agent Status Streaming.
3
+
4
+ This module provides a Rich-based terminal UI listener that displays real-time
5
+ agent status updates in the terminal using the async status stream system.
6
+ """
7
+
8
+ import asyncio
9
+ import logging
10
+ import time
11
+ from datetime import datetime
12
+ from typing import Any, Dict, Optional, Set
13
+
14
+ from rich.console import Console
15
+ from rich.live import Live
16
+ from rich.panel import Panel
17
+
18
+ from .status_core import (
19
+ AgentEvent,
20
+ AgentStatus,
21
+ EventType,
22
+ StatusStreamEvent,
23
+ StatusStreamListener,
24
+ )
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class TerminalStatusListener(StatusStreamListener):
30
+ """
31
+ Terminal UI listener for agent status updates using Rich library.
32
+
33
+ Displays real-time status updates in a formatted terminal interface
34
+ with live updating components.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ listener_id: str = "terminal_ui",
40
+ event_types: Optional[Set[EventType]] = None,
41
+ console: Optional[Console] = None,
42
+ update_frequency: float = 0.5,
43
+ ):
44
+ """
45
+ Initialize terminal status listener.
46
+
47
+ Args:
48
+ listener_id: Unique identifier for this listener
49
+ event_types: Set of event types to listen for
50
+ console: Rich console instance (optional)
51
+ update_frequency: How often to update the display (seconds)
52
+ """
53
+ super().__init__(listener_id, event_types)
54
+
55
+ self.console = console or Console()
56
+ self.update_frequency = update_frequency
57
+
58
+ # Status tracking
59
+ self.agent_statuses: Dict[str, AgentStatus] = {}
60
+ self.active_operations: Dict[str, Dict[str, Any]] = {}
61
+ self.recent_events: list = []
62
+ self.max_recent_events = 10
63
+
64
+ # Display state
65
+ self.live_display: Optional[Live] = None
66
+ self.last_update = 0.0
67
+ self.stats = {
68
+ "events_received": 0,
69
+ "operations_tracked": 0,
70
+ "agents_monitored": 0,
71
+ }
72
+
73
+ async def start_live_display(self) -> None:
74
+ """Start the live display interface."""
75
+ if self.live_display is None:
76
+ self.live_display = Live(
77
+ self._generate_display(),
78
+ console=self.console,
79
+ refresh_per_second=1 / self.update_frequency,
80
+ auto_refresh=True,
81
+ )
82
+ self.live_display.start()
83
+ logger.info("Terminal status display started")
84
+
85
+ async def stop_live_display(self) -> None:
86
+ """Stop the live display interface."""
87
+ if self.live_display:
88
+ self.live_display.stop()
89
+ self.live_display = None
90
+ logger.info("Terminal status display stopped")
91
+
92
+ async def _process_event(self, stream_event: StatusStreamEvent) -> None:
93
+ """Process incoming status stream events."""
94
+ try:
95
+ event = stream_event.event
96
+ self.stats["events_received"] += 1
97
+
98
+ # Update recent events list
99
+ self.recent_events.append(
100
+ {
101
+ "timestamp": event.timestamp,
102
+ "type": event.event_type.value,
103
+ "agent_id": event.agent_id,
104
+ "priority": stream_event.priority.value,
105
+ "data": event.data,
106
+ }
107
+ )
108
+
109
+ # Keep only recent events
110
+ if len(self.recent_events) > self.max_recent_events:
111
+ self.recent_events.pop(0)
112
+
113
+ # Process specific event types
114
+ await self._handle_specific_event(event)
115
+
116
+ # Update display if enough time has passed
117
+ current_time = time.time()
118
+ if current_time - self.last_update >= self.update_frequency:
119
+ await self._update_display()
120
+ self.last_update = current_time
121
+
122
+ except Exception as e:
123
+ logger.error(f"Error processing event in terminal listener: {e}")
124
+
125
+ async def _handle_specific_event(self, event: AgentEvent) -> None:
126
+ """Handle specific event types with appropriate updates."""
127
+ try:
128
+ if event.event_type == EventType.AGENT_STATE_CHANGED:
129
+ # Update agent status
130
+ if event.agent_id and "new_status" in event.data:
131
+ try:
132
+ status_str = event.data["new_status"]
133
+ self.agent_statuses[event.agent_id] = AgentStatus(status_str)
134
+ self.stats["agents_monitored"] = len(
135
+ set(self.agent_statuses.keys())
136
+ )
137
+ except ValueError:
138
+ logger.warning(
139
+ f"Invalid agent status: {event.data['new_status']}"
140
+ )
141
+
142
+ elif event.event_type == EventType.OPERATION_STARTED:
143
+ # Track new operation
144
+ if event.operation_id:
145
+ self.active_operations[event.operation_id] = {
146
+ "agent_id": event.agent_id,
147
+ "type": event.data.get("operation_type", "unknown"),
148
+ "description": event.data.get("description", "No description"),
149
+ "start_time": event.timestamp,
150
+ "progress": 0.0,
151
+ "status": "running",
152
+ }
153
+ self.stats["operations_tracked"] = len(self.active_operations)
154
+
155
+ elif event.event_type == EventType.OPERATION_PROGRESS:
156
+ # Update operation progress
157
+ if event.operation_id and event.operation_id in self.active_operations:
158
+ self.active_operations[event.operation_id].update(
159
+ {
160
+ "progress": event.data.get("progress", 0.0),
161
+ "description": event.data.get(
162
+ "description",
163
+ self.active_operations[event.operation_id][
164
+ "description"
165
+ ],
166
+ ),
167
+ }
168
+ )
169
+
170
+ elif event.event_type in [
171
+ EventType.OPERATION_COMPLETED,
172
+ EventType.OPERATION_FAILED,
173
+ EventType.OPERATION_CANCELLED,
174
+ ]:
175
+ # Complete operation
176
+ if event.operation_id and event.operation_id in self.active_operations:
177
+ self.active_operations[event.operation_id].update(
178
+ {
179
+ "status": (
180
+ "completed"
181
+ if event.event_type == EventType.OPERATION_COMPLETED
182
+ else "failed"
183
+ ),
184
+ "end_time": event.timestamp,
185
+ "progress": (
186
+ 100.0
187
+ if event.event_type == EventType.OPERATION_COMPLETED
188
+ else self.active_operations[event.operation_id][
189
+ "progress"
190
+ ]
191
+ ),
192
+ }
193
+ )
194
+
195
+ # Remove completed operations after a delay (in a real implementation)
196
+ # For now, we'll keep them for display purposes
197
+
198
+ except Exception as e:
199
+ logger.error(f"Error handling specific event {event.event_type}: {e}")
200
+
201
+ async def _update_display(self) -> None:
202
+ """Update the live display with current status."""
203
+ if self.live_display:
204
+ try:
205
+ self.live_display.update(self._generate_display())
206
+ except Exception as e:
207
+ logger.error(f"Error updating display: {e}")
208
+
209
+ def _generate_display(self) -> Panel:
210
+ """Generate the complete display panel."""
211
+ try:
212
+ # Create main layout
213
+ display_content = []
214
+
215
+ # Add system stats
216
+ display_content.append(self._generate_stats_section())
217
+
218
+ # Add agent status section
219
+ if self.agent_statuses:
220
+ display_content.append(self._generate_agents_section())
221
+
222
+ # Add active operations section
223
+ if self.active_operations:
224
+ display_content.append(self._generate_operations_section())
225
+
226
+ # Add recent events section
227
+ if self.recent_events:
228
+ display_content.append(self._generate_events_section())
229
+
230
+ # Combine all sections
231
+ content = "\n\n".join(display_content)
232
+
233
+ return Panel(
234
+ content,
235
+ title="🤖 Agent Status Monitor",
236
+ border_style="blue",
237
+ title_align="left",
238
+ )
239
+
240
+ except Exception as e:
241
+ logger.error(f"Error generating display: {e}")
242
+ return Panel(f"Display Error: {e}", border_style="red")
243
+
244
+ def _generate_stats_section(self) -> str:
245
+ """Generate system statistics section."""
246
+ return (
247
+ f"[bold]System Statistics[/bold]\n"
248
+ f"Events Received: {self.stats['events_received']}\n"
249
+ f"Active Operations: {len([op for op in self.active_operations.values() if op['status'] == 'running'])}\n"
250
+ f"Agents Monitored: {self.stats['agents_monitored']}\n"
251
+ f"Last Update: {datetime.now().strftime('%H:%M:%S')}"
252
+ )
253
+
254
+ def _generate_agents_section(self) -> str:
255
+ """Generate agent status section."""
256
+ content = "[bold]Agent Status[/bold]\n"
257
+
258
+ for agent_id, status in self.agent_statuses.items():
259
+ # Color coding based on status
260
+ if status == AgentStatus.ENABLED:
261
+ color = "green"
262
+ emoji = "🟢"
263
+ elif status == AgentStatus.RUNNING:
264
+ color = "blue"
265
+ emoji = "🔵"
266
+ elif status == AgentStatus.ERROR:
267
+ color = "red"
268
+ emoji = "🔴"
269
+ else:
270
+ color = "yellow"
271
+ emoji = "🟡"
272
+
273
+ content += f"{emoji} [{color}]{agent_id}[/{color}]: {status.value}\n"
274
+
275
+ return content.rstrip()
276
+
277
+ def _generate_operations_section(self) -> str:
278
+ """Generate active operations section."""
279
+ content = "[bold]Active Operations[/bold]\n"
280
+
281
+ active_ops = [
282
+ op for op in self.active_operations.values() if op["status"] == "running"
283
+ ]
284
+ if not active_ops:
285
+ content += "[dim]No active operations[/dim]"
286
+ else:
287
+ for op in active_ops[:5]: # Show max 5 operations
288
+ progress = op["progress"]
289
+ agent_id = op["agent_id"] or "unknown"
290
+ description = (
291
+ op["description"][:50] + "..."
292
+ if len(op["description"]) > 50
293
+ else op["description"]
294
+ )
295
+
296
+ # Progress bar representation
297
+ bar_width = 20
298
+ filled = int((progress / 100) * bar_width)
299
+ bar = "â–ˆ" * filled + "â–‘" * (bar_width - filled)
300
+
301
+ content += f"[cyan]{agent_id}[/cyan]: {description}\n"
302
+ content += f" [{progress:5.1f}%] {bar} \n"
303
+
304
+ return content.rstrip()
305
+
306
+ def _generate_events_section(self) -> str:
307
+ """Generate recent events section."""
308
+ content = "[bold]Recent Events[/bold]\n"
309
+
310
+ if not self.recent_events:
311
+ content += "[dim]No recent events[/dim]"
312
+ else:
313
+ for event in self.recent_events[-5:]: # Show last 5 events
314
+ timestamp = event["timestamp"].strftime("%H:%M:%S")
315
+ event_type = event["type"].replace("_", " ").title()
316
+ agent_id = event["agent_id"] or "system"
317
+ priority = event["priority"]
318
+
319
+ # Color coding by priority
320
+ if priority == "critical":
321
+ color = "red"
322
+ elif priority == "high":
323
+ color = "orange"
324
+ elif priority == "normal":
325
+ color = "yellow"
326
+ else:
327
+ color = "dim"
328
+
329
+ content += f"[{color}]{timestamp}[/{color}] [{agent_id}] {event_type}\n"
330
+
331
+ return content.rstrip()
332
+
333
+
334
+ # Convenience function to create and start a terminal status listener
335
+ async def create_terminal_status_display(
336
+ status_stream: "AsyncStatusStream",
337
+ ) -> TerminalStatusListener:
338
+ """
339
+ Create and start a terminal status display listener.
340
+
341
+ Args:
342
+ status_stream: The status stream to listen to
343
+
344
+ Returns:
345
+ The created and started terminal listener
346
+ """
347
+ listener = TerminalStatusListener()
348
+
349
+ # Add listener to stream
350
+ await status_stream.add_listener(listener)
351
+
352
+ # Start live display
353
+ await listener.start_live_display()
354
+
355
+ return listener
356
+
357
+
358
+ # Example usage function
359
+ async def example_terminal_monitor():
360
+ """Example of how to set up terminal monitoring."""
361
+ try:
362
+ from .status_manager import get_status_manager
363
+
364
+ # Get status system components
365
+ status_manager = get_status_manager()
366
+ # The unified manager handles both status and streaming
367
+
368
+ # Initialize the unified manager
369
+ await status_manager.initialize()
370
+
371
+ # Create terminal display
372
+ terminal_display = await create_terminal_status_display(status_manager)
373
+
374
+ print("Terminal status monitor running. Press Ctrl+C to stop.")
375
+
376
+ # Keep running until interrupted
377
+ try:
378
+ while True:
379
+ await asyncio.sleep(1)
380
+ except KeyboardInterrupt:
381
+ print("\nShutting down terminal monitor...")
382
+ finally:
383
+ await terminal_display.stop_live_display()
384
+ await status_stream.stop()
385
+ await status_manager.shutdown()
386
+
387
+ except Exception as e:
388
+ logger.error(f"Error in terminal monitor example: {e}")
389
+ raise
390
+
391
+
392
+ if __name__ == "__main__":
393
+ # Run the example
394
+ asyncio.run(example_terminal_monitor())