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,281 @@
1
+ """
2
+ Auto-completion handler for Omnimancer CLI commands.
3
+
4
+ This module provides readline-based auto-completion for slash commands,
5
+ including dynamic commands loaded from ~/.omnimancer/commands directory.
6
+ """
7
+
8
+ import logging
9
+ import readline
10
+ from pathlib import Path
11
+ from typing import Dict, List, Optional, Set
12
+
13
+ from .commands import SlashCommand
14
+ from .dynamic_commands import CommandRegistry, DynamicCommand
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class CompletionHandler:
20
+ """Handles auto-completion for Omnimancer CLI commands."""
21
+
22
+ def __init__(self):
23
+ """Initialize the completion handler."""
24
+ self.command_registry = CommandRegistry()
25
+ self.builtin_commands: Set[str] = set()
26
+ self.dynamic_commands: Dict[str, DynamicCommand] = {}
27
+ self._load_commands()
28
+
29
+ # Set up readline completion
30
+ self._setup_readline()
31
+
32
+ def _load_commands(self) -> None:
33
+ """Load both built-in and dynamic commands."""
34
+ # Load built-in commands
35
+ self._load_builtin_commands()
36
+
37
+ # Load dynamic commands from ~/.omnimancer/commands
38
+ self._load_dynamic_commands()
39
+
40
+ def _load_builtin_commands(self) -> None:
41
+ """Load built-in slash commands."""
42
+ try:
43
+ # Get all built-in slash commands
44
+ for cmd in SlashCommand:
45
+ # cmd.value already includes the slash, don't add another one
46
+ self.builtin_commands.add(cmd.value)
47
+
48
+ logger.debug(f"Loaded {len(self.builtin_commands)} built-in commands")
49
+ except Exception as e:
50
+ logger.error(f"Failed to load built-in commands: {e}")
51
+
52
+ def _load_dynamic_commands(self) -> None:
53
+ """Load dynamic commands from ~/.omnimancer/commands directory."""
54
+ try:
55
+ # Use the default commands directory (~/.omnimancer/commands)
56
+ commands_dir = Path.home() / ".omnimancer" / "commands"
57
+
58
+ if not commands_dir.exists():
59
+ logger.debug(f"Commands directory does not exist: {commands_dir}")
60
+ return
61
+
62
+ # Set built-in commands to prevent overriding
63
+ builtin_names = [cmd.lstrip("/") for cmd in self.builtin_commands]
64
+ self.command_registry.set_builtin_commands(builtin_names)
65
+
66
+ # Load commands from directory
67
+ loaded_count = self.command_registry.load_commands_from_directory(
68
+ commands_dir
69
+ )
70
+
71
+ # Update our dynamic commands cache
72
+ self.dynamic_commands = self.command_registry.commands.copy()
73
+
74
+ logger.info(f"Loaded {loaded_count} dynamic commands from {commands_dir}")
75
+
76
+ except Exception as e:
77
+ logger.error(f"Failed to load dynamic commands: {e}")
78
+
79
+ def _setup_readline(self) -> None:
80
+ """Set up readline completion."""
81
+ try:
82
+ # Set the completer function
83
+ readline.set_completer(self.complete)
84
+
85
+ # Set completion display matches function for better formatting
86
+ readline.set_completion_display_matches_hook(self._display_matches)
87
+
88
+ # Use tab for completion
89
+ readline.parse_and_bind("tab: complete")
90
+
91
+ # Enable history and completion
92
+ readline.parse_and_bind("set show-all-if-ambiguous on")
93
+ readline.parse_and_bind("set completion-ignore-case on")
94
+
95
+ logger.debug("Readline completion set up successfully")
96
+
97
+ except Exception as e:
98
+ logger.error(f"Failed to set up readline completion: {e}")
99
+
100
+ def complete(self, text: str, state: int) -> Optional[str]:
101
+ """
102
+ Readline completion function.
103
+
104
+ Args:
105
+ text: The text being completed
106
+ state: The completion state (0 for first call, incrementing)
107
+
108
+ Returns:
109
+ The next completion option, or None when done
110
+ """
111
+ try:
112
+ if state == 0:
113
+ # First call - generate all possible completions
114
+ line = readline.get_line_buffer()
115
+ self._completions = self._get_completions(line, text)
116
+
117
+ # Return the next completion
118
+ if state < len(self._completions):
119
+ return self._completions[state]
120
+ else:
121
+ return None
122
+
123
+ except Exception as e:
124
+ logger.error(f"Completion error: {e}")
125
+ return None
126
+
127
+ def _get_completions(self, line: str, text: str) -> List[str]:
128
+ """
129
+ Get all possible completions for the given text.
130
+
131
+ Args:
132
+ line: The full input line
133
+ text: The text being completed
134
+
135
+ Returns:
136
+ List of completion options
137
+ """
138
+ completions = []
139
+
140
+ # Check if we're completing a slash command
141
+ if line.strip().startswith("/") or text.startswith("/"):
142
+ completions.extend(self._get_command_completions(text))
143
+
144
+ # If no slash commands match and text doesn't start with '/',
145
+ # still show commands if user types '/'
146
+ elif text == "/" or (len(text) == 0 and line.strip() == ""):
147
+ # User typed just '/' - show all commands
148
+ completions.extend(self._get_all_commands())
149
+
150
+ return completions
151
+
152
+ def _get_command_completions(self, text: str) -> List[str]:
153
+ """
154
+ Get command completions for the given text.
155
+
156
+ Args:
157
+ text: The text being completed (may or may not start with '/')
158
+
159
+ Returns:
160
+ List of matching commands
161
+ """
162
+ # Ensure text starts with '/' for matching
163
+ if not text.startswith("/"):
164
+ text = "/" + text
165
+
166
+ completions = []
167
+
168
+ # Match built-in commands
169
+ for cmd in self.builtin_commands:
170
+ if cmd.startswith(text):
171
+ completions.append(cmd)
172
+
173
+ # Match dynamic commands
174
+ for cmd_name, cmd in self.dynamic_commands.items():
175
+ slash_name = f"/{cmd_name}"
176
+ if slash_name.startswith(text):
177
+ completions.append(slash_name)
178
+
179
+ return sorted(completions)
180
+
181
+ def _get_all_commands(self) -> List[str]:
182
+ """Get all available commands."""
183
+ all_commands = []
184
+
185
+ # Add built-in commands
186
+ all_commands.extend(self.builtin_commands)
187
+
188
+ # Add dynamic commands
189
+ for cmd_name in self.dynamic_commands:
190
+ all_commands.append(f"/{cmd_name}")
191
+
192
+ return sorted(all_commands)
193
+
194
+ def _display_matches(
195
+ self, substitution: str, matches: List[str], longest_match_length: int
196
+ ) -> None:
197
+ """
198
+ Custom display function for completion matches.
199
+
200
+ Args:
201
+ substitution: The substitution text
202
+ matches: List of matches
203
+ longest_match_length: Length of longest match
204
+ """
205
+ try:
206
+ print("\nAvailable commands:")
207
+
208
+ # Group matches by type
209
+ builtin_matches = []
210
+ dynamic_matches = []
211
+
212
+ for match in matches:
213
+ if match in self.builtin_commands:
214
+ builtin_matches.append(match)
215
+ else:
216
+ dynamic_matches.append(match)
217
+
218
+ # Display built-in commands
219
+ if builtin_matches:
220
+ print(" Built-in commands:")
221
+ for match in builtin_matches:
222
+ print(f" {match}")
223
+
224
+ # Display dynamic commands with descriptions
225
+ if dynamic_matches:
226
+ print(" Custom commands:")
227
+ for match in dynamic_matches:
228
+ cmd_name = match.lstrip("/")
229
+ cmd = self.dynamic_commands.get(cmd_name)
230
+ if cmd and cmd.description:
231
+ print(f" {match} - {cmd.description}")
232
+ else:
233
+ print(f" {match}")
234
+
235
+ # Redisplay the prompt
236
+ print(">>> ", end="", flush=True)
237
+
238
+ except Exception as e:
239
+ logger.error(f"Error displaying matches: {e}")
240
+
241
+ def reload_commands(self) -> None:
242
+ """Reload all commands (useful for development/testing)."""
243
+ logger.info("Reloading commands...")
244
+ self.dynamic_commands.clear()
245
+ self._load_commands()
246
+
247
+ def get_command_help(self, command_name: str) -> Optional[str]:
248
+ """
249
+ Get help text for a specific command.
250
+
251
+ Args:
252
+ command_name: The command name (with or without slash)
253
+
254
+ Returns:
255
+ Help text or None if command not found
256
+ """
257
+ # Remove leading slash if present
258
+ if command_name.startswith("/"):
259
+ command_name = command_name[1:]
260
+
261
+ # Check dynamic commands first
262
+ if command_name in self.dynamic_commands:
263
+ cmd = self.dynamic_commands[command_name]
264
+ help_text = f"/{command_name}"
265
+ if cmd.description:
266
+ help_text += f" - {cmd.description}"
267
+
268
+ # Add argument information
269
+ if cmd.arguments:
270
+ help_text += "\n Arguments:"
271
+ for arg in cmd.arguments:
272
+ arg_name = arg.get("name", "arg")
273
+ arg_type = arg.get("type", "string")
274
+ arg_desc = arg.get("description", "")
275
+ help_text += f"\n {arg_name} ({arg_type})"
276
+ if arg_desc:
277
+ help_text += f" - {arg_desc}"
278
+
279
+ return help_text
280
+
281
+ return None