webscout 8.2.5__py3-none-any.whl → 8.2.6__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.
Potentially problematic release.
This version of webscout might be problematic. Click here for more details.
- webscout/AIauto.py +112 -22
- webscout/AIutel.py +240 -344
- webscout/Extra/autocoder/autocoder.py +66 -5
- webscout/Provider/AISEARCH/scira_search.py +2 -1
- webscout/Provider/GizAI.py +6 -4
- webscout/Provider/Nemotron.py +218 -0
- webscout/Provider/OPENAI/scirachat.py +2 -1
- webscout/Provider/TeachAnything.py +8 -5
- webscout/Provider/WiseCat.py +1 -1
- webscout/Provider/WrDoChat.py +370 -0
- webscout/Provider/__init__.py +4 -6
- webscout/Provider/ai4chat.py +5 -3
- webscout/Provider/akashgpt.py +59 -66
- webscout/Provider/freeaichat.py +57 -43
- webscout/Provider/scira_chat.py +2 -1
- webscout/Provider/scnet.py +4 -1
- webscout/__init__.py +0 -1
- webscout/conversation.py +305 -446
- webscout/swiftcli/__init__.py +80 -794
- webscout/swiftcli/core/__init__.py +7 -0
- webscout/swiftcli/core/cli.py +297 -0
- webscout/swiftcli/core/context.py +104 -0
- webscout/swiftcli/core/group.py +241 -0
- webscout/swiftcli/decorators/__init__.py +28 -0
- webscout/swiftcli/decorators/command.py +221 -0
- webscout/swiftcli/decorators/options.py +220 -0
- webscout/swiftcli/decorators/output.py +252 -0
- webscout/swiftcli/exceptions.py +21 -0
- webscout/swiftcli/plugins/__init__.py +9 -0
- webscout/swiftcli/plugins/base.py +135 -0
- webscout/swiftcli/plugins/manager.py +262 -0
- webscout/swiftcli/utils/__init__.py +59 -0
- webscout/swiftcli/utils/formatting.py +252 -0
- webscout/swiftcli/utils/parsing.py +267 -0
- webscout/version.py +1 -1
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/METADATA +1 -1
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/RECORD +41 -28
- webscout/LLM.py +0 -442
- webscout/Provider/PizzaGPT.py +0 -228
- webscout/Provider/promptrefine.py +0 -193
- webscout/Provider/tutorai.py +0 -270
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/WHEEL +0 -0
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/entry_points.txt +0 -0
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.2.5.dist-info → webscout-8.2.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Base plugin system for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..exceptions import PluginError
|
|
6
|
+
|
|
7
|
+
class Plugin:
|
|
8
|
+
"""
|
|
9
|
+
Base class for SwiftCLI plugins.
|
|
10
|
+
|
|
11
|
+
Plugins can extend the CLI functionality by hooking into various stages
|
|
12
|
+
of command execution. Subclass this class and override the methods
|
|
13
|
+
you want to hook into.
|
|
14
|
+
|
|
15
|
+
Attributes:
|
|
16
|
+
app: The CLI application instance
|
|
17
|
+
enabled: Whether the plugin is enabled
|
|
18
|
+
config: Plugin configuration dictionary
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
class LoggingPlugin(Plugin):
|
|
22
|
+
def before_command(self, command, args):
|
|
23
|
+
print(f"[LOG] Running command: {command}")
|
|
24
|
+
|
|
25
|
+
def after_command(self, command, args, result):
|
|
26
|
+
print(f"[LOG] Command completed: {result}")
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.app = None # Set by plugin manager
|
|
31
|
+
self.enabled: bool = True
|
|
32
|
+
self.config: Dict[str, Any] = {}
|
|
33
|
+
|
|
34
|
+
def init_app(self, app: Any) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Initialize plugin with CLI app instance.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
app: The CLI application instance
|
|
40
|
+
"""
|
|
41
|
+
self.app = app
|
|
42
|
+
|
|
43
|
+
def before_command(self, command: str, args: List[str]) -> Optional[bool]:
|
|
44
|
+
"""
|
|
45
|
+
Called before command execution.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
command: Command name
|
|
49
|
+
args: Command arguments
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Optional[bool]: Return False to prevent command execution
|
|
53
|
+
"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
def after_command(self, command: str, args: List[str], result: Any) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Called after command execution.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
command: Command name
|
|
62
|
+
args: Command arguments
|
|
63
|
+
result: Command result
|
|
64
|
+
"""
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
def on_error(self, command: str, error: Exception) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Called when command raises an error.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
command: Command name
|
|
73
|
+
error: The exception that was raised
|
|
74
|
+
"""
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
def on_help(self, command: str) -> Optional[str]:
|
|
78
|
+
"""
|
|
79
|
+
Called when help is requested for a command.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
command: Command name
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Optional[str]: Additional help text to display
|
|
86
|
+
"""
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
def on_completion(self, command: str, incomplete: str) -> List[str]:
|
|
90
|
+
"""
|
|
91
|
+
Called when shell completion is requested.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
command: Command name
|
|
95
|
+
incomplete: Incomplete text to complete
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
List[str]: Possible completions
|
|
99
|
+
"""
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
def configure(self, config: Dict[str, Any]) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Configure the plugin.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
config: Configuration dictionary
|
|
108
|
+
"""
|
|
109
|
+
self.config.update(config)
|
|
110
|
+
|
|
111
|
+
def validate_config(self) -> None:
|
|
112
|
+
"""
|
|
113
|
+
Validate plugin configuration.
|
|
114
|
+
|
|
115
|
+
Raises:
|
|
116
|
+
PluginError: If configuration is invalid
|
|
117
|
+
"""
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
def enable(self) -> None:
|
|
121
|
+
"""Enable the plugin."""
|
|
122
|
+
self.enabled = True
|
|
123
|
+
|
|
124
|
+
def disable(self) -> None:
|
|
125
|
+
"""Disable the plugin."""
|
|
126
|
+
self.enabled = False
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def name(self) -> str:
|
|
130
|
+
"""Get plugin name."""
|
|
131
|
+
return self.__class__.__name__
|
|
132
|
+
|
|
133
|
+
def __repr__(self) -> str:
|
|
134
|
+
status = "enabled" if self.enabled else "disabled"
|
|
135
|
+
return f"<{self.name} [{status}]>"
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Plugin manager for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict, List, Optional, Type
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
|
|
11
|
+
from ..exceptions import PluginError
|
|
12
|
+
from .base import Plugin
|
|
13
|
+
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
class PluginManager:
|
|
17
|
+
"""
|
|
18
|
+
Manages SwiftCLI plugins.
|
|
19
|
+
|
|
20
|
+
The plugin manager handles plugin registration, loading, and execution.
|
|
21
|
+
It provides hooks for plugins to extend CLI functionality at various points
|
|
22
|
+
during command execution.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
plugins: List of registered plugins
|
|
26
|
+
plugin_dir: Directory where plugins are stored
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
# Register a plugin
|
|
30
|
+
plugin_manager = PluginManager()
|
|
31
|
+
plugin_manager.register(LoggingPlugin())
|
|
32
|
+
|
|
33
|
+
# Load plugins from directory
|
|
34
|
+
plugin_manager.load_plugins()
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, plugin_dir: Optional[str] = None):
|
|
38
|
+
"""
|
|
39
|
+
Initialize plugin manager.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
plugin_dir: Optional custom plugin directory path
|
|
43
|
+
"""
|
|
44
|
+
self.plugins: List[Plugin] = []
|
|
45
|
+
self.plugin_dir = plugin_dir or os.path.expanduser("~/.swiftcli/plugins")
|
|
46
|
+
os.makedirs(self.plugin_dir, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
# Add plugin directory to Python path
|
|
49
|
+
if self.plugin_dir not in sys.path:
|
|
50
|
+
sys.path.append(self.plugin_dir)
|
|
51
|
+
|
|
52
|
+
def register(self, plugin: Plugin) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Register a new plugin.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
plugin: Plugin instance to register
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
PluginError: If plugin is invalid or already registered
|
|
61
|
+
"""
|
|
62
|
+
if not isinstance(plugin, Plugin):
|
|
63
|
+
raise PluginError(f"Invalid plugin type: {type(plugin)}")
|
|
64
|
+
|
|
65
|
+
if self._get_plugin(plugin.name):
|
|
66
|
+
raise PluginError(f"Plugin already registered: {plugin.name}")
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
plugin.validate_config()
|
|
70
|
+
except Exception as e:
|
|
71
|
+
raise PluginError(f"Plugin configuration invalid: {str(e)}")
|
|
72
|
+
|
|
73
|
+
self.plugins.append(plugin)
|
|
74
|
+
|
|
75
|
+
def unregister(self, plugin_name: str) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Unregister a plugin.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
plugin_name: Name of plugin to unregister
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
PluginError: If plugin not found
|
|
84
|
+
"""
|
|
85
|
+
plugin = self._get_plugin(plugin_name)
|
|
86
|
+
if not plugin:
|
|
87
|
+
raise PluginError(f"Plugin not found: {plugin_name}")
|
|
88
|
+
|
|
89
|
+
self.plugins.remove(plugin)
|
|
90
|
+
|
|
91
|
+
def load_plugins(self) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Load all plugins from plugin directory.
|
|
94
|
+
|
|
95
|
+
This method searches for Python files in the plugin directory and
|
|
96
|
+
attempts to load any Plugin subclasses defined in them.
|
|
97
|
+
"""
|
|
98
|
+
for file in Path(self.plugin_dir).glob("*.py"):
|
|
99
|
+
if file.name.startswith("_"):
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
module = importlib.import_module(file.stem)
|
|
104
|
+
|
|
105
|
+
# Find Plugin subclasses in module
|
|
106
|
+
for attr_name in dir(module):
|
|
107
|
+
attr = getattr(module, attr_name)
|
|
108
|
+
if (isinstance(attr, type) and
|
|
109
|
+
issubclass(attr, Plugin) and
|
|
110
|
+
attr is not Plugin):
|
|
111
|
+
plugin = attr()
|
|
112
|
+
self.register(plugin)
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
console.print(f"[red]Error loading plugin {file.name}: {e}[/red]")
|
|
116
|
+
|
|
117
|
+
def init_plugins(self, app: Any) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Initialize all plugins with the CLI application instance.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
app: The CLI application instance
|
|
123
|
+
"""
|
|
124
|
+
for plugin in self.plugins:
|
|
125
|
+
plugin.init_app(app)
|
|
126
|
+
|
|
127
|
+
def configure_plugins(self, config: Dict[str, Dict[str, Any]]) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Configure plugins with provided configuration.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
config: Dictionary of plugin configurations
|
|
133
|
+
"""
|
|
134
|
+
for plugin in self.plugins:
|
|
135
|
+
if plugin.name in config:
|
|
136
|
+
plugin.configure(config[plugin.name])
|
|
137
|
+
|
|
138
|
+
def before_command(self, command: str, args: List[str]) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Run before_command hooks for all plugins.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
command: Command name
|
|
144
|
+
args: Command arguments
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
bool: False if any plugin prevents command execution
|
|
148
|
+
"""
|
|
149
|
+
for plugin in self.plugins:
|
|
150
|
+
if not plugin.enabled:
|
|
151
|
+
continue
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
result = plugin.before_command(command, args)
|
|
155
|
+
if result is False:
|
|
156
|
+
return False
|
|
157
|
+
except Exception as e:
|
|
158
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
159
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
160
|
+
import traceback
|
|
161
|
+
traceback.print_exc()
|
|
162
|
+
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
def after_command(self, command: str, args: List[str], result: Any) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Run after_command hooks for all plugins.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
command: Command name
|
|
171
|
+
args: Command arguments
|
|
172
|
+
result: Command result
|
|
173
|
+
"""
|
|
174
|
+
for plugin in self.plugins:
|
|
175
|
+
if not plugin.enabled:
|
|
176
|
+
continue
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
plugin.after_command(command, args, result)
|
|
180
|
+
except Exception as e:
|
|
181
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
182
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
183
|
+
import traceback
|
|
184
|
+
traceback.print_exc()
|
|
185
|
+
|
|
186
|
+
def on_error(self, command: str, error: Exception) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Run error hooks for all plugins.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
command: Command name
|
|
192
|
+
error: The exception that was raised
|
|
193
|
+
"""
|
|
194
|
+
for plugin in self.plugins:
|
|
195
|
+
if not plugin.enabled:
|
|
196
|
+
continue
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
plugin.on_error(command, error)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
202
|
+
if plugin.app and getattr(plugin.app, 'debug', False):
|
|
203
|
+
import traceback
|
|
204
|
+
traceback.print_exc()
|
|
205
|
+
|
|
206
|
+
def get_help_text(self, command: str) -> List[str]:
|
|
207
|
+
"""
|
|
208
|
+
Get additional help text from plugins.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
command: Command name
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
List[str]: List of help text strings from plugins
|
|
215
|
+
"""
|
|
216
|
+
help_texts = []
|
|
217
|
+
for plugin in self.plugins:
|
|
218
|
+
if not plugin.enabled:
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
help_text = plugin.on_help(command)
|
|
223
|
+
if help_text:
|
|
224
|
+
help_texts.append(help_text)
|
|
225
|
+
except Exception as e:
|
|
226
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
227
|
+
|
|
228
|
+
return help_texts
|
|
229
|
+
|
|
230
|
+
def get_completions(self, command: str, incomplete: str) -> List[str]:
|
|
231
|
+
"""
|
|
232
|
+
Get command completions from plugins.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
command: Command name
|
|
236
|
+
incomplete: Incomplete text to complete
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
List[str]: Combined list of completions from all plugins
|
|
240
|
+
"""
|
|
241
|
+
completions = []
|
|
242
|
+
for plugin in self.plugins:
|
|
243
|
+
if not plugin.enabled:
|
|
244
|
+
continue
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
plugin_completions = plugin.on_completion(command, incomplete)
|
|
248
|
+
completions.extend(plugin_completions)
|
|
249
|
+
except Exception as e:
|
|
250
|
+
console.print(f"[red]Error in plugin {plugin.name}: {e}[/red]")
|
|
251
|
+
|
|
252
|
+
return list(set(completions)) # Remove duplicates
|
|
253
|
+
|
|
254
|
+
def _get_plugin(self, name: str) -> Optional[Plugin]:
|
|
255
|
+
"""Get plugin by name."""
|
|
256
|
+
for plugin in self.plugins:
|
|
257
|
+
if plugin.name == name:
|
|
258
|
+
return plugin
|
|
259
|
+
return None
|
|
260
|
+
|
|
261
|
+
def __repr__(self) -> str:
|
|
262
|
+
return f"<PluginManager plugins={len(self.plugins)}>"
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Utility functions for SwiftCLI."""
|
|
2
|
+
|
|
3
|
+
from .formatting import (
|
|
4
|
+
style_text,
|
|
5
|
+
format_error,
|
|
6
|
+
format_warning,
|
|
7
|
+
format_success,
|
|
8
|
+
format_info,
|
|
9
|
+
create_table,
|
|
10
|
+
truncate_text,
|
|
11
|
+
wrap_text,
|
|
12
|
+
format_dict,
|
|
13
|
+
format_list,
|
|
14
|
+
strip_ansi,
|
|
15
|
+
get_terminal_size,
|
|
16
|
+
clear_screen,
|
|
17
|
+
create_padding
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from .parsing import (
|
|
21
|
+
parse_args,
|
|
22
|
+
validate_required,
|
|
23
|
+
convert_type,
|
|
24
|
+
validate_choice,
|
|
25
|
+
load_config_file,
|
|
26
|
+
parse_key_value,
|
|
27
|
+
parse_list,
|
|
28
|
+
parse_dict,
|
|
29
|
+
get_env_var
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Formatting utilities
|
|
34
|
+
'style_text',
|
|
35
|
+
'format_error',
|
|
36
|
+
'format_warning',
|
|
37
|
+
'format_success',
|
|
38
|
+
'format_info',
|
|
39
|
+
'create_table',
|
|
40
|
+
'truncate_text',
|
|
41
|
+
'wrap_text',
|
|
42
|
+
'format_dict',
|
|
43
|
+
'format_list',
|
|
44
|
+
'strip_ansi',
|
|
45
|
+
'get_terminal_size',
|
|
46
|
+
'clear_screen',
|
|
47
|
+
'create_padding',
|
|
48
|
+
|
|
49
|
+
# Parsing utilities
|
|
50
|
+
'parse_args',
|
|
51
|
+
'validate_required',
|
|
52
|
+
'convert_type',
|
|
53
|
+
'validate_choice',
|
|
54
|
+
'load_config_file',
|
|
55
|
+
'parse_key_value',
|
|
56
|
+
'parse_list',
|
|
57
|
+
'parse_dict',
|
|
58
|
+
'get_env_var'
|
|
59
|
+
]
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Utility functions for text formatting and styling."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
|
5
|
+
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.style import Style
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.padding import Padding
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
def style_text(
|
|
15
|
+
text: str,
|
|
16
|
+
color: Optional[str] = None,
|
|
17
|
+
bold: bool = False,
|
|
18
|
+
italic: bool = False,
|
|
19
|
+
underline: bool = False
|
|
20
|
+
) -> Text:
|
|
21
|
+
"""
|
|
22
|
+
Apply styling to text.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
text: Text to style
|
|
26
|
+
color: Text color
|
|
27
|
+
bold: Bold text
|
|
28
|
+
italic: Italic text
|
|
29
|
+
underline: Underline text
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Rich Text object with applied styling
|
|
33
|
+
"""
|
|
34
|
+
style = []
|
|
35
|
+
if color:
|
|
36
|
+
style.append(color)
|
|
37
|
+
if bold:
|
|
38
|
+
style.append("bold")
|
|
39
|
+
if italic:
|
|
40
|
+
style.append("italic")
|
|
41
|
+
if underline:
|
|
42
|
+
style.append("underline")
|
|
43
|
+
|
|
44
|
+
return Text(text, style=" ".join(style))
|
|
45
|
+
|
|
46
|
+
def format_error(message: str, title: str = "Error") -> None:
|
|
47
|
+
"""
|
|
48
|
+
Format and display error message.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
message: Error message
|
|
52
|
+
title: Error title
|
|
53
|
+
"""
|
|
54
|
+
console.print(f"[bold red]{title}:[/] {message}")
|
|
55
|
+
|
|
56
|
+
def format_warning(message: str, title: str = "Warning") -> None:
|
|
57
|
+
"""
|
|
58
|
+
Format and display warning message.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
message: Warning message
|
|
62
|
+
title: Warning title
|
|
63
|
+
"""
|
|
64
|
+
console.print(f"[bold yellow]{title}:[/] {message}")
|
|
65
|
+
|
|
66
|
+
def format_success(message: str, title: str = "Success") -> None:
|
|
67
|
+
"""
|
|
68
|
+
Format and display success message.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
message: Success message
|
|
72
|
+
title: Success title
|
|
73
|
+
"""
|
|
74
|
+
console.print(f"[bold green]{title}:[/] {message}")
|
|
75
|
+
|
|
76
|
+
def format_info(message: str, title: str = "Info") -> None:
|
|
77
|
+
"""
|
|
78
|
+
Format and display info message.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
message: Info message
|
|
82
|
+
title: Info title
|
|
83
|
+
"""
|
|
84
|
+
console.print(f"[bold blue]{title}:[/] {message}")
|
|
85
|
+
|
|
86
|
+
def create_table(
|
|
87
|
+
headers: List[str],
|
|
88
|
+
rows: List[List[Any]],
|
|
89
|
+
title: Optional[str] = None,
|
|
90
|
+
style: str = "default",
|
|
91
|
+
show_lines: bool = False
|
|
92
|
+
) -> Table:
|
|
93
|
+
"""
|
|
94
|
+
Create a formatted table.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
headers: Column headers
|
|
98
|
+
rows: Table rows
|
|
99
|
+
title: Table title
|
|
100
|
+
style: Table style
|
|
101
|
+
show_lines: Show row/column lines
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Rich Table object
|
|
105
|
+
"""
|
|
106
|
+
table = Table(
|
|
107
|
+
title=title,
|
|
108
|
+
show_header=True,
|
|
109
|
+
header_style="bold blue",
|
|
110
|
+
show_lines=show_lines
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Add columns
|
|
114
|
+
for header in headers:
|
|
115
|
+
table.add_column(header)
|
|
116
|
+
|
|
117
|
+
# Add rows
|
|
118
|
+
for row in rows:
|
|
119
|
+
table.add_row(*[str(cell) for cell in row])
|
|
120
|
+
|
|
121
|
+
return table
|
|
122
|
+
|
|
123
|
+
def truncate_text(
|
|
124
|
+
text: str,
|
|
125
|
+
max_length: int,
|
|
126
|
+
suffix: str = "..."
|
|
127
|
+
) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Truncate text to specified length.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
text: Text to truncate
|
|
133
|
+
max_length: Maximum length
|
|
134
|
+
suffix: Truncation suffix
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Truncated text
|
|
138
|
+
"""
|
|
139
|
+
if len(text) <= max_length:
|
|
140
|
+
return text
|
|
141
|
+
return text[:max_length - len(suffix)] + suffix
|
|
142
|
+
|
|
143
|
+
def wrap_text(
|
|
144
|
+
text: str,
|
|
145
|
+
width: int,
|
|
146
|
+
indent: str = "",
|
|
147
|
+
initial_indent: str = ""
|
|
148
|
+
) -> str:
|
|
149
|
+
"""
|
|
150
|
+
Wrap text to specified width.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
text: Text to wrap
|
|
154
|
+
width: Maximum line width
|
|
155
|
+
indent: Indentation for wrapped lines
|
|
156
|
+
initial_indent: Indentation for first line
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Wrapped text
|
|
160
|
+
"""
|
|
161
|
+
import textwrap
|
|
162
|
+
return textwrap.fill(
|
|
163
|
+
text,
|
|
164
|
+
width=width,
|
|
165
|
+
initial_indent=initial_indent,
|
|
166
|
+
subsequent_indent=indent
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def format_dict(
|
|
170
|
+
data: Dict[str, Any],
|
|
171
|
+
indent: int = 2,
|
|
172
|
+
sort_keys: bool = True
|
|
173
|
+
) -> str:
|
|
174
|
+
"""
|
|
175
|
+
Format dictionary for display.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
data: Dictionary to format
|
|
179
|
+
indent: Indentation level
|
|
180
|
+
sort_keys: Sort dictionary keys
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Formatted string
|
|
184
|
+
"""
|
|
185
|
+
import json
|
|
186
|
+
return json.dumps(
|
|
187
|
+
data,
|
|
188
|
+
indent=indent,
|
|
189
|
+
sort_keys=sort_keys,
|
|
190
|
+
default=str
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def format_list(
|
|
194
|
+
items: List[Any],
|
|
195
|
+
bullet: str = "•",
|
|
196
|
+
indent: int = 2
|
|
197
|
+
) -> str:
|
|
198
|
+
"""
|
|
199
|
+
Format list for display.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
items: List to format
|
|
203
|
+
bullet: Bullet point character
|
|
204
|
+
indent: Indentation level
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Formatted string
|
|
208
|
+
"""
|
|
209
|
+
indent_str = " " * indent
|
|
210
|
+
return "\n".join(f"{indent_str}{bullet} {item}" for item in items)
|
|
211
|
+
|
|
212
|
+
def strip_ansi(text: str) -> str:
|
|
213
|
+
"""
|
|
214
|
+
Remove ANSI escape sequences from text.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
text: Text containing ANSI sequences
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
Clean text
|
|
221
|
+
"""
|
|
222
|
+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
|
|
223
|
+
return ansi_escape.sub('', text)
|
|
224
|
+
|
|
225
|
+
def get_terminal_size() -> tuple:
|
|
226
|
+
"""
|
|
227
|
+
Get terminal size.
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Tuple of (width, height)
|
|
231
|
+
"""
|
|
232
|
+
return console.size
|
|
233
|
+
|
|
234
|
+
def clear_screen() -> None:
|
|
235
|
+
"""Clear the terminal screen."""
|
|
236
|
+
console.clear()
|
|
237
|
+
|
|
238
|
+
def create_padding(
|
|
239
|
+
renderable: Any,
|
|
240
|
+
pad: Union[int, tuple] = 1
|
|
241
|
+
) -> Padding:
|
|
242
|
+
"""
|
|
243
|
+
Add padding around content.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
renderable: Content to pad
|
|
247
|
+
pad: Padding amount
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
Padded content
|
|
251
|
+
"""
|
|
252
|
+
return Padding(renderable, pad)
|