ngpt 3.5.6__py3-none-any.whl → 3.6.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.
ngpt/cli/modes/shell.py CHANGED
@@ -1,11 +1,14 @@
1
1
  from ..formatters import COLORS
2
- from ..ui import spinner
2
+ from ..ui import spinner, copy_to_clipboard
3
+ from ..renderers import prettify_markdown, has_markdown_renderer
3
4
  from ...utils import enhance_prompt_with_web_search
4
5
  import subprocess
5
6
  import sys
6
7
  import threading
7
8
  import platform
8
9
  import os
10
+ import shutil
11
+ import re
9
12
 
10
13
  # System prompt for shell command generation
11
14
  SHELL_SYSTEM_PROMPT = """Your role: Provide only plain text without Markdown formatting. Do not show any warnings or information regarding your capabilities. Do not provide any description. If you need to store any data, assume it will be stored in the chat. Provide only {shell_name} command for {operating_system} without any description. If there is a lack of details, provide most logical solution. Ensure the output is a valid shell command. If multiple steps required try to combine them together.
@@ -34,6 +37,100 @@ Your role: Provide only plain text without Markdown formatting. Do not show any
34
37
 
35
38
  Command:"""
36
39
 
40
+ def detect_shell():
41
+ """Detect the current shell type and OS more accurately.
42
+
43
+ Returns:
44
+ tuple: (shell_name, highlight_language, operating_system) - the detected shell name,
45
+ appropriate syntax highlighting language, and operating system
46
+ """
47
+ os_type = platform.system()
48
+
49
+ # Determine OS with detailed information
50
+ if os_type == "Darwin":
51
+ operating_system = "MacOS"
52
+ elif os_type == "Linux":
53
+ # Try to get Linux distribution name
54
+ try:
55
+ result = subprocess.run(["lsb_release", "-si"], capture_output=True, text=True)
56
+ distro = result.stdout.strip()
57
+ operating_system = f"Linux/{distro}" if distro else "Linux"
58
+ except:
59
+ operating_system = "Linux"
60
+ elif os_type == "Windows":
61
+ operating_system = "Windows"
62
+ else:
63
+ operating_system = os_type
64
+
65
+ # Handle WSL specially - it looks like Linux but runs on Windows
66
+ is_wsl = False
67
+ try:
68
+ with open('/proc/version', 'r') as f:
69
+ if 'microsoft' in f.read().lower():
70
+ is_wsl = True
71
+ operating_system = "Windows/WSL"
72
+ except:
73
+ pass
74
+
75
+ # Try to detect the shell by examining environment variables
76
+ try:
77
+ # Check for specific shell by examining SHELL_NAME or equivalent
78
+ if os_type == "Windows" and not is_wsl:
79
+ # Check for Git Bash or MSYS2/Cygwin
80
+ if "MINGW" in os.environ.get("MSYSTEM", "") or "MSYS" in os.environ.get("MSYSTEM", ""):
81
+ return "bash", "bash", operating_system
82
+
83
+ # Check if we're in Git Bash by examining PATH for /mingw/
84
+ if any("/mingw/" in path.lower() for path in os.environ.get("PATH", "").split(os.pathsep)):
85
+ return "bash", "bash", operating_system
86
+
87
+ # Check for WSL within Windows
88
+ if "WSL" in os.environ.get("PATH", "") or "Microsoft" in os.environ.get("PATH", ""):
89
+ return "bash", "bash", operating_system
90
+
91
+ # Check for explicit shell path in environment
92
+ if os.environ.get("SHELL"):
93
+ shell_path = os.environ.get("SHELL").lower()
94
+ if "bash" in shell_path:
95
+ return "bash", "bash", operating_system
96
+ elif "zsh" in shell_path:
97
+ return "zsh", "zsh", operating_system
98
+ elif "powershell" in shell_path:
99
+ return "powershell.exe", "powershell", operating_system
100
+ elif "cmd" in shell_path:
101
+ return "cmd.exe", "batch", operating_system
102
+
103
+ # Check for PowerShell vs CMD
104
+ if os.environ.get("PSModulePath"):
105
+ # Further distinguish PowerShell vs PowerShell Core
106
+ if "pwsh" in os.environ.get("PSModulePath", "").lower():
107
+ return "pwsh", "powershell", operating_system
108
+ else:
109
+ return "powershell.exe", "powershell", operating_system
110
+ else:
111
+ return "cmd.exe", "batch", operating_system
112
+ else:
113
+ # Unix-like systems - try to get more specific
114
+ shell_path = os.environ.get("SHELL", "/bin/bash")
115
+ shell_name = os.path.basename(shell_path)
116
+
117
+ # Map shell name to syntax highlight language
118
+ if shell_name == "zsh":
119
+ return shell_name, "zsh", operating_system
120
+ elif shell_name == "fish":
121
+ return shell_name, "fish", operating_system
122
+ elif "csh" in shell_name:
123
+ return shell_name, "csh", operating_system
124
+ else:
125
+ # Default to bash for sh, bash, and other shells
126
+ return shell_name, "bash", operating_system
127
+ except Exception as e:
128
+ # Fall back to simple detection if anything fails
129
+ if os_type == "Windows":
130
+ return "powershell.exe", "powershell", operating_system
131
+ else:
132
+ return "bash", "bash", operating_system
133
+
37
134
  def shell_mode(client, args, logger=None):
38
135
  """Handle the shell command generation mode.
39
136
 
@@ -94,29 +191,8 @@ def shell_mode(client, args, logger=None):
94
191
  print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
95
192
  # Continue with the original prompt if web search fails
96
193
 
97
- # Determine OS type
98
- os_type = platform.system()
99
- if os_type == "Darwin":
100
- operating_system = "MacOS"
101
- elif os_type == "Linux":
102
- # Try to get Linux distribution name
103
- try:
104
- result = subprocess.run(["lsb_release", "-si"], capture_output=True, text=True)
105
- distro = result.stdout.strip()
106
- operating_system = f"Linux/{distro}" if distro else "Linux"
107
- except:
108
- operating_system = "Linux"
109
- elif os_type == "Windows":
110
- operating_system = "Windows"
111
- else:
112
- operating_system = os_type
113
-
114
- # Determine shell type
115
- if os_type == "Windows":
116
- shell_name = "powershell.exe" if os.environ.get("PSModulePath") else "cmd.exe"
117
- else:
118
- shell_name = os.environ.get("SHELL", "/bin/bash")
119
- shell_name = os.path.basename(shell_name)
194
+ # Detect shell type, highlight language, and operating system
195
+ shell_name, highlight_lang, operating_system = detect_shell()
120
196
 
121
197
  # Format the system prompt based on whether preprompt is provided
122
198
  if args.preprompt:
@@ -185,17 +261,42 @@ def shell_mode(client, args, logger=None):
185
261
  # Log the generated command if logging is enabled
186
262
  if logger:
187
263
  logger.log("assistant", command)
264
+
265
+ # Get the most up-to-date shell type at command generation time
266
+ _, highlight_lang, _ = detect_shell()
267
+
268
+ # Create a markdown-formatted display of the command with appropriate syntax highlighting
269
+ formatted_command = f"### Generated Command\n\n```{highlight_lang}\n{command}\n```"
270
+
271
+ # Check if we can use rich rendering
272
+ if has_markdown_renderer('rich'):
273
+ # Use rich renderer for pretty output
274
+ prettify_markdown(formatted_command, 'rich')
275
+ else:
276
+ # Fallback to simpler formatting
277
+ term_width = shutil.get_terminal_size().columns
278
+ box_width = min(term_width - 4, len(command) + 8)
279
+ horizontal_line = "─" * box_width
188
280
 
189
- print(f"\nGenerated command: {command}")
281
+ print(f"\n┌{horizontal_line}")
282
+ print(f"│ {COLORS['bold']}Generated Command:{COLORS['reset']} {' ' * (box_width - 20)}│")
283
+ print(f"│ {COLORS['green']}{command}{COLORS['reset']}{' ' * (box_width - len(command) - 2)}│")
284
+ print(f"└{horizontal_line}┘\n")
285
+
286
+ # Display options with better formatting
287
+ print(f"{COLORS['bold']}Options:{COLORS['reset']}")
288
+ print(f" {COLORS['cyan']}c{COLORS['reset']} - Copy to clipboard")
289
+ print(f" {COLORS['cyan']}e{COLORS['reset']} - Execute command")
290
+ print(f" {COLORS['cyan']}n{COLORS['reset']} - Cancel (default)")
291
+ print(f"\nWhat would you like to do? [{COLORS['cyan']}c{COLORS['reset']}/{COLORS['cyan']}e{COLORS['reset']}/N] ", end='')
190
292
 
191
293
  try:
192
- print("Do you want to execute this command? [y/N] ", end='')
193
294
  response = input().lower()
194
295
  except KeyboardInterrupt:
195
296
  print("\nCommand execution cancelled by user.")
196
297
  return
197
298
 
198
- if response == 'y' or response == 'yes':
299
+ if response == 'e' or response == 'execute':
199
300
  # Log the execution if logging is enabled
200
301
  if logger:
201
302
  logger.log("system", f"Executing command: {command}")
@@ -224,4 +325,13 @@ def shell_mode(client, args, logger=None):
224
325
  if logger:
225
326
  logger.log("system", f"Command error: {error}")
226
327
 
227
- print(f"\nError:\n{error}")
328
+ print(f"\nError:\n{error}")
329
+ elif response == 'c' or response == 'copy':
330
+ # Copy command to clipboard without confirmation prompt
331
+ copied = copy_to_clipboard(command, skip_confirmation=True)
332
+ if not copied:
333
+ print(f"{COLORS['yellow']}Failed to copy to clipboard. Command: {COLORS['green']}{command}{COLORS['reset']}")
334
+
335
+ # Log the copy if logging is enabled
336
+ if logger:
337
+ logger.log("system", "Command copied to clipboard")
ngpt/cli/ui.py CHANGED
@@ -174,12 +174,13 @@ def get_terminal_input():
174
174
  except (IOError, OSError):
175
175
  return None
176
176
 
177
- def copy_to_clipboard(content, prompt_message=None):
177
+ def copy_to_clipboard(content, prompt_message=None, skip_confirmation=False):
178
178
  """Copy content to clipboard with user confirmation.
179
179
 
180
180
  Args:
181
181
  content: The text content to copy to clipboard
182
182
  prompt_message: Optional custom message for the prompt (default: "Copy to clipboard? (y/n)")
183
+ skip_confirmation: When True, skips the confirmation prompt and copies directly
183
184
 
184
185
  Returns:
185
186
  bool: True if copied to clipboard successfully, False otherwise
@@ -189,17 +190,21 @@ def copy_to_clipboard(content, prompt_message=None):
189
190
  return False
190
191
 
191
192
  try:
192
- # Default prompt message
193
- if prompt_message is None:
194
- prompt_message = "Copy to clipboard? (y/n)"
193
+ # Skip confirmation if requested
194
+ if skip_confirmation:
195
+ answer = 'y'
196
+ else:
197
+ # Default prompt message
198
+ if prompt_message is None:
199
+ prompt_message = "Copy to clipboard? (y/n)"
200
+
201
+ # Make the prompt more visible with colors and formatting
202
+ clipboard_prompt = f"{COLORS['cyan']}{COLORS['bold']}{prompt_message}{COLORS['reset']} "
203
+ print(clipboard_prompt, end="")
204
+ sys.stdout.flush()
195
205
 
196
- # Make the prompt more visible with colors and formatting
197
- clipboard_prompt = f"{COLORS['cyan']}{COLORS['bold']}{prompt_message}{COLORS['reset']} "
198
- print(clipboard_prompt, end="")
199
- sys.stdout.flush()
200
-
201
- # Cross-platform terminal input
202
- answer = get_terminal_input()
206
+ # Cross-platform terminal input
207
+ answer = get_terminal_input()
203
208
 
204
209
  if answer == 'y':
205
210
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.5.6
3
+ Version: 3.6.0
4
4
  Summary: Swiss army knife for LLMs: powerful CLI and interactive chatbot in one package. Seamlessly work with OpenAI, Ollama, Groq, Claude, Gemini, or any OpenAI-compatible API to generate code, craft git commits, rewrite text, and execute shell commands.
5
5
  Project-URL: Homepage, https://github.com/nazdridoy/ngpt
6
6
  Project-URL: Repository, https://github.com/nazdridoy/ngpt
@@ -7,22 +7,22 @@ ngpt/cli/config_manager.py,sha256=NQQcWnjUppAAd0s0p9YAf8EyKS1ex5-0EB4DvKdB4dk,36
7
7
  ngpt/cli/formatters.py,sha256=HBYGlx_7eoAKyzfy0Vq5L0yn8yVKjngqYBukMmXCcz0,9401
8
8
  ngpt/cli/main.py,sha256=oKX7ryTIrsvQRJHVnH2a763pGyNZthq81wkrRILwHLw,28932
9
9
  ngpt/cli/renderers.py,sha256=m71BeUXKynpKKGXFzwRSW1XngvyKiZ_xEsdujUbU0MA,16597
10
- ngpt/cli/ui.py,sha256=H52yH2BQiYUNxeax4DFx9K3q77MamATZLukGRU-r3Ec,9345
10
+ ngpt/cli/ui.py,sha256=tVJGTP1DWjCRq7ONFdOOKPHcVQz0MqiLyJtodKFabTk,9612
11
11
  ngpt/cli/modes/__init__.py,sha256=KP7VR6Xw9k1p5Jcu0F38RDxSFvFIzH3j1ThDLNwznUI,363
12
12
  ngpt/cli/modes/chat.py,sha256=jfKkrtSkx1gKPsKXDMxZ7BiJiMsCtFHyZCGIdmNQ0fk,7816
13
13
  ngpt/cli/modes/code.py,sha256=3avR9OM-D3r4HHfVm2bTfCOlsYQoqgtvU49zGzYfUqw,12513
14
14
  ngpt/cli/modes/gitcommsg.py,sha256=Alm1OLxXkuteiDSnDxjmnPvlSggGG2sTlUBAqJaYaN4,46739
15
15
  ngpt/cli/modes/interactive.py,sha256=TtBrZUX45CVfKOPvkb1ya7dIQhXLILtn7ajmfM9ohso,17419
16
16
  ngpt/cli/modes/rewrite.py,sha256=EKCPZwvu0MTDpD-nj_oty8vjVQpaF4ucwmTG99LJT6M,10736
17
- ngpt/cli/modes/shell.py,sha256=FaFOzxTxPJCpYsiQJIttS69fZg-asV4LVA5TLaGVN0Y,8897
17
+ ngpt/cli/modes/shell.py,sha256=kOfOi7uREd98FLHCcON3UuyeUcbQmk8ylOgsELoD810,14168
18
18
  ngpt/cli/modes/text.py,sha256=7t5WWXMFxGkBM5HMP4irbN9aQwxE2YgywjiVPep710k,6417
19
19
  ngpt/utils/__init__.py,sha256=qu_66I1Vtav2f1LDiPn5J3DUsbK7o1CSScMcTkYqxoM,1179
20
20
  ngpt/utils/cli_config.py,sha256=Ug8cECBTIuzOwkBWidLTfs-OAdOsCMJ2bNa70pOADfw,11195
21
21
  ngpt/utils/config.py,sha256=wsArA4osnh8fKqOvtsPqqBxAz3DpdjtaWUFaRtnUdyc,10452
22
22
  ngpt/utils/log.py,sha256=f1jg2iFo35PAmsarH8FVL_62plq4VXH0Mu2QiP6RJGw,15934
23
23
  ngpt/utils/web_search.py,sha256=w5ke4KJMRxq7r5jtbUXvspja6XhjoPZloVkZ0IvBXIE,30731
24
- ngpt-3.5.6.dist-info/METADATA,sha256=cyj7LzAMbu15UfjKmGtR7hnenYRfQgxBUAa7Eu_0t3s,23912
25
- ngpt-3.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- ngpt-3.5.6.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
27
- ngpt-3.5.6.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
28
- ngpt-3.5.6.dist-info/RECORD,,
24
+ ngpt-3.6.0.dist-info/METADATA,sha256=cUXBG4wlN36T0SsClQPpfT-SfeLYFdg2iy_n3Krrpx4,23912
25
+ ngpt-3.6.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ ngpt-3.6.0.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
27
+ ngpt-3.6.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
28
+ ngpt-3.6.0.dist-info/RECORD,,
File without changes