webscout 6.4__py3-none-any.whl → 6.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.

Files changed (116) hide show
  1. webscout/AIutel.py +7 -54
  2. webscout/DWEBS.py +48 -26
  3. webscout/{YTdownloader.py → Extra/YTToolkit/YTdownloader.py} +990 -1103
  4. webscout/Extra/YTToolkit/__init__.py +3 -0
  5. webscout/{transcriber.py → Extra/YTToolkit/transcriber.py} +1 -1
  6. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  7. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  8. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  9. webscout/Extra/YTToolkit/ytapi/extras.py +45 -0
  10. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  11. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  12. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  13. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  14. webscout/Extra/YTToolkit/ytapi/query.py +37 -0
  15. webscout/Extra/YTToolkit/ytapi/stream.py +60 -0
  16. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  17. webscout/Extra/YTToolkit/ytapi/video.py +102 -0
  18. webscout/Extra/__init__.py +2 -1
  19. webscout/Extra/autocoder/autocoder_utiles.py +119 -101
  20. webscout/Extra/autocoder/rawdog.py +679 -680
  21. webscout/Extra/gguf.py +441 -441
  22. webscout/Extra/markdownlite/__init__.py +862 -0
  23. webscout/Extra/weather_ascii.py +2 -2
  24. webscout/Provider/AISEARCH/__init__.py +2 -0
  25. webscout/Provider/AISEARCH/ooai.py +155 -0
  26. webscout/Provider/Amigo.py +70 -85
  27. webscout/Provider/{prefind.py → Jadve.py} +72 -70
  28. webscout/Provider/Netwrck.py +235 -0
  29. webscout/Provider/Openai.py +4 -3
  30. webscout/Provider/PI.py +292 -221
  31. webscout/Provider/PizzaGPT.py +3 -3
  32. webscout/Provider/Reka.py +0 -1
  33. webscout/Provider/TTS/__init__.py +5 -1
  34. webscout/Provider/TTS/deepgram.py +183 -0
  35. webscout/Provider/TTS/elevenlabs.py +137 -0
  36. webscout/Provider/TTS/gesserit.py +151 -0
  37. webscout/Provider/TTS/murfai.py +139 -0
  38. webscout/Provider/TTS/parler.py +134 -107
  39. webscout/Provider/TTS/streamElements.py +360 -275
  40. webscout/Provider/TTS/utils.py +280 -0
  41. webscout/Provider/TTS/voicepod.py +116 -116
  42. webscout/Provider/TeachAnything.py +15 -2
  43. webscout/Provider/Youchat.py +42 -8
  44. webscout/Provider/__init__.py +8 -21
  45. webscout/Provider/meta.py +794 -779
  46. webscout/Provider/multichat.py +230 -0
  47. webscout/Provider/promptrefine.py +2 -2
  48. webscout/Provider/talkai.py +10 -13
  49. webscout/Provider/turboseek.py +5 -4
  50. webscout/Provider/tutorai.py +8 -112
  51. webscout/Provider/typegpt.py +5 -7
  52. webscout/Provider/x0gpt.py +81 -9
  53. webscout/Provider/yep.py +123 -361
  54. webscout/__init__.py +33 -28
  55. webscout/conversation.py +24 -9
  56. webscout/exceptions.py +188 -20
  57. webscout/litprinter/__init__.py +719 -831
  58. webscout/litprinter/colors.py +54 -0
  59. webscout/optimizers.py +420 -270
  60. webscout/prompt_manager.py +279 -279
  61. webscout/scout/__init__.py +8 -0
  62. webscout/scout/core/__init__.py +7 -0
  63. webscout/scout/core/crawler.py +140 -0
  64. webscout/scout/core/scout.py +571 -0
  65. webscout/scout/core/search_result.py +96 -0
  66. webscout/scout/core/text_analyzer.py +63 -0
  67. webscout/scout/core/text_utils.py +277 -0
  68. webscout/scout/core/web_analyzer.py +52 -0
  69. webscout/scout/core.py +884 -0
  70. webscout/scout/element.py +460 -0
  71. webscout/scout/parsers/__init__.py +69 -0
  72. webscout/scout/parsers/html5lib_parser.py +172 -0
  73. webscout/scout/parsers/html_parser.py +236 -0
  74. webscout/scout/parsers/lxml_parser.py +178 -0
  75. webscout/scout/utils.py +38 -0
  76. webscout/update_checker.py +184 -125
  77. webscout/version.py +1 -1
  78. webscout/zeroart/__init__.py +55 -0
  79. webscout/zeroart/base.py +60 -0
  80. webscout/zeroart/effects.py +99 -0
  81. webscout/zeroart/fonts.py +816 -0
  82. webscout/zerodir/__init__.py +225 -0
  83. {webscout-6.4.dist-info → webscout-6.6.dist-info}/METADATA +18 -231
  84. webscout-6.6.dist-info/RECORD +197 -0
  85. webscout-6.6.dist-info/top_level.txt +2 -0
  86. webstoken/__init__.py +30 -0
  87. webstoken/classifier.py +189 -0
  88. webstoken/keywords.py +216 -0
  89. webstoken/language.py +128 -0
  90. webstoken/ner.py +164 -0
  91. webstoken/normalizer.py +35 -0
  92. webstoken/processor.py +77 -0
  93. webstoken/sentiment.py +206 -0
  94. webstoken/stemmer.py +73 -0
  95. webstoken/t.py +75 -0
  96. webstoken/tagger.py +60 -0
  97. webstoken/tokenizer.py +158 -0
  98. webscout/Agents/Onlinesearcher.py +0 -182
  99. webscout/Agents/__init__.py +0 -2
  100. webscout/Agents/functioncall.py +0 -248
  101. webscout/Bing_search.py +0 -251
  102. webscout/Provider/Perplexity.py +0 -599
  103. webscout/Provider/RoboCoders.py +0 -206
  104. webscout/Provider/genspark.py +0 -225
  105. webscout/Provider/perplexitylabs.py +0 -265
  106. webscout/Provider/twitterclone.py +0 -251
  107. webscout/Provider/upstage.py +0 -230
  108. webscout/gpt4free.py +0 -666
  109. webscout/requestsHTMLfix.py +0 -775
  110. webscout/webai.py +0 -2590
  111. webscout-6.4.dist-info/RECORD +0 -154
  112. webscout-6.4.dist-info/top_level.txt +0 -1
  113. /webscout/Provider/{felo_search.py → AISEARCH/felo_search.py} +0 -0
  114. {webscout-6.4.dist-info → webscout-6.6.dist-info}/LICENSE.md +0 -0
  115. {webscout-6.4.dist-info → webscout-6.6.dist-info}/WHEEL +0 -0
  116. {webscout-6.4.dist-info → webscout-6.6.dist-info}/entry_points.txt +0 -0
@@ -1,681 +1,680 @@
1
- """RawDog module for generating and auto-executing Python scripts in the CLI."""
2
-
3
- import os
4
- import re
5
- import sys
6
- import click
7
- import queue
8
- import threading
9
- import platform
10
- import datetime
11
- import subprocess
12
- import pygetwindow as gw
13
- from rich import print as rprint
14
- from rich.panel import Panel
15
- from rich.syntax import Syntax
16
- from rich.console import Console, Group
17
- from rich.markdown import Markdown
18
- from rich.table import Table
19
- from rich.style import Style
20
- from rich.theme import Theme
21
- from rich.live import Live
22
- from rich.status import Status
23
- from rich.rule import Rule
24
- from typing import Optional, Dict, Any, Generator, List, Tuple
25
- from webscout.AIutel import run_system_command, default_path
26
- from webscout import LitLogger, LogFormat, ColorScheme
27
- from webscout.litprinter import LitPrinter
28
- from .autocoder_utiles import EXAMPLES, get_intro_prompt
29
-
30
- # Initialize LitLogger with custom format and colors
31
- logger = LitLogger(
32
- name="RawDog",
33
- format=LogFormat.MODERN_EMOJI,
34
- color_scheme=ColorScheme.CYBERPUNK
35
- )
36
-
37
- # Custom theme for consistent styling
38
- CUSTOM_THEME = Theme({
39
- "info": "cyan",
40
- "warning": "yellow",
41
- "error": "red bold",
42
- "success": "green",
43
- "code": "blue",
44
- "output": "white",
45
- })
46
-
47
- console = Console(theme=CUSTOM_THEME)
48
-
49
- class AutoCoder:
50
- """Generate and auto-execute Python scripts in the CLI with advanced error handling and retry logic.
51
-
52
- This class provides:
53
- - Automatic code generation
54
- - Script execution with safety checks
55
- - Advanced error handling and retries
56
- - Beautiful logging with LitLogger
57
-
58
- Examples:
59
- >>> coder = AutoCoder()
60
- >>> coder.execute("Get system info")
61
- Generating system info script...
62
- Script executed successfully!
63
- """
64
-
65
- examples = EXAMPLES
66
-
67
- def __init__(
68
- self,
69
- quiet: bool = False,
70
- internal_exec: bool = False,
71
- confirm_script: bool = False,
72
- interpreter: str = "python",
73
- prettify: bool = True,
74
- path_to_script: str = "",
75
- max_retries: int = 3,
76
- ai_instance = None
77
- ):
78
- """Initialize AutoCoder instance.
79
-
80
- Args:
81
- quiet (bool): Flag to control logging. Defaults to False.
82
- internal_exec (bool): Execute scripts with exec function. Defaults to False.
83
- confirm_script (bool): Give consent to scripts prior to execution. Defaults to False.
84
- interpreter (str): Python's interpreter name. Defaults to "python".
85
- prettify (bool): Prettify the code on stdout. Defaults to True.
86
- path_to_script (str): Path to save generated scripts. Defaults to "".
87
- max_retries (int): Maximum number of retry attempts. Defaults to 3.
88
- ai_instance: AI instance for error correction. Defaults to None.
89
- """
90
- self.internal_exec = internal_exec
91
- self.confirm_script = confirm_script
92
- self.quiet = quiet
93
- self.interpreter = interpreter
94
- self.prettify = prettify
95
- self.path_to_script = path_to_script or os.path.join(default_path, "execute_this.py")
96
- self.max_retries = max_retries
97
- self.tried_solutions = set()
98
- self.ai_instance = ai_instance
99
-
100
- # Initialize logger with modern format and cyberpunk colors
101
- self.logger = LitLogger(
102
- name="AutoCoder",
103
- format=LogFormat.MODERN_EMOJI,
104
- color_scheme=ColorScheme.CYBERPUNK,
105
- console_output=not quiet
106
- )
107
-
108
- # Get Python version with enhanced logging
109
- self.logger.info("Initializing AutoCoder...")
110
- if self.internal_exec:
111
- self.python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
112
- self.logger.info(f"Using internal Python {self.python_version}")
113
- else:
114
- version_output = run_system_command(
115
- f"{self.interpreter} --version",
116
- exit_on_error=True,
117
- stdout_error=True,
118
- help="If you're using Webscout-cli, use the flag '--internal-exec'"
119
- )[1].stdout
120
- self.python_version = version_output.split(" ")[1]
121
- self.logger.info(f"Using external Python {self.python_version}")
122
-
123
- self.logger.success("AutoCoder initialized successfully!")
124
-
125
- def get_current_app(self) -> str:
126
- """Get the name of the currently active application.
127
-
128
- Returns:
129
- str: Name of the active window or "Unknown"
130
- """
131
- try:
132
- active_window = gw.getActiveWindow()
133
- if active_window:
134
- return active_window.title
135
- except Exception as e:
136
- self.logger.error(f"Error getting active window: {e}")
137
- return "Unknown"
138
-
139
- def _extract_code_blocks(self, response: str) -> List[Tuple[str, str]]:
140
- """Extract code blocks from a response string.
141
-
142
- Args:
143
- response (str): Response string containing code blocks
144
-
145
- Returns:
146
- List[Tuple[str, str]]: List of (code_type, code) tuples
147
- """
148
- blocks = []
149
-
150
- # First try to find code blocks with explicit language tags
151
- pattern = r"```(\w+)\n(.*?)```"
152
- matches = re.finditer(pattern, response, re.DOTALL)
153
-
154
- for match in matches:
155
- code_type = match.group(1).lower()
156
- code = match.group(2).strip()
157
- blocks.append(('python', code))
158
-
159
- # If no explicit code blocks found, treat as Python code
160
- if not blocks:
161
- lines = [line.strip() for line in response.split('\n') if line.strip()]
162
- for line in lines:
163
- blocks.append(('python', line))
164
-
165
- return blocks
166
-
167
- def _execute_code_block(self, code_type: str, code: str, ai_instance=None) -> Optional[str]:
168
- """Execute a code block.
169
-
170
- Args:
171
- code_type (str): Type of code block ('python')
172
- code (str): Code to execute
173
- ai_instance: Optional AI instance for error correction
174
-
175
- Returns:
176
- Optional[str]: Error message if execution failed, None if successful
177
- """
178
- try:
179
- return self._execute_with_retry(code, ai_instance)
180
- except Exception as e:
181
- return str(e)
182
-
183
- def _format_output_panel(self, code: str, output_lines: list) -> Panel:
184
- """Format code and output into a single panel.
185
-
186
- Args:
187
- code (str): The code that was executed
188
- output_lines (list): List of output lines
189
-
190
- Returns:
191
- Panel: Formatted panel with code and output
192
- """
193
- code_syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
194
-
195
- # Format output
196
- output_text = "\n".join(output_lines) if output_lines else "Running..."
197
-
198
- # Combine code and output with a separator
199
- content = Group(
200
- code_syntax,
201
- Rule(style="bright_blue"),
202
- output_text
203
- )
204
-
205
- # Create panel
206
- panel = Panel(
207
- content,
208
- title="[bold blue]Code Execution[/bold blue]",
209
- border_style="blue",
210
- expand=True,
211
- padding=(0, 1)
212
- )
213
-
214
- return panel
215
-
216
- def _stream_output(self, process: subprocess.Popen) -> Generator[str, None, None]:
217
- """Stream output from a subprocess in realtime.
218
-
219
- Args:
220
- process: Subprocess to stream output from
221
-
222
- Yields:
223
- str: Lines of output
224
- """
225
- # Stream stdout
226
- for line in process.stdout:
227
- line = line.strip()
228
- if line:
229
- yield line
230
-
231
- # Check stderr
232
- error = process.stderr.read() if process.stderr else None
233
- if error and error.strip():
234
- yield f"Error: {error.strip()}"
235
-
236
- def _execute_with_retry(self, code: str, ai_instance=None) -> Optional[str]:
237
- """Execute code with retry logic and error correction.
238
-
239
- Args:
240
- code (str): Code to execute
241
- ai_instance: Optional AI instance for error correction
242
-
243
- Returns:
244
- Optional[str]: Error message if execution failed, None if successful
245
- """
246
- last_error = None
247
- retries = 0
248
- while retries < self.max_retries:
249
- try:
250
- if self.path_to_script:
251
- script_dir = os.path.dirname(self.path_to_script)
252
- if script_dir:
253
- os.makedirs(script_dir, exist_ok=True)
254
- with open(self.path_to_script, "w", encoding="utf-8") as f:
255
- f.write(code)
256
-
257
- if self.internal_exec:
258
- self.logger.info("Executing code internally")
259
- # Create StringIO for output capture
260
- import io
261
- import sys
262
- stdout = io.StringIO()
263
- stderr = io.StringIO()
264
-
265
- # Create a queue for realtime output
266
- output_queue = queue.Queue()
267
- output_lines = []
268
-
269
- def execute_code():
270
- try:
271
- # Redirect stdout/stderr
272
- sys.stdout = stdout
273
- sys.stderr = stderr
274
-
275
- # Execute the code
276
- exec(code, globals())
277
-
278
- # Get any output
279
- output = stdout.getvalue()
280
- error = stderr.getvalue()
281
-
282
- if error:
283
- output_queue.put(("error", error))
284
- elif output:
285
- output_queue.put(("output", output))
286
-
287
- finally:
288
- # Restore stdout/stderr
289
- sys.stdout = sys.__stdout__
290
- sys.stderr = sys.__stderr__
291
-
292
- # Create and start execution thread
293
- thread = threading.Thread(target=execute_code)
294
- thread.start()
295
-
296
- # Display output in realtime
297
- with Live(auto_refresh=False, transient=True) as live:
298
- while thread.is_alive() or not output_queue.empty():
299
- try:
300
- msg_type, content = output_queue.get_nowait()
301
- if content:
302
- output_lines.extend(content.splitlines())
303
- live.update(self._format_output_panel(code, output_lines))
304
- live.refresh()
305
- except queue.Empty:
306
- continue
307
-
308
- thread.join()
309
-
310
- # Check for any final errors
311
- error = stderr.getvalue()
312
- if error:
313
- raise Exception(error)
314
-
315
- else:
316
- self.logger.info("Executing code as external process")
317
- process = subprocess.Popen(
318
- [self.interpreter, self.path_to_script],
319
- stdout=subprocess.PIPE,
320
- stderr=subprocess.PIPE,
321
- text=True,
322
- bufsize=1,
323
- universal_newlines=True
324
- )
325
-
326
- output_lines = []
327
- # Stream output in realtime
328
- with Live(auto_refresh=False, transient=True) as live:
329
- for line in self._stream_output(process):
330
- output_lines.append(line)
331
- live.update(self._format_output_panel(code, output_lines))
332
- live.refresh()
333
-
334
- process.wait()
335
- error = process.stderr.read() if not isinstance(process.stderr, str) else process.stderr
336
- if process.returncode != 0 and error:
337
- raise Exception(error)
338
-
339
- return None
340
-
341
- except Exception as e:
342
- last_error = e
343
- if retries < self.max_retries - 1 and ai_instance:
344
- error_context = self._get_error_context(e, code)
345
- try:
346
- self.logger.info(f"Attempting correction (retry {retries + 1}/{self.max_retries})")
347
- fixed_response = ai_instance.chat(error_context)
348
- fixed_code = self._extract_code_from_response(fixed_response)
349
-
350
- if not fixed_code:
351
- self.logger.error("AI provided empty response")
352
- break
353
-
354
- if self._is_similar_solution(fixed_code):
355
- self.logger.warning("AI provided similar solution, requesting different approach")
356
- error_context += "\nPrevious solutions were not successful. Please provide a significantly different approach."
357
- fixed_response = ai_instance.chat(error_context)
358
- fixed_code = self._extract_code_from_response(fixed_response)
359
-
360
- if self._is_similar_solution(fixed_code):
361
- self.logger.error("AI unable to provide sufficiently different solution")
362
- break
363
-
364
- code = fixed_code
365
- retries += 1
366
- continue
367
- except Exception as ai_error:
368
- self.logger.error(f"Error getting AI correction: {str(ai_error)}")
369
- break
370
- break
371
-
372
- return str(last_error) if last_error else "Unknown error occurred"
373
-
374
- def execute(self, prompt: str, ai_instance=None) -> bool:
375
- """Execute the given prompt using the appropriate executor.
376
-
377
- Args:
378
- prompt (str): Prompt to execute
379
- ai_instance: Optional AI instance for error correction
380
-
381
- Returns:
382
- bool: True if execution was successful, False otherwise
383
- """
384
- try:
385
- # Extract code blocks
386
- code_blocks = self._extract_code_blocks(prompt)
387
- if not code_blocks:
388
- self.logger.warning("No code blocks found in prompt")
389
- return False
390
-
391
- # Execute each code block
392
- for code_type, code in code_blocks:
393
- self.logger.info(f"Executing {code_type} block")
394
- error = self._execute_code_block(code_type, code, ai_instance)
395
-
396
- if error:
397
- self.logger.error(f"Execution failed: {error}")
398
- return False
399
-
400
- return True
401
-
402
- except Exception as e:
403
- self.logger.error(f"Execution error: {str(e)}")
404
- return False
405
-
406
- def _extract_code_from_response(self, response: str) -> str:
407
- """Extract code from AI response.
408
-
409
- Args:
410
- response (str): AI response containing code blocks
411
-
412
- Returns:
413
- str: Extracted code from the first code block
414
- """
415
- code_blocks = self._extract_code_blocks(response)
416
- if not code_blocks:
417
- return ""
418
-
419
- # Return the content of the first code block, regardless of type
420
- return code_blocks[0][1]
421
-
422
- def _get_error_context(self, error: Exception, code: str) -> str:
423
- """Create context about the error for AI correction.
424
-
425
- Args:
426
- error (Exception): The caught exception
427
- code (str): The code that caused the error
428
-
429
- Returns:
430
- str: Formatted error context for AI
431
- """
432
- error_type = type(error).__name__
433
- error_msg = str(error)
434
-
435
- return f"""
436
- The code failed with error:
437
- Error Type: {error_type}
438
- Error Message: {error_msg}
439
-
440
- Original Code:
441
- ```python
442
- {code}
443
- ```
444
-
445
- Please fix the code to handle this error. Provide only the corrected code without any explanation.
446
- """
447
-
448
- def _handle_import_error(self, error: ImportError, code: str) -> Optional[str]:
449
- """Handle missing package errors by attempting to install them.
450
-
451
- Args:
452
- error (ImportError): The import error
453
- code (str): The code that caused the error
454
-
455
- Returns:
456
- Optional[str]: Fixed code or None if installation failed
457
- """
458
- missing_package = str(error).split("'")[1] if "'" in str(error) else str(error).split()[3]
459
- try:
460
- logger.info(f"Installing missing package: {missing_package}")
461
- result = subprocess.run(
462
- [sys.executable, "-m", "pip", "install", missing_package],
463
- capture_output=True,
464
- text=True
465
- )
466
- if result.returncode == 0:
467
- logger.success(f"Successfully installed {missing_package}")
468
- return code # Retry with same code after installing package
469
- else:
470
- raise Exception(f"Failed to install {missing_package}: {result.stderr}")
471
- except Exception as e:
472
- logger.error(f"Error installing package: {str(e)}")
473
- return None
474
-
475
- def _is_similar_solution(self, new_code: str, threshold: float = 0.8) -> bool:
476
- """Check if the new solution is too similar to previously tried ones.
477
-
478
- Args:
479
- new_code (str): New solution to check
480
- threshold (float): Similarity threshold (0-1). Defaults to 0.8.
481
-
482
- Returns:
483
- bool: True if solution is too similar to previous attempts
484
- """
485
- import difflib
486
-
487
- def normalize_code(code: str) -> str:
488
- lines = [line.split('#')[0].strip() for line in code.split('\n')]
489
- return '\n'.join(line for line in lines if line)
490
-
491
- new_code_norm = normalize_code(new_code)
492
-
493
- for tried_code in self.tried_solutions:
494
- tried_code_norm = normalize_code(tried_code)
495
- similarity = difflib.SequenceMatcher(None, new_code_norm, tried_code_norm).ratio()
496
- if similarity > threshold:
497
- return True
498
- return False
499
-
500
- def main(self, response: str) -> Optional[str]:
501
- """Execute code with error correction.
502
-
503
- Args:
504
- response (str): AI response containing code
505
-
506
- Returns:
507
- Optional[str]: Error message if execution failed, None if successful
508
- """
509
- if not response:
510
- return None
511
-
512
- code = self._extract_code_from_response(response)
513
-
514
- # Print the generated code with syntax highlighting
515
- self.print_code(code)
516
-
517
- ai_instance = self.ai_instance or globals().get('ai')
518
-
519
- if not ai_instance:
520
- logger.warning("AI instance not found, error correction disabled")
521
- try:
522
- if self.path_to_script:
523
- script_dir = os.path.dirname(self.path_to_script)
524
- if script_dir:
525
- os.makedirs(script_dir, exist_ok=True)
526
- with open(self.path_to_script, "w", encoding="utf-8") as f:
527
- f.write(code)
528
-
529
- if self.internal_exec:
530
- logger.info("Executing code internally")
531
- exec(code, globals())
532
- else:
533
- logger.info("Executing code as external process")
534
- result = subprocess.run(
535
- [self.interpreter, self.path_to_script],
536
- capture_output=True,
537
- text=True
538
- )
539
- if result.returncode != 0:
540
- raise Exception(result.stderr or result.stdout)
541
- return None
542
- except Exception as e:
543
- return str(e)
544
-
545
- return self._execute_with_retry(code, ai_instance)
546
-
547
- @property
548
- def intro_prompt(self) -> str:
549
- """Get the introduction prompt.
550
-
551
- Returns:
552
- str: Introduction prompt
553
- """
554
- return get_intro_prompt()
555
-
556
- def log(self, message: str, category: str = "info"):
557
- """RawDog logger
558
-
559
- Args:
560
- message (str): Log message
561
- category (str, optional): Log level. Defaults to 'info'.
562
- """
563
- if self.quiet:
564
- return
565
-
566
- message = "[Webscout] - " + message
567
- if category == "error":
568
- logger.error(message)
569
- else:
570
- logger.info(message)
571
-
572
- def stdout(self, message: str, style: str = "info") -> None:
573
- """Enhanced stdout with Rich formatting.
574
-
575
- Args:
576
- message (str): Text to be printed
577
- style (str, optional): Style to apply. Defaults to "info".
578
- """
579
- if not self.prettify:
580
- click.secho(message, fg="yellow")
581
- return
582
-
583
- if message.startswith("```") and message.endswith("```"):
584
- # Handle code blocks
585
- code = message.strip("`").strip()
586
- syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
587
- console.print(Panel(syntax, title="Code", border_style="blue"))
588
- elif "```python" in message:
589
- # Handle markdown code blocks
590
- md = Markdown(message)
591
- console.print(md)
592
- else:
593
- # Handle regular text with optional styling
594
- console.print(message, style=style)
595
-
596
- def print_code(self, code: str, title: str = "Generated Code") -> None:
597
- """Print code with syntax highlighting and panel.
598
-
599
- Args:
600
- code (str): Code to print
601
- title (str, optional): Panel title. Defaults to "Generated Code".
602
- """
603
- if self.prettify:
604
- syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
605
- console.print(Panel(
606
- syntax,
607
- title=f"[bold blue]In [1]: {title}[/bold blue]",
608
- border_style="blue",
609
- expand=True
610
- ))
611
- else:
612
- print(f"\n{title}:")
613
- print(code)
614
-
615
- def print_output(self, output: str, style: str = "output") -> None:
616
- """Print command output with optional styling.
617
-
618
- Args:
619
- output (str): Output to print
620
- style (str, optional): Style to apply. Defaults to "output".
621
- """
622
- if self.prettify:
623
- # Try to detect if output is Python code
624
- try:
625
- # If it looks like Python code, syntax highlight it
626
- compile(output, '<string>', 'exec')
627
- syntax = Syntax(output, "python", theme="monokai", line_numbers=False)
628
- formatted_output = syntax
629
- except SyntaxError:
630
- # If not Python code, treat as plain text
631
- formatted_output = output
632
-
633
- console.print(Panel(
634
- formatted_output,
635
- title="[bold red]Out [1]:[/bold red]",
636
- border_style="red",
637
- expand=True,
638
- padding=(0, 1)
639
- ))
640
- else:
641
- print("\nOutput:")
642
- print(output)
643
-
644
- def print_error(self, error: str) -> None:
645
- """Print error message with styling.
646
-
647
- Args:
648
- error (str): Error message to print
649
- """
650
- if self.prettify:
651
- console.print(f"\n Error:", style="error bold")
652
- console.print(error, style="error")
653
- else:
654
- print("\nError:")
655
- print(error)
656
-
657
- def print_table(self, headers: list, rows: list) -> None:
658
- """Print data in a formatted table.
659
-
660
- Args:
661
- headers (list): Table headers
662
- rows (list): Table rows
663
- """
664
- if not self.prettify:
665
- # Simple ASCII table
666
- print("\n" + "-" * 80)
667
- print("| " + " | ".join(headers) + " |")
668
- print("-" * 80)
669
- for row in rows:
670
- print("| " + " | ".join(str(cell) for cell in row) + " |")
671
- print("-" * 80)
672
- return
673
-
674
- table = Table(show_header=True, header_style="bold cyan")
675
- for header in headers:
676
- table.add_column(header)
677
-
678
- for row in rows:
679
- table.add_row(*[str(cell) for cell in row])
680
-
1
+ """RawDog module for generating and auto-executing Python scripts in the CLI."""
2
+
3
+ import os
4
+ import re
5
+ import sys
6
+ import queue
7
+ import threading
8
+ import platform
9
+ import datetime
10
+ import subprocess
11
+ import pygetwindow as gw
12
+ from rich import print as rprint
13
+ from rich.panel import Panel
14
+ from rich.syntax import Syntax
15
+ from rich.console import Console, Group
16
+ from rich.markdown import Markdown
17
+ from rich.table import Table
18
+ from rich.style import Style
19
+ from rich.theme import Theme
20
+ from rich.live import Live
21
+ from rich.status import Status
22
+ from rich.rule import Rule
23
+ from typing import Optional, Dict, Any, Generator, List, Tuple
24
+ from webscout.AIutel import run_system_command, default_path
25
+ from webscout import LitLogger, LogFormat, ColorScheme
26
+ from webscout.litprinter import LitPrinter
27
+ from .autocoder_utiles import EXAMPLES, get_intro_prompt
28
+
29
+ # Initialize LitLogger with custom format and colors
30
+ logger = LitLogger(
31
+ name="RawDog",
32
+ format=LogFormat.MODERN_EMOJI,
33
+ color_scheme=ColorScheme.CYBERPUNK
34
+ )
35
+
36
+ # Custom theme for consistent styling
37
+ CUSTOM_THEME = Theme({
38
+ "info": "cyan",
39
+ "warning": "yellow",
40
+ "error": "red bold",
41
+ "success": "green",
42
+ "code": "blue",
43
+ "output": "white",
44
+ })
45
+
46
+ console = Console(theme=CUSTOM_THEME)
47
+
48
+ class AutoCoder:
49
+ """Generate and auto-execute Python scripts in the CLI with advanced error handling and retry logic.
50
+
51
+ This class provides:
52
+ - Automatic code generation
53
+ - Script execution with safety checks
54
+ - Advanced error handling and retries
55
+ - Beautiful logging with LitLogger
56
+
57
+ Examples:
58
+ >>> coder = AutoCoder()
59
+ >>> coder.execute("Get system info")
60
+ Generating system info script...
61
+ Script executed successfully!
62
+ """
63
+
64
+ examples = EXAMPLES
65
+
66
+ def __init__(
67
+ self,
68
+ quiet: bool = False,
69
+ internal_exec: bool = False,
70
+ confirm_script: bool = False,
71
+ interpreter: str = "python",
72
+ prettify: bool = True,
73
+ path_to_script: str = "",
74
+ max_retries: int = 3,
75
+ ai_instance = None
76
+ ):
77
+ """Initialize AutoCoder instance.
78
+
79
+ Args:
80
+ quiet (bool): Flag to control logging. Defaults to False.
81
+ internal_exec (bool): Execute scripts with exec function. Defaults to False.
82
+ confirm_script (bool): Give consent to scripts prior to execution. Defaults to False.
83
+ interpreter (str): Python's interpreter name. Defaults to "python".
84
+ prettify (bool): Prettify the code on stdout. Defaults to True.
85
+ path_to_script (str): Path to save generated scripts. Defaults to "".
86
+ max_retries (int): Maximum number of retry attempts. Defaults to 3.
87
+ ai_instance: AI instance for error correction. Defaults to None.
88
+ """
89
+ self.internal_exec = internal_exec
90
+ self.confirm_script = confirm_script
91
+ self.quiet = quiet
92
+ self.interpreter = interpreter
93
+ self.prettify = prettify
94
+ self.path_to_script = path_to_script or os.path.join(default_path, "execute_this.py")
95
+ self.max_retries = max_retries
96
+ self.tried_solutions = set()
97
+ self.ai_instance = ai_instance
98
+
99
+ # Initialize logger with modern format and cyberpunk colors
100
+ self.logger = LitLogger(
101
+ name="AutoCoder",
102
+ format=LogFormat.MODERN_EMOJI,
103
+ color_scheme=ColorScheme.CYBERPUNK,
104
+ console_output=not quiet
105
+ )
106
+
107
+ # Get Python version with enhanced logging
108
+ self.logger.info("Initializing AutoCoder...")
109
+ if self.internal_exec:
110
+ self.python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
111
+ self.logger.info(f"Using internal Python {self.python_version}")
112
+ else:
113
+ version_output = run_system_command(
114
+ f"{self.interpreter} --version",
115
+ exit_on_error=True,
116
+ stdout_error=True,
117
+ help="If you're using Webscout-cli, use the flag '--internal-exec'"
118
+ )[1].stdout
119
+ self.python_version = version_output.split(" ")[1]
120
+ self.logger.info(f"Using external Python {self.python_version}")
121
+
122
+ self.logger.success("AutoCoder initialized successfully!")
123
+
124
+ def get_current_app(self) -> str:
125
+ """Get the name of the currently active application.
126
+
127
+ Returns:
128
+ str: Name of the active window or "Unknown"
129
+ """
130
+ try:
131
+ active_window = gw.getActiveWindow()
132
+ if active_window:
133
+ return active_window.title
134
+ except Exception as e:
135
+ self.logger.error(f"Error getting active window: {e}")
136
+ return "Unknown"
137
+
138
+ def _extract_code_blocks(self, response: str) -> List[Tuple[str, str]]:
139
+ """Extract code blocks from a response string.
140
+
141
+ Args:
142
+ response (str): Response string containing code blocks
143
+
144
+ Returns:
145
+ List[Tuple[str, str]]: List of (code_type, code) tuples
146
+ """
147
+ blocks = []
148
+
149
+ # First try to find code blocks with explicit language tags
150
+ pattern = r"```(\w+)\n(.*?)```"
151
+ matches = re.finditer(pattern, response, re.DOTALL)
152
+
153
+ for match in matches:
154
+ code_type = match.group(1).lower()
155
+ code = match.group(2).strip()
156
+ blocks.append(('python', code))
157
+
158
+ # If no explicit code blocks found, treat as Python code
159
+ if not blocks:
160
+ lines = [line.strip() for line in response.split('\n') if line.strip()]
161
+ for line in lines:
162
+ blocks.append(('python', line))
163
+
164
+ return blocks
165
+
166
+ def _execute_code_block(self, code_type: str, code: str, ai_instance=None) -> Optional[str]:
167
+ """Execute a code block.
168
+
169
+ Args:
170
+ code_type (str): Type of code block ('python')
171
+ code (str): Code to execute
172
+ ai_instance: Optional AI instance for error correction
173
+
174
+ Returns:
175
+ Optional[str]: Error message if execution failed, None if successful
176
+ """
177
+ try:
178
+ return self._execute_with_retry(code, ai_instance)
179
+ except Exception as e:
180
+ return str(e)
181
+
182
+ def _format_output_panel(self, code: str, output_lines: list) -> Panel:
183
+ """Format code and output into a single panel.
184
+
185
+ Args:
186
+ code (str): The code that was executed
187
+ output_lines (list): List of output lines
188
+
189
+ Returns:
190
+ Panel: Formatted panel with code and output
191
+ """
192
+ code_syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
193
+
194
+ # Format output
195
+ output_text = "\n".join(output_lines) if output_lines else "Running..."
196
+
197
+ # Combine code and output with a separator
198
+ content = Group(
199
+ code_syntax,
200
+ Rule(style="bright_blue"),
201
+ output_text
202
+ )
203
+
204
+ # Create panel
205
+ panel = Panel(
206
+ content,
207
+ title="[bold blue]Code Execution[/bold blue]",
208
+ border_style="blue",
209
+ expand=True,
210
+ padding=(0, 1)
211
+ )
212
+
213
+ return panel
214
+
215
+ def _stream_output(self, process: subprocess.Popen) -> Generator[str, None, None]:
216
+ """Stream output from a subprocess in realtime.
217
+
218
+ Args:
219
+ process: Subprocess to stream output from
220
+
221
+ Yields:
222
+ str: Lines of output
223
+ """
224
+ # Stream stdout
225
+ for line in process.stdout:
226
+ line = line.strip()
227
+ if line:
228
+ yield line
229
+
230
+ # Check stderr
231
+ error = process.stderr.read() if process.stderr else None
232
+ if error and error.strip():
233
+ yield f"Error: {error.strip()}"
234
+
235
+ def _execute_with_retry(self, code: str, ai_instance=None) -> Optional[str]:
236
+ """Execute code with retry logic and error correction.
237
+
238
+ Args:
239
+ code (str): Code to execute
240
+ ai_instance: Optional AI instance for error correction
241
+
242
+ Returns:
243
+ Optional[str]: Error message if execution failed, None if successful
244
+ """
245
+ last_error = None
246
+ retries = 0
247
+ while retries < self.max_retries:
248
+ try:
249
+ if self.path_to_script:
250
+ script_dir = os.path.dirname(self.path_to_script)
251
+ if script_dir:
252
+ os.makedirs(script_dir, exist_ok=True)
253
+ with open(self.path_to_script, "w", encoding="utf-8") as f:
254
+ f.write(code)
255
+
256
+ if self.internal_exec:
257
+ self.logger.info("Executing code internally")
258
+ # Create StringIO for output capture
259
+ import io
260
+ import sys
261
+ stdout = io.StringIO()
262
+ stderr = io.StringIO()
263
+
264
+ # Create a queue for realtime output
265
+ output_queue = queue.Queue()
266
+ output_lines = []
267
+
268
+ def execute_code():
269
+ try:
270
+ # Redirect stdout/stderr
271
+ sys.stdout = stdout
272
+ sys.stderr = stderr
273
+
274
+ # Execute the code
275
+ exec(code, globals())
276
+
277
+ # Get any output
278
+ output = stdout.getvalue()
279
+ error = stderr.getvalue()
280
+
281
+ if error:
282
+ output_queue.put(("error", error))
283
+ elif output:
284
+ output_queue.put(("output", output))
285
+
286
+ finally:
287
+ # Restore stdout/stderr
288
+ sys.stdout = sys.__stdout__
289
+ sys.stderr = sys.__stderr__
290
+
291
+ # Create and start execution thread
292
+ thread = threading.Thread(target=execute_code)
293
+ thread.start()
294
+
295
+ # Display output in realtime
296
+ with Live(auto_refresh=False, transient=True) as live:
297
+ while thread.is_alive() or not output_queue.empty():
298
+ try:
299
+ msg_type, content = output_queue.get_nowait()
300
+ if content:
301
+ output_lines.extend(content.splitlines())
302
+ live.update(self._format_output_panel(code, output_lines))
303
+ live.refresh()
304
+ except queue.Empty:
305
+ continue
306
+
307
+ thread.join()
308
+
309
+ # Check for any final errors
310
+ error = stderr.getvalue()
311
+ if error:
312
+ raise Exception(error)
313
+
314
+ else:
315
+ self.logger.info("Executing code as external process")
316
+ process = subprocess.Popen(
317
+ [self.interpreter, self.path_to_script],
318
+ stdout=subprocess.PIPE,
319
+ stderr=subprocess.PIPE,
320
+ text=True,
321
+ bufsize=1,
322
+ universal_newlines=True
323
+ )
324
+
325
+ output_lines = []
326
+ # Stream output in realtime
327
+ with Live(auto_refresh=False, transient=True) as live:
328
+ for line in self._stream_output(process):
329
+ output_lines.append(line)
330
+ live.update(self._format_output_panel(code, output_lines))
331
+ live.refresh()
332
+
333
+ process.wait()
334
+ error = process.stderr.read() if not isinstance(process.stderr, str) else process.stderr
335
+ if process.returncode != 0 and error:
336
+ raise Exception(error)
337
+
338
+ return None
339
+
340
+ except Exception as e:
341
+ last_error = e
342
+ if retries < self.max_retries - 1 and ai_instance:
343
+ error_context = self._get_error_context(e, code)
344
+ try:
345
+ self.logger.info(f"Attempting correction (retry {retries + 1}/{self.max_retries})")
346
+ fixed_response = ai_instance.chat(error_context)
347
+ fixed_code = self._extract_code_from_response(fixed_response)
348
+
349
+ if not fixed_code:
350
+ self.logger.error("AI provided empty response")
351
+ break
352
+
353
+ if self._is_similar_solution(fixed_code):
354
+ self.logger.warning("AI provided similar solution, requesting different approach")
355
+ error_context += "\nPrevious solutions were not successful. Please provide a significantly different approach."
356
+ fixed_response = ai_instance.chat(error_context)
357
+ fixed_code = self._extract_code_from_response(fixed_response)
358
+
359
+ if self._is_similar_solution(fixed_code):
360
+ self.logger.error("AI unable to provide sufficiently different solution")
361
+ break
362
+
363
+ code = fixed_code
364
+ retries += 1
365
+ continue
366
+ except Exception as ai_error:
367
+ self.logger.error(f"Error getting AI correction: {str(ai_error)}")
368
+ break
369
+ break
370
+
371
+ return str(last_error) if last_error else "Unknown error occurred"
372
+
373
+ def execute(self, prompt: str, ai_instance=None) -> bool:
374
+ """Execute the given prompt using the appropriate executor.
375
+
376
+ Args:
377
+ prompt (str): Prompt to execute
378
+ ai_instance: Optional AI instance for error correction
379
+
380
+ Returns:
381
+ bool: True if execution was successful, False otherwise
382
+ """
383
+ try:
384
+ # Extract code blocks
385
+ code_blocks = self._extract_code_blocks(prompt)
386
+ if not code_blocks:
387
+ self.logger.warning("No code blocks found in prompt")
388
+ return False
389
+
390
+ # Execute each code block
391
+ for code_type, code in code_blocks:
392
+ self.logger.info(f"Executing {code_type} block")
393
+ error = self._execute_code_block(code_type, code, ai_instance)
394
+
395
+ if error:
396
+ self.logger.error(f"Execution failed: {error}")
397
+ return False
398
+
399
+ return True
400
+
401
+ except Exception as e:
402
+ self.logger.error(f"Execution error: {str(e)}")
403
+ return False
404
+
405
+ def _extract_code_from_response(self, response: str) -> str:
406
+ """Extract code from AI response.
407
+
408
+ Args:
409
+ response (str): AI response containing code blocks
410
+
411
+ Returns:
412
+ str: Extracted code from the first code block
413
+ """
414
+ code_blocks = self._extract_code_blocks(response)
415
+ if not code_blocks:
416
+ return ""
417
+
418
+ # Return the content of the first code block, regardless of type
419
+ return code_blocks[0][1]
420
+
421
+ def _get_error_context(self, error: Exception, code: str) -> str:
422
+ """Create context about the error for AI correction.
423
+
424
+ Args:
425
+ error (Exception): The caught exception
426
+ code (str): The code that caused the error
427
+
428
+ Returns:
429
+ str: Formatted error context for AI
430
+ """
431
+ error_type = type(error).__name__
432
+ error_msg = str(error)
433
+
434
+ return f"""
435
+ The code failed with error:
436
+ Error Type: {error_type}
437
+ Error Message: {error_msg}
438
+
439
+ Original Code:
440
+ ```python
441
+ {code}
442
+ ```
443
+
444
+ Please fix the code to handle this error. Provide only the corrected code without any explanation.
445
+ """
446
+
447
+ def _handle_import_error(self, error: ImportError, code: str) -> Optional[str]:
448
+ """Handle missing package errors by attempting to install them.
449
+
450
+ Args:
451
+ error (ImportError): The import error
452
+ code (str): The code that caused the error
453
+
454
+ Returns:
455
+ Optional[str]: Fixed code or None if installation failed
456
+ """
457
+ missing_package = str(error).split("'")[1] if "'" in str(error) else str(error).split()[3]
458
+ try:
459
+ logger.info(f"Installing missing package: {missing_package}")
460
+ result = subprocess.run(
461
+ [sys.executable, "-m", "pip", "install", missing_package],
462
+ capture_output=True,
463
+ text=True
464
+ )
465
+ if result.returncode == 0:
466
+ logger.success(f"Successfully installed {missing_package}")
467
+ return code # Retry with same code after installing package
468
+ else:
469
+ raise Exception(f"Failed to install {missing_package}: {result.stderr}")
470
+ except Exception as e:
471
+ logger.error(f"Error installing package: {str(e)}")
472
+ return None
473
+
474
+ def _is_similar_solution(self, new_code: str, threshold: float = 0.8) -> bool:
475
+ """Check if the new solution is too similar to previously tried ones.
476
+
477
+ Args:
478
+ new_code (str): New solution to check
479
+ threshold (float): Similarity threshold (0-1). Defaults to 0.8.
480
+
481
+ Returns:
482
+ bool: True if solution is too similar to previous attempts
483
+ """
484
+ import difflib
485
+
486
+ def normalize_code(code: str) -> str:
487
+ lines = [line.split('#')[0].strip() for line in code.split('\n')]
488
+ return '\n'.join(line for line in lines if line)
489
+
490
+ new_code_norm = normalize_code(new_code)
491
+
492
+ for tried_code in self.tried_solutions:
493
+ tried_code_norm = normalize_code(tried_code)
494
+ similarity = difflib.SequenceMatcher(None, new_code_norm, tried_code_norm).ratio()
495
+ if similarity > threshold:
496
+ return True
497
+ return False
498
+
499
+ def main(self, response: str) -> Optional[str]:
500
+ """Execute code with error correction.
501
+
502
+ Args:
503
+ response (str): AI response containing code
504
+
505
+ Returns:
506
+ Optional[str]: Error message if execution failed, None if successful
507
+ """
508
+ if not response:
509
+ return None
510
+
511
+ code = self._extract_code_from_response(response)
512
+
513
+ # Print the generated code with syntax highlighting
514
+ self.print_code(code)
515
+
516
+ ai_instance = self.ai_instance or globals().get('ai')
517
+
518
+ if not ai_instance:
519
+ logger.warning("AI instance not found, error correction disabled")
520
+ try:
521
+ if self.path_to_script:
522
+ script_dir = os.path.dirname(self.path_to_script)
523
+ if script_dir:
524
+ os.makedirs(script_dir, exist_ok=True)
525
+ with open(self.path_to_script, "w", encoding="utf-8") as f:
526
+ f.write(code)
527
+
528
+ if self.internal_exec:
529
+ logger.info("Executing code internally")
530
+ exec(code, globals())
531
+ else:
532
+ logger.info("Executing code as external process")
533
+ result = subprocess.run(
534
+ [self.interpreter, self.path_to_script],
535
+ capture_output=True,
536
+ text=True
537
+ )
538
+ if result.returncode != 0:
539
+ raise Exception(result.stderr or result.stdout)
540
+ return None
541
+ except Exception as e:
542
+ return str(e)
543
+
544
+ return self._execute_with_retry(code, ai_instance)
545
+
546
+ @property
547
+ def intro_prompt(self) -> str:
548
+ """Get the introduction prompt.
549
+
550
+ Returns:
551
+ str: Introduction prompt
552
+ """
553
+ return get_intro_prompt()
554
+
555
+ def log(self, message: str, category: str = "info"):
556
+ """RawDog logger
557
+
558
+ Args:
559
+ message (str): Log message
560
+ category (str, optional): Log level. Defaults to 'info'.
561
+ """
562
+ if self.quiet:
563
+ return
564
+
565
+ message = "[Webscout] - " + message
566
+ if category == "error":
567
+ logger.error(message)
568
+ else:
569
+ logger.info(message)
570
+
571
+ def stdout(self, message: str, style: str = "info") -> None:
572
+ """Enhanced stdout with Rich formatting.
573
+
574
+ Args:
575
+ message (str): Text to be printed
576
+ style (str, optional): Style to apply. Defaults to "info".
577
+ """
578
+ if not self.prettify:
579
+ print(message)
580
+ return
581
+
582
+ if message.startswith("```") and message.endswith("```"):
583
+ # Handle code blocks
584
+ code = message.strip("`").strip()
585
+ syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
586
+ console.print(Panel(syntax, title="Code", border_style="blue"))
587
+ elif "```python" in message:
588
+ # Handle markdown code blocks
589
+ md = Markdown(message)
590
+ console.print(md)
591
+ else:
592
+ # Handle regular text with optional styling
593
+ console.print(message, style=style)
594
+
595
+ def print_code(self, code: str, title: str = "Generated Code") -> None:
596
+ """Print code with syntax highlighting and panel.
597
+
598
+ Args:
599
+ code (str): Code to print
600
+ title (str, optional): Panel title. Defaults to "Generated Code".
601
+ """
602
+ if self.prettify:
603
+ syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
604
+ console.print(Panel(
605
+ syntax,
606
+ title=f"[bold blue]In [1]: {title}[/bold blue]",
607
+ border_style="blue",
608
+ expand=True
609
+ ))
610
+ else:
611
+ print(f"\n{title}:")
612
+ print(code)
613
+
614
+ def print_output(self, output: str, style: str = "output") -> None:
615
+ """Print command output with optional styling.
616
+
617
+ Args:
618
+ output (str): Output to print
619
+ style (str, optional): Style to apply. Defaults to "output".
620
+ """
621
+ if self.prettify:
622
+ # Try to detect if output is Python code
623
+ try:
624
+ # If it looks like Python code, syntax highlight it
625
+ compile(output, '<string>', 'exec')
626
+ syntax = Syntax(output, "python", theme="monokai", line_numbers=False)
627
+ formatted_output = syntax
628
+ except SyntaxError:
629
+ # If not Python code, treat as plain text
630
+ formatted_output = output
631
+
632
+ console.print(Panel(
633
+ formatted_output,
634
+ title="[bold red]Out [1]:[/bold red]",
635
+ border_style="red",
636
+ expand=True,
637
+ padding=(0, 1)
638
+ ))
639
+ else:
640
+ print("\nOutput:")
641
+ print(output)
642
+
643
+ def print_error(self, error: str) -> None:
644
+ """Print error message with styling.
645
+
646
+ Args:
647
+ error (str): Error message to print
648
+ """
649
+ if self.prettify:
650
+ console.print(f"\n Error:", style="error bold")
651
+ console.print(error, style="error")
652
+ else:
653
+ print("\nError:")
654
+ print(error)
655
+
656
+ def print_table(self, headers: list, rows: list) -> None:
657
+ """Print data in a formatted table.
658
+
659
+ Args:
660
+ headers (list): Table headers
661
+ rows (list): Table rows
662
+ """
663
+ if not self.prettify:
664
+ # Simple ASCII table
665
+ print("\n" + "-" * 80)
666
+ print("| " + " | ".join(headers) + " |")
667
+ print("-" * 80)
668
+ for row in rows:
669
+ print("| " + " | ".join(str(cell) for cell in row) + " |")
670
+ print("-" * 80)
671
+ return
672
+
673
+ table = Table(show_header=True, header_style="bold cyan")
674
+ for header in headers:
675
+ table.add_column(header)
676
+
677
+ for row in rows:
678
+ table.add_row(*[str(cell) for cell in row])
679
+
681
680
  console.print(table)