omnimancer-cli 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dynamic command registration and management system for Omnimancer.
|
|
3
|
+
|
|
4
|
+
This module provides a system for dynamically loading and registering
|
|
5
|
+
custom slash commands from external files, similar to Claude Code's
|
|
6
|
+
command system.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class DynamicCommand:
|
|
20
|
+
"""Represents a dynamically loaded command."""
|
|
21
|
+
|
|
22
|
+
name: str # Command name without slash (e.g., "custom-test")
|
|
23
|
+
description: str
|
|
24
|
+
handler: Optional[Callable] = None # Python handler if applicable
|
|
25
|
+
script_path: Optional[Path] = None # Path to script file
|
|
26
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
27
|
+
arguments: List[Dict[str, Any]] = field(
|
|
28
|
+
default_factory=list
|
|
29
|
+
) # Argument definitions
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def slash_name(self) -> str:
|
|
33
|
+
"""Get the full slash command name."""
|
|
34
|
+
return f"/{self.name}"
|
|
35
|
+
|
|
36
|
+
def get_argument_names(self) -> List[str]:
|
|
37
|
+
"""Get list of argument names for autocomplete."""
|
|
38
|
+
return [arg.get("name", "") for arg in self.arguments if arg.get("name")]
|
|
39
|
+
|
|
40
|
+
def get_argument_at_position(self, position: int) -> Optional[Dict[str, Any]]:
|
|
41
|
+
"""Get argument definition at a specific position."""
|
|
42
|
+
if 0 <= position < len(self.arguments):
|
|
43
|
+
return self.arguments[position]
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CommandRegistry:
|
|
48
|
+
"""Registry for managing dynamic commands."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, commands_dir: Optional[Path] = None):
|
|
51
|
+
"""
|
|
52
|
+
Initialize the command registry.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
commands_dir: Directory to load commands from
|
|
56
|
+
"""
|
|
57
|
+
self.commands: Dict[str, DynamicCommand] = {}
|
|
58
|
+
self.commands_dir = commands_dir or self._get_default_commands_dir()
|
|
59
|
+
self._builtin_commands: List[str] = [] # Track built-in commands
|
|
60
|
+
|
|
61
|
+
def _get_default_commands_dir(self) -> Path:
|
|
62
|
+
"""Get the default commands directory."""
|
|
63
|
+
# Use ~/.omnimancer/commands as requested by user
|
|
64
|
+
return Path.home() / ".omnimancer" / "commands"
|
|
65
|
+
|
|
66
|
+
def register_command(self, command: DynamicCommand) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
Register a dynamic command.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
command: The command to register
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
True if registered successfully, False otherwise
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
# Check for conflicts with built-in commands
|
|
78
|
+
if command.name in self._builtin_commands:
|
|
79
|
+
logger.warning(f"Cannot override built-in command: {command.name}")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
self.commands[command.name] = command
|
|
83
|
+
logger.debug(f"Registered dynamic command: {command.name}")
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
logger.error(f"Failed to register command {command.name}: {e}")
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
def unregister_command(self, name: str) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Unregister a dynamic command.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
name: Command name (without slash)
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
True if unregistered successfully, False otherwise
|
|
99
|
+
"""
|
|
100
|
+
if name in self.commands:
|
|
101
|
+
del self.commands[name]
|
|
102
|
+
logger.debug(f"Unregistered command: {name}")
|
|
103
|
+
return True
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def get_command(self, name: str) -> Optional[DynamicCommand]:
|
|
107
|
+
"""
|
|
108
|
+
Get a registered command by name.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
name: Command name (with or without slash)
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The command if found, None otherwise
|
|
115
|
+
"""
|
|
116
|
+
# Remove leading slash if present
|
|
117
|
+
if name.startswith("/"):
|
|
118
|
+
name = name[1:]
|
|
119
|
+
return self.commands.get(name)
|
|
120
|
+
|
|
121
|
+
def list_commands(self) -> List[str]:
|
|
122
|
+
"""
|
|
123
|
+
List all registered dynamic command names.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
List of command names (with slash prefix)
|
|
127
|
+
"""
|
|
128
|
+
return [f"/{name}" for name in self.commands.keys()]
|
|
129
|
+
|
|
130
|
+
def load_commands_from_directory(self, directory: Optional[Path] = None) -> int:
|
|
131
|
+
"""
|
|
132
|
+
Load commands from a directory.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
directory: Directory to load from (uses default if None)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Number of commands loaded
|
|
139
|
+
"""
|
|
140
|
+
load_dir = directory or self.commands_dir
|
|
141
|
+
if not load_dir.exists():
|
|
142
|
+
logger.debug(f"Commands directory does not exist: {load_dir}")
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
loaded_count = 0
|
|
146
|
+
|
|
147
|
+
# Load JSON command definitions
|
|
148
|
+
for json_file in load_dir.glob("*.json"):
|
|
149
|
+
try:
|
|
150
|
+
command = self._load_json_command(json_file)
|
|
151
|
+
if command and self.register_command(command):
|
|
152
|
+
loaded_count += 1
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.error(f"Failed to load command from {json_file}: {e}")
|
|
155
|
+
|
|
156
|
+
# Load Python command modules
|
|
157
|
+
for py_file in load_dir.glob("*.py"):
|
|
158
|
+
if py_file.name.startswith("_"):
|
|
159
|
+
continue # Skip private modules
|
|
160
|
+
try:
|
|
161
|
+
command = self._load_python_command(py_file)
|
|
162
|
+
if command and self.register_command(command):
|
|
163
|
+
loaded_count += 1
|
|
164
|
+
except Exception as e:
|
|
165
|
+
logger.error(f"Failed to load command from {py_file}: {e}")
|
|
166
|
+
|
|
167
|
+
# Load shell script commands
|
|
168
|
+
for script_file in load_dir.glob("*.sh"):
|
|
169
|
+
try:
|
|
170
|
+
command = self._load_script_command(script_file)
|
|
171
|
+
if command and self.register_command(command):
|
|
172
|
+
loaded_count += 1
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.error(f"Failed to load command from {script_file}: {e}")
|
|
175
|
+
|
|
176
|
+
logger.info(f"Loaded {loaded_count} dynamic commands from {load_dir}")
|
|
177
|
+
return loaded_count
|
|
178
|
+
|
|
179
|
+
def _load_json_command(self, file_path: Path) -> Optional[DynamicCommand]:
|
|
180
|
+
"""Load a command definition from a JSON file."""
|
|
181
|
+
try:
|
|
182
|
+
with open(file_path, "r") as f:
|
|
183
|
+
data = json.load(f)
|
|
184
|
+
|
|
185
|
+
# Extract command info
|
|
186
|
+
name = data.get("name") or file_path.stem
|
|
187
|
+
description = data.get("description", "")
|
|
188
|
+
arguments = data.get("arguments", [])
|
|
189
|
+
metadata = data.get("metadata", {})
|
|
190
|
+
|
|
191
|
+
# Check for associated script
|
|
192
|
+
script_path = None
|
|
193
|
+
if "script" in data:
|
|
194
|
+
script_file = file_path.parent / data["script"]
|
|
195
|
+
if script_file.exists():
|
|
196
|
+
script_path = script_file
|
|
197
|
+
|
|
198
|
+
return DynamicCommand(
|
|
199
|
+
name=name,
|
|
200
|
+
description=description,
|
|
201
|
+
script_path=script_path,
|
|
202
|
+
metadata=metadata,
|
|
203
|
+
arguments=arguments,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.error(f"Error loading JSON command from {file_path}: {e}")
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
def _load_python_command(self, file_path: Path) -> Optional[DynamicCommand]:
|
|
211
|
+
"""Load a command from a Python module."""
|
|
212
|
+
try:
|
|
213
|
+
import importlib.util
|
|
214
|
+
|
|
215
|
+
# Load the module
|
|
216
|
+
spec = importlib.util.spec_from_file_location(file_path.stem, file_path)
|
|
217
|
+
if not spec or not spec.loader:
|
|
218
|
+
return None
|
|
219
|
+
|
|
220
|
+
module = importlib.util.module_from_spec(spec)
|
|
221
|
+
spec.loader.exec_module(module)
|
|
222
|
+
|
|
223
|
+
# Look for command metadata
|
|
224
|
+
if not hasattr(module, "COMMAND_INFO"):
|
|
225
|
+
logger.warning(f"No COMMAND_INFO found in {file_path}")
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
info = module.COMMAND_INFO
|
|
229
|
+
name = info.get("name", file_path.stem)
|
|
230
|
+
description = info.get("description", "")
|
|
231
|
+
arguments = info.get("arguments", [])
|
|
232
|
+
|
|
233
|
+
# Look for handler function
|
|
234
|
+
handler = None
|
|
235
|
+
if hasattr(module, "handle_command"):
|
|
236
|
+
handler = module.handle_command
|
|
237
|
+
|
|
238
|
+
return DynamicCommand(
|
|
239
|
+
name=name,
|
|
240
|
+
description=description,
|
|
241
|
+
handler=handler,
|
|
242
|
+
arguments=arguments,
|
|
243
|
+
metadata={"source": str(file_path)},
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
except Exception as e:
|
|
247
|
+
logger.error(f"Error loading Python command from {file_path}: {e}")
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
def _load_script_command(self, file_path: Path) -> Optional[DynamicCommand]:
|
|
251
|
+
"""Load a command from a shell script."""
|
|
252
|
+
try:
|
|
253
|
+
# Look for metadata in script comments
|
|
254
|
+
name = file_path.stem
|
|
255
|
+
description = ""
|
|
256
|
+
arguments = []
|
|
257
|
+
|
|
258
|
+
with open(file_path, "r") as f:
|
|
259
|
+
lines = f.readlines()
|
|
260
|
+
|
|
261
|
+
# Parse header comments for metadata
|
|
262
|
+
for line in lines[:20]: # Check first 20 lines
|
|
263
|
+
if line.startswith("# NAME:"):
|
|
264
|
+
name = line.replace("# NAME:", "").strip()
|
|
265
|
+
elif line.startswith("# DESCRIPTION:"):
|
|
266
|
+
description = line.replace("# DESCRIPTION:", "").strip()
|
|
267
|
+
elif line.startswith("# ARG:"):
|
|
268
|
+
# Format: # ARG: name:type:description
|
|
269
|
+
arg_info = line.replace("# ARG:", "").strip()
|
|
270
|
+
parts = arg_info.split(":", 2)
|
|
271
|
+
if len(parts) >= 1:
|
|
272
|
+
arg = {"name": parts[0]}
|
|
273
|
+
if len(parts) >= 2:
|
|
274
|
+
arg["type"] = parts[1]
|
|
275
|
+
if len(parts) >= 3:
|
|
276
|
+
arg["description"] = parts[2]
|
|
277
|
+
arguments.append(arg)
|
|
278
|
+
|
|
279
|
+
return DynamicCommand(
|
|
280
|
+
name=name,
|
|
281
|
+
description=description or f"Script command: {name}",
|
|
282
|
+
script_path=file_path,
|
|
283
|
+
arguments=arguments,
|
|
284
|
+
metadata={"type": "shell_script"},
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
except Exception as e:
|
|
288
|
+
logger.error(f"Error loading script command from {file_path}: {e}")
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
def set_builtin_commands(self, commands: List[str]) -> None:
|
|
292
|
+
"""
|
|
293
|
+
Set the list of built-in commands to prevent overriding.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
commands: List of built-in command names (without slash)
|
|
297
|
+
"""
|
|
298
|
+
self._builtin_commands = [cmd.lstrip("/") for cmd in commands]
|
|
299
|
+
|
|
300
|
+
def get_completions_for_command(
|
|
301
|
+
self, command_name: str, arg_position: int, text: str
|
|
302
|
+
) -> List[str]:
|
|
303
|
+
"""
|
|
304
|
+
Get autocomplete suggestions for a command's arguments.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
command_name: The command name (with or without slash)
|
|
308
|
+
arg_position: The argument position being completed
|
|
309
|
+
text: The current text being typed
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
List of completion suggestions
|
|
313
|
+
"""
|
|
314
|
+
command = self.get_command(command_name)
|
|
315
|
+
if not command:
|
|
316
|
+
return []
|
|
317
|
+
|
|
318
|
+
# Get argument definition for this position
|
|
319
|
+
arg_def = command.get_argument_at_position(arg_position)
|
|
320
|
+
if not arg_def:
|
|
321
|
+
return []
|
|
322
|
+
|
|
323
|
+
# Check if argument has predefined choices
|
|
324
|
+
if "choices" in arg_def:
|
|
325
|
+
choices = arg_def["choices"]
|
|
326
|
+
if callable(choices):
|
|
327
|
+
# Dynamic choices function
|
|
328
|
+
try:
|
|
329
|
+
choices = choices()
|
|
330
|
+
except Exception:
|
|
331
|
+
return []
|
|
332
|
+
return [c for c in choices if str(c).startswith(text)]
|
|
333
|
+
|
|
334
|
+
# Check for type-based completions
|
|
335
|
+
arg_type = arg_def.get("type", "string")
|
|
336
|
+
if arg_type == "file":
|
|
337
|
+
return self._complete_file_path(text)
|
|
338
|
+
elif arg_type == "directory":
|
|
339
|
+
return self._complete_directory_path(text)
|
|
340
|
+
|
|
341
|
+
return []
|
|
342
|
+
|
|
343
|
+
def _complete_file_path(self, text: str) -> List[str]:
|
|
344
|
+
"""Complete file paths."""
|
|
345
|
+
try:
|
|
346
|
+
path = Path(text)
|
|
347
|
+
if text.endswith("/"):
|
|
348
|
+
# List directory contents
|
|
349
|
+
if path.exists() and path.is_dir():
|
|
350
|
+
return [str(p) for p in path.iterdir()]
|
|
351
|
+
else:
|
|
352
|
+
# Complete partial path
|
|
353
|
+
parent = path.parent
|
|
354
|
+
prefix = path.name
|
|
355
|
+
if parent.exists():
|
|
356
|
+
return [
|
|
357
|
+
str(p) for p in parent.iterdir() if p.name.startswith(prefix)
|
|
358
|
+
]
|
|
359
|
+
except Exception:
|
|
360
|
+
pass
|
|
361
|
+
return []
|
|
362
|
+
|
|
363
|
+
def _complete_directory_path(self, text: str) -> List[str]:
|
|
364
|
+
"""Complete directory paths."""
|
|
365
|
+
try:
|
|
366
|
+
paths = self._complete_file_path(text)
|
|
367
|
+
# Filter to only directories
|
|
368
|
+
return [p for p in paths if Path(p).is_dir()]
|
|
369
|
+
except Exception:
|
|
370
|
+
pass
|
|
371
|
+
return []
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI Handlers package.
|
|
3
|
+
|
|
4
|
+
This package contains specialized handlers for different CLI commands
|
|
5
|
+
to keep the main interface file organized and maintainable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .agent_handler import AgentCLIHandler
|
|
9
|
+
from .agent_persona_handler import AgentPersonaHandler
|
|
10
|
+
from .custom_agent_handler import CustomAgentHandler
|
|
11
|
+
from .permissions_handler import PermissionsHandler
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"AgentCLIHandler",
|
|
15
|
+
"AgentPersonaHandler",
|
|
16
|
+
"PermissionsHandler",
|
|
17
|
+
"CustomAgentHandler",
|
|
18
|
+
]
|