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 +138 -28
- ngpt/cli/ui.py +16 -11
- {ngpt-3.5.6.dist-info → ngpt-3.6.0.dist-info}/METADATA +1 -1
- {ngpt-3.5.6.dist-info → ngpt-3.6.0.dist-info}/RECORD +7 -7
- {ngpt-3.5.6.dist-info → ngpt-3.6.0.dist-info}/WHEEL +0 -0
- {ngpt-3.5.6.dist-info → ngpt-3.6.0.dist-info}/entry_points.txt +0 -0
- {ngpt-3.5.6.dist-info → ngpt-3.6.0.dist-info}/licenses/LICENSE +0 -0
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
|
-
#
|
98
|
-
|
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
|
-
|
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 == '
|
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
|
-
#
|
193
|
-
if
|
194
|
-
|
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
|
-
|
197
|
-
|
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.
|
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=
|
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=
|
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.
|
25
|
-
ngpt-3.
|
26
|
-
ngpt-3.
|
27
|
-
ngpt-3.
|
28
|
-
ngpt-3.
|
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
|
File without changes
|
File without changes
|