ngpt 3.6.0__py3-none-any.whl → 3.8.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/args.py CHANGED
@@ -69,6 +69,8 @@ def setup_argument_parser():
69
69
  help='Model to use')
70
70
  global_group.add_argument('--web-search', action='store_true',
71
71
  help='Enable web search capability using DuckDuckGo to enhance prompts with relevant information')
72
+ global_group.add_argument('--pipe', action='store_true',
73
+ help='Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content. Can be used with any mode option except --text and --interactive')
72
74
  global_group.add_argument('--temperature', type=float, default=0.7,
73
75
  help='Set temperature (controls randomness, default: 0.7)')
74
76
  global_group.add_argument('--top_p', type=float, default=1.0,
@@ -119,8 +121,6 @@ def setup_argument_parser():
119
121
  help='Generate code')
120
122
  mode_exclusive_group.add_argument('-t', '--text', action='store_true',
121
123
  help='Enter multi-line text input (submit with Ctrl+D)')
122
- mode_exclusive_group.add_argument('-p', '--pipe', action='store_true',
123
- help='Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content')
124
124
  mode_exclusive_group.add_argument('-r', '--rewrite', action='store_true',
125
125
  help='Rewrite text from stdin to be more natural while preserving tone and meaning')
126
126
  mode_exclusive_group.add_argument('-g', '--gitcommsg', action='store_true',
@@ -167,7 +167,11 @@ def validate_args(args):
167
167
  if args.stream_prettify and not has_markdown_renderer('rich'):
168
168
  raise ValueError("--stream-prettify requires Rich to be installed. Install with: pip install \"ngpt[full]\" or pip install rich")
169
169
 
170
- # If stdin mode is used, check if input is available
170
+ # Check for incompatible --pipe flag with certain modes
171
+ if args.pipe and (args.text or args.interactive):
172
+ raise ValueError("--pipe flag cannot be used with --text or --interactive modes. These modes already handle input directly.")
173
+
174
+ # If pipe flag is used, check if input is available
171
175
  if args.pipe and sys.stdin.isatty():
172
176
  raise ValueError("--pipe was specified but no input is piped. Use echo 'content' | ngpt --pipe 'prompt with {}'")
173
177
 
ngpt/cli/main.py CHANGED
@@ -550,13 +550,6 @@ def main():
550
550
  # Text mode (multiline input)
551
551
  text_mode(client, args, logger=logger)
552
552
 
553
- elif args.pipe:
554
- # Apply CLI config for pipe mode (similar to chat mode)
555
- args = apply_cli_config(args, "all")
556
-
557
- # Pipe mode (using the chat mode with stdin input)
558
- chat_mode(client, args, logger=logger)
559
-
560
553
  elif args.rewrite:
561
554
  # Apply CLI config for rewrite mode
562
555
  args = apply_cli_config(args, "all")
@@ -571,8 +564,8 @@ def main():
571
564
  # Git commit message generation mode
572
565
  gitcommsg_mode(client, args, logger=logger)
573
566
 
567
+ # Choose chat mode by default if no other specific mode is selected
574
568
  else:
575
- # Default to chat mode
576
569
  # Apply CLI config for default chat mode
577
570
  args = apply_cli_config(args, "all")
578
571
 
ngpt/cli/modes/chat.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from ..formatters import COLORS
2
2
  from ..renderers import prettify_markdown, prettify_streaming_markdown
3
3
  from ..ui import spinner
4
- from ...utils import enhance_prompt_with_web_search
4
+ from ...utils import enhance_prompt_with_web_search, process_piped_input
5
5
  import sys
6
6
  import threading
7
7
 
@@ -13,43 +13,20 @@ def chat_mode(client, args, logger=None):
13
13
  args: The parsed command-line arguments
14
14
  logger: Optional logger instance
15
15
  """
16
+ # Get the prompt
17
+ if args.prompt is None:
18
+ try:
19
+ print("Enter your prompt: ", end='')
20
+ prompt = input()
21
+ except KeyboardInterrupt:
22
+ print("\nInput cancelled by user. Exiting gracefully.")
23
+ sys.exit(130)
24
+ else:
25
+ prompt = args.prompt
26
+
16
27
  # Handle pipe mode
17
28
  if args.pipe:
18
- # Read input from stdin
19
- stdin_content = sys.stdin.read().strip()
20
-
21
- # Get the prompt - either use provided one or ask user
22
- if args.prompt is None:
23
- try:
24
- print("Enter your prompt (use {} as placeholder for stdin): ", end='')
25
- prompt = input()
26
- except KeyboardInterrupt:
27
- print("\nInput cancelled by user. Exiting gracefully.")
28
- sys.exit(130)
29
- else:
30
- prompt = args.prompt
31
-
32
- # Replace the placeholder in the prompt with stdin content
33
- placeholder = "{}"
34
-
35
- # Check if the placeholder exists in the prompt
36
- if placeholder not in prompt:
37
- print(f"{COLORS['yellow']}Warning: Placeholder '{placeholder}' not found in prompt. Appending stdin content to the end.{COLORS['reset']}")
38
- prompt = f"{prompt} {stdin_content}"
39
- else:
40
- prompt = prompt.replace(placeholder, stdin_content)
41
- # Handle regular chat mode
42
- else:
43
- # Get the prompt
44
- if args.prompt is None:
45
- try:
46
- print("Enter your prompt: ", end='')
47
- prompt = input()
48
- except KeyboardInterrupt:
49
- print("\nInput cancelled by user. Exiting gracefully.")
50
- sys.exit(130)
51
- else:
52
- prompt = args.prompt
29
+ prompt = process_piped_input(prompt, logger=logger)
53
30
 
54
31
  # Log the user message if logging is enabled
55
32
  if logger:
ngpt/cli/modes/code.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from ..formatters import COLORS
2
2
  from ..renderers import prettify_markdown, prettify_streaming_markdown, has_markdown_renderer, show_available_renderers
3
3
  from ..ui import spinner, copy_to_clipboard
4
- from ...utils import enhance_prompt_with_web_search
4
+ from ...utils import enhance_prompt_with_web_search, process_piped_input
5
5
  import sys
6
6
  import threading
7
7
 
@@ -97,6 +97,10 @@ def code_mode(client, args, logger=None):
97
97
  else:
98
98
  prompt = args.prompt
99
99
 
100
+ # Apply piped input if --pipe is enabled
101
+ if args.pipe:
102
+ prompt = process_piped_input(prompt, logger=logger)
103
+
100
104
  # Log the user prompt if logging is enabled
101
105
  if logger:
102
106
  logger.log("user", prompt)
@@ -11,6 +11,7 @@ from ..formatters import COLORS
11
11
  from ..ui import spinner, copy_to_clipboard
12
12
  from ...utils.log import create_gitcommsg_logger
13
13
  from ...utils.cli_config import get_cli_config_option
14
+ from ...utils import process_piped_input
14
15
 
15
16
  def get_diff_content(diff_file=None):
16
17
  """Get git diff content from file or git staged changes.
@@ -964,6 +965,39 @@ REMINDER:
964
965
 
965
966
  DO NOT ask for the original diff or add explanations outside the commit message format."""
966
967
 
968
+ def is_git_diff(content):
969
+ """Check if the content looks like a git diff.
970
+
971
+ Args:
972
+ content: The content to check
973
+
974
+ Returns:
975
+ bool: True if the content looks like a git diff, False otherwise
976
+ """
977
+ # Check for common git diff patterns
978
+ diff_patterns = [
979
+ r'diff --git a/.*? b/.*?', # diff --git a/file b/file
980
+ r'index [a-f0-9]+\.\.[a-f0-9]+', # index hash..hash
981
+ r'--- a/.*?', # --- a/file
982
+ r'\+\+\+ b/.*?', # +++ b/file
983
+ r'@@ -\d+,\d+ \+\d+,\d+ @@' # @@ -line,count +line,count @@
984
+ ]
985
+
986
+ # Check if the content contains at least one of these patterns
987
+ for pattern in diff_patterns:
988
+ if re.search(pattern, content):
989
+ return True
990
+
991
+ # Check if the content contains lines starting with + or - (changes)
992
+ lines = content.splitlines()
993
+ plus_minus_lines = [line for line in lines if line.startswith('+') or line.startswith('-')]
994
+
995
+ # If there are many +/- lines, it's likely a diff
996
+ if len(plus_minus_lines) > 5 and len(plus_minus_lines) / len(lines) > 0.1:
997
+ return True
998
+
999
+ return False
1000
+
967
1001
  def gitcommsg_mode(client, args, logger=None):
968
1002
  """Handle the Git commit message generation mode.
969
1003
 
@@ -987,16 +1021,47 @@ def gitcommsg_mode(client, args, logger=None):
987
1021
  active_logger.debug(f"Args: {args}")
988
1022
 
989
1023
  try:
1024
+ # Process piped input as diff content when --pipe flag is set
1025
+ piped_diff_content = None
1026
+ if args.pipe:
1027
+ if active_logger:
1028
+ active_logger.info("Processing piped input as diff content")
1029
+
1030
+ if not sys.stdin.isatty():
1031
+ piped_diff_content = sys.stdin.read().strip()
1032
+ if not piped_diff_content:
1033
+ print(f"{COLORS['yellow']}Warning: No diff content received from stdin.{COLORS['reset']}")
1034
+ else:
1035
+ # Validate that the piped content looks like a git diff
1036
+ if not is_git_diff(piped_diff_content):
1037
+ print(f"{COLORS['red']}Error: The piped content doesn't appear to be a git diff. Exiting.{COLORS['reset']}")
1038
+ if active_logger:
1039
+ active_logger.error("Piped content doesn't appear to be a git diff. Aborting.")
1040
+ return
1041
+ elif active_logger:
1042
+ active_logger.info(f"Received {len(piped_diff_content.splitlines())} lines of diff content from stdin")
1043
+ else:
1044
+ print(f"{COLORS['yellow']}Error: --pipe was specified but no input is piped.{COLORS['reset']}")
1045
+ return
1046
+
990
1047
  # Check if --diff was explicitly passed on the command line
991
1048
  diff_option_provided = '--diff' in sys.argv
992
1049
  diff_path_provided = diff_option_provided and args.diff is not None and args.diff is not True
993
1050
 
1051
+ # If piped diff content is available, use it directly
1052
+ if piped_diff_content:
1053
+ diff_content = piped_diff_content
1054
+ if active_logger:
1055
+ active_logger.info("Using diff content from stdin")
994
1056
  # If --diff wasn't explicitly provided on the command line, don't use the config value
995
- if not diff_option_provided:
1057
+ elif not diff_option_provided:
996
1058
  # Even if diff is in CLI config, don't use it unless --diff flag is provided
997
1059
  diff_file = None
998
1060
  if active_logger:
999
1061
  active_logger.info("Not using diff file from CLI config because --diff flag was not provided")
1062
+
1063
+ # Get diff content from git staged changes
1064
+ diff_content = get_diff_content(diff_file)
1000
1065
  else:
1001
1066
  # --diff flag was provided on command line
1002
1067
  if args.diff is True:
@@ -1013,9 +1078,9 @@ def gitcommsg_mode(client, args, logger=None):
1013
1078
  diff_file = args.diff
1014
1079
  if active_logger:
1015
1080
  active_logger.info(f"Using explicitly provided diff file: {diff_file}")
1016
-
1017
- # Get diff content
1018
- diff_content = get_diff_content(diff_file)
1081
+
1082
+ # Get diff content from file
1083
+ diff_content = get_diff_content(diff_file)
1019
1084
 
1020
1085
  if not diff_content:
1021
1086
  print(f"{COLORS['red']}No diff content available. Exiting.{COLORS['reset']}")
ngpt/cli/modes/rewrite.py CHANGED
@@ -4,7 +4,7 @@ import time
4
4
  from ..formatters import COLORS
5
5
  from ..renderers import prettify_markdown, prettify_streaming_markdown
6
6
  from ..ui import get_multiline_input, spinner, copy_to_clipboard
7
- from ...utils import enhance_prompt_with_web_search
7
+ from ...utils import enhance_prompt_with_web_search, process_piped_input
8
8
 
9
9
  # System prompt for rewriting text
10
10
  REWRITE_SYSTEM_PROMPT = """You are an expert text editor and rewriter. Your task is to rewrite the user's text to improve readability and flow while carefully preserving the original meaning, tone, and style.
@@ -78,14 +78,17 @@ def rewrite_mode(client, args, logger=None):
78
78
  args: The parsed command-line arguments
79
79
  logger: Optional logger instance
80
80
  """
81
- # Determine the input source (stdin pipe, command-line argument, or multiline input)
82
- if not sys.stdin.isatty():
81
+ # Check if using --pipe flag with a specific placeholder
82
+ if args.pipe and args.prompt:
83
+ input_text = process_piped_input(args.prompt, logger=logger)
84
+ # Normal rewrite mode functionality (direct stdin piping without --pipe flag)
85
+ elif not sys.stdin.isatty():
83
86
  # Read from stdin if data is piped
84
87
  input_text = sys.stdin.read().strip()
85
88
 
86
- # If stdin is empty but prompt is provided, use the prompt
87
- if not input_text and args.prompt:
88
- input_text = args.prompt
89
+ # If prompt is also provided, append it to the piped input
90
+ if args.prompt:
91
+ input_text = f"{input_text}\n\n{args.prompt}"
89
92
  elif args.prompt:
90
93
  # Use the command-line argument if provided
91
94
  input_text = args.prompt
ngpt/cli/modes/shell.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from ..formatters import COLORS
2
2
  from ..ui import spinner, copy_to_clipboard
3
- from ..renderers import prettify_markdown, has_markdown_renderer
4
- from ...utils import enhance_prompt_with_web_search
3
+ from ..renderers import prettify_markdown, has_markdown_renderer, prettify_streaming_markdown, show_available_renderers
4
+ from ...utils import enhance_prompt_with_web_search, process_piped_input
5
5
  import subprocess
6
6
  import sys
7
7
  import threading
@@ -9,6 +9,7 @@ import platform
9
9
  import os
10
10
  import shutil
11
11
  import re
12
+ import time
12
13
 
13
14
  # System prompt for shell command generation
14
15
  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.
@@ -131,6 +132,251 @@ def detect_shell():
131
132
  else:
132
133
  return "bash", "bash", operating_system
133
134
 
135
+ def setup_streaming(args, logger=None):
136
+ """Set up streaming configuration based on command-line arguments.
137
+
138
+ Args:
139
+ args: The parsed command-line arguments
140
+ logger: Optional logger instance for logging
141
+
142
+ Returns:
143
+ tuple: (should_stream, use_stream_prettify, use_regular_prettify,
144
+ stream_setup) - Configuration settings and streaming components
145
+ """
146
+ # Default values - initialize all at once
147
+ stream_callback = live_display = stop_spinner_func = None
148
+ stop_spinner = spinner_thread = stop_spinner_event = None
149
+ should_stream = True # Default to streaming
150
+ use_stream_prettify = use_regular_prettify = False
151
+ first_content_received = False
152
+
153
+ # Determine final behavior based on flag priority
154
+ if args.stream_prettify:
155
+ # Highest priority: stream-prettify
156
+ if has_markdown_renderer('rich'):
157
+ should_stream = True
158
+ use_stream_prettify = True
159
+ live_display, stream_callback, setup_spinner = prettify_streaming_markdown(args.renderer)
160
+ if not live_display:
161
+ # Fallback if live display fails
162
+ use_stream_prettify = False
163
+ use_regular_prettify = True
164
+ should_stream = False
165
+ print(f"{COLORS['yellow']}Live display setup failed. Falling back to regular prettify mode.{COLORS['reset']}")
166
+ else:
167
+ # Rich not available for stream-prettify
168
+ print(f"{COLORS['yellow']}Warning: Rich is not available for --stream-prettify. Install with: pip install \"ngpt[full]\".{COLORS['reset']}")
169
+ print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
170
+ should_stream = True
171
+ use_stream_prettify = False
172
+ elif args.no_stream:
173
+ # Second priority: no-stream
174
+ should_stream = False
175
+ use_regular_prettify = False # No prettify if no streaming
176
+ elif args.prettify:
177
+ # Third priority: prettify (requires disabling stream)
178
+ if has_markdown_renderer(args.renderer):
179
+ should_stream = False
180
+ use_regular_prettify = True
181
+ print(f"{COLORS['yellow']}Note: Using standard markdown rendering (--prettify). For streaming markdown rendering, use --stream-prettify instead.{COLORS['reset']}")
182
+ else:
183
+ # Renderer not available for prettify
184
+ print(f"{COLORS['yellow']}Warning: Renderer '{args.renderer}' not available for --prettify.{COLORS['reset']}")
185
+ show_available_renderers()
186
+ print(f"{COLORS['yellow']}Falling back to default streaming without prettify.{COLORS['reset']}")
187
+ should_stream = True
188
+ use_regular_prettify = False
189
+
190
+ # Create a wrapper for the stream callback that will stop the spinner on first content
191
+ if stream_callback:
192
+ original_callback = stream_callback
193
+
194
+ def spinner_handling_callback(content, **kwargs):
195
+ nonlocal first_content_received
196
+
197
+ # On first content, stop the spinner
198
+ if not first_content_received and stop_spinner_func:
199
+ first_content_received = True
200
+ # Stop the spinner
201
+ stop_spinner_func()
202
+ # Ensure spinner message is cleared with an extra blank line
203
+ sys.stdout.write("\r" + " " * 100 + "\r")
204
+ sys.stdout.flush()
205
+
206
+ # Call the original callback to update the display
207
+ if original_callback:
208
+ original_callback(content, **kwargs)
209
+
210
+ # Use our wrapper callback
211
+ if use_stream_prettify and live_display:
212
+ stream_callback = spinner_handling_callback
213
+
214
+ # Set up the spinner if we have a live display
215
+ stop_spinner_event = threading.Event()
216
+ stop_spinner_func = setup_spinner(stop_spinner_event, color=COLORS['cyan'])
217
+
218
+ # Create spinner for non-stream-prettify modes EXCEPT no-stream
219
+ if not use_stream_prettify and not args.no_stream:
220
+ # Prepare spinner (but don't start it yet - will be started in generate_with_model)
221
+ stop_spinner = threading.Event()
222
+ spinner_thread = threading.Thread(
223
+ target=spinner,
224
+ args=("Generating...",),
225
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
226
+ )
227
+ spinner_thread.daemon = True
228
+
229
+ # Create a stream_setup dict to hold all the variables - use a dict comprehension
230
+ stream_setup = {
231
+ 'stream_callback': stream_callback,
232
+ 'live_display': live_display,
233
+ 'stop_spinner_func': stop_spinner_func,
234
+ 'stop_spinner': stop_spinner,
235
+ 'spinner_thread': spinner_thread,
236
+ 'stop_spinner_event': stop_spinner_event,
237
+ 'first_content_received': first_content_received
238
+ }
239
+
240
+ return (should_stream, use_stream_prettify, use_regular_prettify, stream_setup)
241
+
242
+ def generate_with_model(client, prompt, messages, args, stream_setup,
243
+ use_stream_prettify, should_stream, spinner_message="Generating...",
244
+ temp_override=None, logger=None):
245
+ """Generate content using the model with proper streaming and spinner handling.
246
+
247
+ Args:
248
+ client: The NGPTClient instance
249
+ prompt: The prompt to send to the model
250
+ messages: The formatted messages to send
251
+ args: The parsed command-line arguments
252
+ stream_setup: The streaming setup from setup_streaming
253
+ use_stream_prettify: Whether to use stream prettify
254
+ should_stream: Whether to stream the response
255
+ spinner_message: Message to show in the spinner
256
+ temp_override: Optional temperature override
257
+ logger: Optional logger instance
258
+
259
+ Returns:
260
+ str: The generated content
261
+ """
262
+ # Extract variables from stream_setup - only unpack what we need
263
+ stream_callback = stream_setup['stream_callback']
264
+ stop_spinner = stream_setup['stop_spinner']
265
+ spinner_thread = stream_setup['spinner_thread']
266
+ stop_spinner_event = stream_setup['stop_spinner_event']
267
+ stop_spinner_func = stream_setup['stop_spinner_func']
268
+
269
+ # Show spinner for all modes except no-stream
270
+ if not args.no_stream:
271
+ # Two possible spinner types:
272
+ # 1. Rich spinner for stream_prettify
273
+ # 2. Regular spinner for all other modes (including --prettify)
274
+
275
+ if use_stream_prettify and stop_spinner_func:
276
+ # Rich spinner is handled by callbacks
277
+ pass
278
+ elif spinner_thread and stop_spinner:
279
+ # Start the regular spinner thread
280
+ spinner_thread._args = (spinner_message,)
281
+ if not spinner_thread.is_alive():
282
+ spinner_thread.start()
283
+ else:
284
+ # No-stream mode just gets a status message
285
+ print(spinner_message)
286
+
287
+ # Set temperature
288
+ temp = args.temperature if temp_override is None else temp_override
289
+
290
+ try:
291
+ # Make the API call
292
+ return client.chat(
293
+ prompt=prompt,
294
+ stream=should_stream,
295
+ messages=messages,
296
+ temperature=temp,
297
+ top_p=args.top_p,
298
+ max_tokens=args.max_tokens,
299
+ stream_callback=stream_callback
300
+ )
301
+ except KeyboardInterrupt:
302
+ print("\nRequest cancelled by user.")
303
+ return ""
304
+ except Exception as e:
305
+ print(f"Error generating content: {e}")
306
+ return ""
307
+ finally:
308
+ # Stop the spinner
309
+ if use_stream_prettify and stop_spinner_event:
310
+ # Stop rich spinner
311
+ if not stream_setup['first_content_received']:
312
+ stop_spinner_event.set()
313
+ elif stop_spinner:
314
+ # Stop regular spinner
315
+ stop_spinner.set()
316
+ if spinner_thread and spinner_thread.is_alive():
317
+ spinner_thread.join()
318
+
319
+ # Clear the spinner line completely
320
+ sys.stdout.write("\r" + " " * 100 + "\r")
321
+ sys.stdout.flush()
322
+
323
+ def display_content(content, content_type, highlight_lang, args, use_stream_prettify, use_regular_prettify):
324
+ """Display generated content with appropriate formatting.
325
+
326
+ Args:
327
+ content: The content to display
328
+ content_type: Type of content ('command' or 'description')
329
+ highlight_lang: Language for syntax highlighting
330
+ args: The parsed command-line arguments
331
+ use_stream_prettify: Whether stream prettify is enabled
332
+ use_regular_prettify: Whether regular prettify is enabled
333
+ """
334
+ if not content:
335
+ return
336
+
337
+ # Define title based on content type - use a lookup instead of if-else
338
+ titles = {
339
+ 'command': "Generated Command",
340
+ 'description': "Command Description"
341
+ }
342
+ title = titles.get(content_type, "Generated Content")
343
+
344
+ # Format content appropriately - create formatted content only when needed
345
+ if use_regular_prettify and has_markdown_renderer(args.renderer):
346
+ if content_type == 'command':
347
+ formatted_content = f"### {title}\n\n```{highlight_lang}\n{content}\n```"
348
+ else: # description
349
+ formatted_content = f"### {title}\n\n{content}"
350
+
351
+ # Only show formatted content if not already shown by stream-prettify
352
+ if not use_stream_prettify:
353
+ if use_regular_prettify and has_markdown_renderer(args.renderer):
354
+ # Use rich renderer for pretty output
355
+ prettify_markdown(formatted_content, args.renderer)
356
+ elif args.no_stream:
357
+ # Simple output for no-stream mode (no box)
358
+ if content_type == 'command':
359
+ print(f"\n{title}:\n{COLORS['green']}{content}{COLORS['reset']}\n")
360
+ else:
361
+ print(f"\n{title}:\n{content}\n")
362
+ else:
363
+ # Regular display or fallback
364
+ if content_type == 'command':
365
+ # Box formatting for commands in regular mode - calculate once
366
+ term_width = shutil.get_terminal_size().columns
367
+ box_width = min(term_width - 4, len(content) + 8)
368
+ horizontal_line = "─" * box_width
369
+ spacing = box_width - len(title) - 11
370
+ content_spacing = box_width - len(content) - 2
371
+
372
+ print(f"\n┌{horizontal_line}┐")
373
+ print(f"│ {COLORS['bold']}{title}:{COLORS['reset']} {' ' * spacing}│")
374
+ print(f"│ {COLORS['green']}{content}{COLORS['reset']}{' ' * content_spacing}│")
375
+ print(f"└{horizontal_line}┘\n")
376
+ else:
377
+ # Simple display for descriptions
378
+ print(f"\n{content}\n")
379
+
134
380
  def shell_mode(client, args, logger=None):
135
381
  """Handle the shell command generation mode.
136
382
 
@@ -139,6 +385,7 @@ def shell_mode(client, args, logger=None):
139
385
  args: The parsed command-line arguments
140
386
  logger: Optional logger instance
141
387
  """
388
+ # Get the user prompt more efficiently
142
389
  if args.prompt is None:
143
390
  try:
144
391
  print("Enter shell command description: ", end='')
@@ -149,15 +396,20 @@ def shell_mode(client, args, logger=None):
149
396
  else:
150
397
  prompt = args.prompt
151
398
 
399
+ # Process piped input if --pipe flag is set
400
+ if args.pipe:
401
+ prompt = process_piped_input(prompt, logger=logger)
402
+
152
403
  # Log the user prompt if logging is enabled
153
404
  if logger:
154
405
  logger.log("user", prompt)
155
406
 
156
- # Enhance prompt with web search if enabled
407
+ # Enhance prompt with web search if enabled - reuse variables
157
408
  if args.web_search:
409
+ original_prompt = prompt
410
+ web_search_succeeded = False
411
+
158
412
  try:
159
- original_prompt = prompt
160
-
161
413
  # Start spinner for web search
162
414
  stop_spinner = threading.Event()
163
415
  spinner_thread = threading.Thread(
@@ -170,23 +422,23 @@ def shell_mode(client, args, logger=None):
170
422
 
171
423
  try:
172
424
  prompt = enhance_prompt_with_web_search(prompt, logger=logger, disable_citations=True)
173
- # Stop the spinner
425
+ web_search_succeeded = True
426
+ finally:
427
+ # Always stop the spinner
174
428
  stop_spinner.set()
175
429
  spinner_thread.join()
430
+
176
431
  # Clear the spinner line completely
177
432
  sys.stdout.write("\r" + " " * 100 + "\r")
178
433
  sys.stdout.flush()
179
- print("Enhanced input with web search results.")
180
- except Exception as e:
181
- # Stop the spinner before re-raising
182
- stop_spinner.set()
183
- spinner_thread.join()
184
- raise e
185
434
 
186
- # Log the enhanced prompt if logging is enabled
187
- if logger:
188
- # Use "web_search" role instead of "system" for clearer logs
189
- logger.log("web_search", prompt.replace(original_prompt, "").strip())
435
+ if web_search_succeeded:
436
+ print("Enhanced input with web search results.")
437
+
438
+ # Log the enhanced prompt if logging is enabled
439
+ if logger:
440
+ # Use "web_search" role instead of "system" for clearer logs
441
+ logger.log("web_search", prompt.replace(original_prompt, "").strip())
190
442
  except Exception as e:
191
443
  print(f"{COLORS['yellow']}Warning: Failed to enhance prompt with web search: {str(e)}{COLORS['reset']}")
192
444
  # Continue with the original prompt if web search fails
@@ -224,36 +476,21 @@ def shell_mode(client, args, logger=None):
224
476
  if logger:
225
477
  logger.log("system", system_prompt)
226
478
 
227
- # Start spinner while waiting for command generation
228
- stop_spinner = threading.Event()
229
- spinner_thread = threading.Thread(
230
- target=spinner,
231
- args=("Generating command...",),
232
- kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
233
- )
234
- spinner_thread.daemon = True
235
- spinner_thread.start()
479
+ # Set up streaming once and reuse for both command and description
480
+ should_stream, use_stream_prettify, use_regular_prettify, stream_setup = setup_streaming(args)
236
481
 
237
- try:
238
- command = client.chat(
239
- prompt=prompt,
240
- stream=False,
241
- messages=messages,
242
- temperature=args.temperature,
243
- top_p=args.top_p,
244
- max_tokens=args.max_tokens
245
- )
246
- except Exception as e:
247
- print(f"Error generating shell command: {e}")
248
- command = ""
249
- finally:
250
- # Stop the spinner
251
- stop_spinner.set()
252
- spinner_thread.join()
253
-
254
- # Clear the spinner line completely
255
- sys.stdout.write("\r" + " " * 100 + "\r")
256
- sys.stdout.flush()
482
+ # Generate the command
483
+ command = generate_with_model(
484
+ client=client,
485
+ prompt=prompt,
486
+ messages=messages,
487
+ args=args,
488
+ stream_setup=stream_setup,
489
+ use_stream_prettify=use_stream_prettify,
490
+ should_stream=should_stream,
491
+ spinner_message="Generating command...",
492
+ logger=logger
493
+ )
257
494
 
258
495
  if not command:
259
496
  return # Error already printed by client
@@ -265,30 +502,45 @@ def shell_mode(client, args, logger=None):
265
502
  # Get the most up-to-date shell type at command generation time
266
503
  _, highlight_lang, _ = detect_shell()
267
504
 
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```"
505
+ # Format with proper syntax highlighting for streaming prettify - only if needed
506
+ if use_stream_prettify and stream_setup['stream_callback'] and command:
507
+ # Create properly formatted markdown for streaming display
508
+ formatted_command = f"```{highlight_lang}\n{command}\n```"
509
+ # Update the live display with the formatted command
510
+ stream_setup['stream_callback'](formatted_command, complete=True)
270
511
 
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
280
-
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='')
512
+ # Display the command
513
+ display_content(
514
+ content=command,
515
+ content_type='command',
516
+ highlight_lang=highlight_lang,
517
+ args=args,
518
+ use_stream_prettify=use_stream_prettify,
519
+ use_regular_prettify=use_regular_prettify
520
+ )
521
+
522
+ # Display options with better formatting - prepare strings once
523
+ options_text = f"{COLORS['bold']}Options:{COLORS['reset']}"
524
+ options = [
525
+ f" {COLORS['cyan']}C{COLORS['reset']} - Copy - Copy the command to clipboard",
526
+ f" {COLORS['cyan']}E{COLORS['reset']} - Execute - Run the command in your shell",
527
+ f" {COLORS['cyan']}D{COLORS['reset']} - Describe - Explain what this command does",
528
+ f" {COLORS['cyan']}A{COLORS['reset']} - Abort - Cancel and return to prompt"
529
+ ]
530
+ prompt_text = f"\nWhat would you like to do? [{COLORS['cyan']}C{COLORS['reset']}/{COLORS['cyan']}E{COLORS['reset']}/{COLORS['cyan']}D{COLORS['reset']}/{COLORS['cyan']}A{COLORS['reset']}] "
531
+
532
+ # Print options with proper flushing to ensure display
533
+ print(options_text, flush=True)
534
+ for option in options:
535
+ print(option, flush=True)
536
+
537
+ # Add a small delay to ensure terminal rendering is complete,
538
+ # especially important for stream-prettify mode
539
+ if use_stream_prettify:
540
+ time.sleep(0.2)
541
+
542
+ # Print prompt and flush to ensure it appears
543
+ print(prompt_text, end='', flush=True)
292
544
 
293
545
  try:
294
546
  response = input().lower()
@@ -296,7 +548,7 @@ def shell_mode(client, args, logger=None):
296
548
  print("\nCommand execution cancelled by user.")
297
549
  return
298
550
 
299
- if response == 'e' or response == 'execute':
551
+ if response == 'e':
300
552
  # Log the execution if logging is enabled
301
553
  if logger:
302
554
  logger.log("system", f"Executing command: {command}")
@@ -326,7 +578,7 @@ def shell_mode(client, args, logger=None):
326
578
  logger.log("system", f"Command error: {error}")
327
579
 
328
580
  print(f"\nError:\n{error}")
329
- elif response == 'c' or response == 'copy':
581
+ elif response == 'c':
330
582
  # Copy command to clipboard without confirmation prompt
331
583
  copied = copy_to_clipboard(command, skip_confirmation=True)
332
584
  if not copied:
@@ -334,4 +586,93 @@ def shell_mode(client, args, logger=None):
334
586
 
335
587
  # Log the copy if logging is enabled
336
588
  if logger:
337
- logger.log("system", "Command copied to clipboard")
589
+ logger.log("system", "Command copied to clipboard")
590
+ elif response == 'd':
591
+ # Ask LLM to describe what the command does
592
+ describe_prompt = f"Please explain this command: {command}"
593
+
594
+ # Create system prompt for description that includes OS and shell info
595
+ describe_system_prompt = f"You are a helpful assistant explaining shell commands. The user is running {shell_name} on {operating_system}. Explain what the following shell command does in detail, considering this specific environment. Include any potential risks, side effects, or compatibility issues with this OS/shell combination."
596
+
597
+ # Prepare messages for the chat API
598
+ describe_messages = [
599
+ {"role": "system", "content": describe_system_prompt},
600
+ {"role": "user", "content": describe_prompt}
601
+ ]
602
+
603
+ # Log the system prompt if logging is enabled
604
+ if logger:
605
+ logger.log("system", f"Command description requested for {operating_system}/{shell_name}")
606
+
607
+ # Set up fresh streaming for description - reuse existing setup when possible
608
+ # We only need to refresh the streaming setup if we're using stream_prettify
609
+ if use_stream_prettify:
610
+ _, use_stream_prettify_desc, use_regular_prettify_desc, stream_setup_desc = setup_streaming(args)
611
+ else:
612
+ # Reuse the existing setup for non-prettify streaming
613
+ use_stream_prettify_desc = use_stream_prettify
614
+ use_regular_prettify_desc = use_regular_prettify
615
+
616
+ # Always create a fresh spinner for description
617
+ stop_spinner = threading.Event()
618
+ spinner_thread = threading.Thread(
619
+ target=spinner,
620
+ args=("Generating command description...",),
621
+ kwargs={"stop_event": stop_spinner, "color": COLORS['cyan']}
622
+ )
623
+ spinner_thread.daemon = True
624
+
625
+ # Create a new stream setup with the fresh spinner
626
+ stream_setup_desc = {
627
+ 'stream_callback': stream_setup.get('stream_callback'),
628
+ 'live_display': stream_setup.get('live_display'),
629
+ 'stop_spinner_func': stream_setup.get('stop_spinner_func'),
630
+ 'stop_spinner': stop_spinner,
631
+ 'spinner_thread': spinner_thread,
632
+ 'stop_spinner_event': stream_setup.get('stop_spinner_event'),
633
+ 'first_content_received': False
634
+ }
635
+
636
+ # Generate the description
637
+ description = generate_with_model(
638
+ client=client,
639
+ prompt=describe_prompt,
640
+ messages=describe_messages,
641
+ args=args,
642
+ stream_setup=stream_setup_desc,
643
+ use_stream_prettify=use_stream_prettify_desc,
644
+ should_stream=should_stream,
645
+ spinner_message="Generating command description...",
646
+ temp_override=0.3,
647
+ logger=logger
648
+ )
649
+
650
+ if not description:
651
+ return # Error already printed
652
+
653
+ # Log the generated description if logging is enabled
654
+ if logger:
655
+ logger.log("assistant", description)
656
+
657
+ # Format with proper markdown for streaming prettify - only if needed
658
+ if use_stream_prettify_desc and stream_setup_desc['stream_callback'] and description:
659
+ # Format description as markdown for prettier display
660
+ md_description = f"### Command Description\n\n{description}"
661
+ # Update the live display with the formatted description
662
+ stream_setup_desc['stream_callback'](md_description, complete=True)
663
+
664
+ # Display the description
665
+ display_content(
666
+ content=description,
667
+ content_type='description',
668
+ highlight_lang=highlight_lang,
669
+ args=args,
670
+ use_stream_prettify=use_stream_prettify_desc,
671
+ use_regular_prettify=use_regular_prettify_desc
672
+ )
673
+ elif response == 'a' or response == '':
674
+ print("\nCommand aborted.")
675
+
676
+ # Log the abort if logging is enabled
677
+ if logger:
678
+ logger.log("system", "Command aborted by user")
ngpt/utils/__init__.py CHANGED
@@ -27,6 +27,7 @@ from .web_search import (
27
27
  get_web_search_results,
28
28
  format_web_search_results_for_prompt
29
29
  )
30
+ from .pipe import process_piped_input
30
31
 
31
32
  __all__ = [
32
33
  "create_logger", "Logger",
@@ -35,5 +36,6 @@ __all__ = [
35
36
  "load_cli_config", "set_cli_config_option", "get_cli_config_option",
36
37
  "unset_cli_config_option", "apply_cli_config", "list_cli_config_options",
37
38
  "CLI_CONFIG_OPTIONS", "get_cli_config_dir", "get_cli_config_path",
38
- "enhance_prompt_with_web_search", "get_web_search_results", "format_web_search_results_for_prompt"
39
+ "enhance_prompt_with_web_search", "get_web_search_results", "format_web_search_results_for_prompt",
40
+ "process_piped_input"
39
41
  ]
ngpt/utils/pipe.py ADDED
@@ -0,0 +1,52 @@
1
+ import sys
2
+
3
+ def process_piped_input(prompt, logger=None):
4
+ """Process piped input to be used with a prompt.
5
+
6
+ Args:
7
+ prompt: The prompt string which may contain a {} placeholder
8
+ logger: Optional logger instance
9
+
10
+ Returns:
11
+ str: The processed prompt with piped input inserted at placeholder, or appended
12
+ """
13
+ # Import COLORS here to avoid circular imports
14
+ from ..cli.formatters import COLORS
15
+
16
+ # Only process if stdin is not a TTY (i.e., if we're receiving piped input)
17
+ if not sys.stdin.isatty():
18
+ # Read input from stdin
19
+ stdin_content = sys.stdin.read().strip()
20
+
21
+ # If we have stdin content and prompt is provided
22
+ if stdin_content and prompt:
23
+ placeholder = "{}"
24
+
25
+ # Check if the placeholder exists in the prompt
26
+ if placeholder not in prompt:
27
+ print(f"{COLORS['yellow']}Warning: Placeholder '{placeholder}' not found in prompt. Appending stdin content to the end.{COLORS['reset']}")
28
+ processed_prompt = f"{prompt}\n\n{stdin_content}"
29
+ else:
30
+ # Replace the placeholder in the prompt with stdin content
31
+ processed_prompt = prompt.replace(placeholder, stdin_content)
32
+
33
+ # Log if a logger is provided
34
+ if logger:
35
+ logger.log("info", f"Processed piped input: Combined prompt with stdin content")
36
+
37
+ return processed_prompt
38
+
39
+ # If we have stdin content but no prompt, just use the stdin content
40
+ elif stdin_content:
41
+ return stdin_content
42
+ elif prompt:
43
+ # We have prompt but stdin is empty
44
+ print(f"{COLORS['yellow']}Warning: No stdin content received. Using only the provided prompt.{COLORS['reset']}")
45
+ return prompt
46
+ else:
47
+ # No stdin content and no prompt
48
+ print(f"{COLORS['yellow']}Error: No stdin content and no prompt provided.{COLORS['reset']}")
49
+ return ""
50
+
51
+ # If no stdin or no content in stdin, just return the original prompt
52
+ return prompt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.6.0
3
+ Version: 3.8.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
@@ -150,7 +150,19 @@ ngpt --code --stream-prettify "function to calculate the Fibonacci sequence"
150
150
  ngpt --shell "list all files in the current directory"
151
151
 
152
152
  # Read from stdin and use the content in your prompt
153
- echo "What is this text about?" | ngpt -p "Analyze the following text: {}"
153
+ echo "What is this text about?" | ngpt --pipe "Analyze the following text: {}"
154
+
155
+ # Using here-string (<<<) for quick single-line input
156
+ ngpt --pipe {} <<< "What is the best way to learn shell redirects?"
157
+
158
+ # Using standard input redirection to process file contents
159
+ ngpt --pipe "summarise {}" < README.md
160
+
161
+ # Using here-document (<<EOF) for multiline input
162
+ ngpt --pipe {} << EOF
163
+ What is the best way to learn Golang?
164
+ Provide simple hello world example.
165
+ EOF
154
166
 
155
167
  # Rewrite text to improve quality while preserving tone and meaning
156
168
  echo "your text" | ngpt -r
@@ -173,6 +185,9 @@ ngpt -g --rec-chunk
173
185
  # Process a diff file instead of staged changes
174
186
  ngpt -g --diff /path/to/changes.diff
175
187
 
188
+ # Use piped diff content for commit message generation
189
+ git diff HEAD~1 | ngpt -g --pipe
190
+
176
191
  # Generate a commit message with logging for debugging
177
192
  ngpt -g --log commit_log.txt
178
193
 
@@ -201,7 +216,7 @@ ngpt --interactive --log conversation.log
201
216
  ngpt --log "Tell me about quantum computing"
202
217
 
203
218
  # Process text from stdin using the {} placeholder
204
- cat README.md | ngpt -p "Summarize this document: {}"
219
+ cat README.md | ngpt --pipe "Summarize this document: {}"
205
220
 
206
221
  # Use different model providers by specifying the provider name
207
222
  ngpt --provider Groq "Explain quantum computing"
@@ -249,13 +264,14 @@ For more examples and detailed usage, visit the [CLI Usage Guide](https://nazdri
249
264
 
250
265
  ```console
251
266
  ❯ ngpt -h
267
+
252
268
  usage: ngpt [-h] [-v] [--language LANGUAGE] [--config [CONFIG]] [--config-index CONFIG_INDEX] [--provider PROVIDER]
253
269
  [--remove] [--show-config] [--all] [--list-models] [--list-renderers] [--cli-config [COMMAND ...]]
254
- [--api-key API_KEY] [--base-url BASE_URL] [--model MODEL] [--web-search] [--temperature TEMPERATURE]
255
- [--top_p TOP_P] [--max_tokens MAX_TOKENS] [--log [FILE]] [--preprompt PREPROMPT] [--no-stream | --prettify |
256
- --stream-prettify] [--renderer {auto,rich,glow}] [--rec-chunk] [--diff [FILE]] [--chunk-size CHUNK_SIZE]
257
- [--analyses-chunk-size ANALYSES_CHUNK_SIZE] [--max-msg-lines MAX_MSG_LINES]
258
- [--max-recursion-depth MAX_RECURSION_DEPTH] [-i | -s | -c | -t | -p | -r | -g]
270
+ [--api-key API_KEY] [--base-url BASE_URL] [--model MODEL] [--web-search] [--pipe]
271
+ [--temperature TEMPERATURE] [--top_p TOP_P] [--max_tokens MAX_TOKENS] [--log [FILE]] [--preprompt PREPROMPT]
272
+ [--no-stream | --prettify | --stream-prettify] [--renderer {auto,rich,glow}] [--rec-chunk] [--diff [FILE]]
273
+ [--chunk-size CHUNK_SIZE] [--analyses-chunk-size ANALYSES_CHUNK_SIZE] [--max-msg-lines MAX_MSG_LINES]
274
+ [--max-recursion-depth MAX_RECURSION_DEPTH] [-i | -s | -c | -t | -r | -g]
259
275
  [prompt]
260
276
 
261
277
  nGPT - Interact with AI language models via OpenAI-compatible APIs
@@ -288,6 +304,7 @@ Global Options::
288
304
  --base-url BASE_URL Base URL for the API
289
305
  --model MODEL Model to use
290
306
  --web-search Enable web search capability using DuckDuckGo to enhance prompts with relevant information
307
+ --pipe Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content. Can be used with any mode option except --text and --interactive
291
308
  --temperature TEMPERATURE Set temperature (controls randomness, default: 0.7)
292
309
  --top_p TOP_P Set top_p (controls diversity, default: 1.0)
293
310
  --max_tokens MAX_TOKENS Set max response length in tokens
@@ -308,7 +325,7 @@ Git Commit Message Options::
308
325
  --chunk-size CHUNK_SIZE Number of lines per chunk when chunking is enabled (default: 200)
309
326
  --analyses-chunk-size ANALYSES_CHUNK_SIZE Number of lines per chunk when recursively chunking analyses (default: 200)
310
327
  --max-msg-lines MAX_MSG_LINES Maximum number of lines in commit message before condensing (default: 20)
311
- --max-recursion-depth MAX_RECURSION_DEPTH Maximum recursion depth for commit message condensing (default: 3)
328
+ --max-recursion-depth MAX_RECURSION_DEPTH Maximum recursion depth for commit message condensing (default: 3)
312
329
 
313
330
  Modes (mutually exclusive)::
314
331
 
@@ -316,9 +333,9 @@ Modes (mutually exclusive)::
316
333
  -s, --shell Generate and execute shell commands
317
334
  -c, --code Generate code
318
335
  -t, --text Enter multi-line text input (submit with Ctrl+D)
319
- -p, --pipe Read from stdin and use content with prompt. Use {} in prompt as placeholder for stdin content
320
336
  -r, --rewrite Rewrite text from stdin to be more natural while preserving tone and meaning
321
337
  -g, --gitcommsg Generate AI-powered git commit messages from staged changes or diff file
338
+
322
339
  ```
323
340
 
324
341
  > **Note**: For better visualization of conventional commit messages on GitHub, you can use the [GitHub Commit Labels](https://greasyfork.org/en/scripts/526153-github-commit-labels) userscript, which adds colorful labels to your commits.
@@ -2,27 +2,28 @@ ngpt/__init__.py,sha256=kpKhViLakwMdHZkuLht2vWcjt0uD_5gR33gvMhfXr6w,664
2
2
  ngpt/__main__.py,sha256=j3eFYPOtCCFBOGh7NK5IWEnADnTMMSEB9GLyIDoW724,66
3
3
  ngpt/client.py,sha256=XjpA2UnvrRvzk6_DzVEddUTzoPlF8koQ-cZURpHoT7c,9041
4
4
  ngpt/cli/__init__.py,sha256=hebbDSMGiOd43YNnQP67uzr67Ue6rZPwm2czynr5iZY,43
5
- ngpt/cli/args.py,sha256=Gjpu6GLgdb0nsV9e54mgrA1wTg9PDR8NwkBA-c0BBKw,12406
5
+ ngpt/cli/args.py,sha256=HYCDHhqP-BI_tibL1qGQ9we4483h_kCa2ksh-QxOeiU,12694
6
6
  ngpt/cli/config_manager.py,sha256=NQQcWnjUppAAd0s0p9YAf8EyKS1ex5-0EB4DvKdB4dk,3662
7
7
  ngpt/cli/formatters.py,sha256=HBYGlx_7eoAKyzfy0Vq5L0yn8yVKjngqYBukMmXCcz0,9401
8
- ngpt/cli/main.py,sha256=oKX7ryTIrsvQRJHVnH2a763pGyNZthq81wkrRILwHLw,28932
8
+ ngpt/cli/main.py,sha256=PVulo8Pm53-oQ2Pgc4G90YhwyPImt8j7HKmY38SJ7CM,28696
9
9
  ngpt/cli/renderers.py,sha256=m71BeUXKynpKKGXFzwRSW1XngvyKiZ_xEsdujUbU0MA,16597
10
10
  ngpt/cli/ui.py,sha256=tVJGTP1DWjCRq7ONFdOOKPHcVQz0MqiLyJtodKFabTk,9612
11
11
  ngpt/cli/modes/__init__.py,sha256=KP7VR6Xw9k1p5Jcu0F38RDxSFvFIzH3j1ThDLNwznUI,363
12
- ngpt/cli/modes/chat.py,sha256=jfKkrtSkx1gKPsKXDMxZ7BiJiMsCtFHyZCGIdmNQ0fk,7816
13
- ngpt/cli/modes/code.py,sha256=3avR9OM-D3r4HHfVm2bTfCOlsYQoqgtvU49zGzYfUqw,12513
14
- ngpt/cli/modes/gitcommsg.py,sha256=Alm1OLxXkuteiDSnDxjmnPvlSggGG2sTlUBAqJaYaN4,46739
12
+ ngpt/cli/modes/chat.py,sha256=1mH3nTDwU52qQJRliNUAFSqJWyyiJ6j0T5uVso-VnxE,6828
13
+ ngpt/cli/modes/code.py,sha256=QBFgMRPKJhxkYCmumoSkNSF15XNRGUDum5yuK8aBnyM,12662
14
+ ngpt/cli/modes/gitcommsg.py,sha256=iTg3KlZwI0lGMcmUa62b0ashwLcxegdEEvT29PPtpBc,49595
15
15
  ngpt/cli/modes/interactive.py,sha256=TtBrZUX45CVfKOPvkb1ya7dIQhXLILtn7ajmfM9ohso,17419
16
- ngpt/cli/modes/rewrite.py,sha256=EKCPZwvu0MTDpD-nj_oty8vjVQpaF4ucwmTG99LJT6M,10736
17
- ngpt/cli/modes/shell.py,sha256=kOfOi7uREd98FLHCcON3UuyeUcbQmk8ylOgsELoD810,14168
16
+ ngpt/cli/modes/rewrite.py,sha256=yPmJPPkMHNxrnV-eoM0j6lMNRhdSAMXmcw2s9xG6TIo,10918
17
+ ngpt/cli/modes/shell.py,sha256=Bi5MDteUCOLc-KTecJtyUZKaY9Qab94xpN7qYz1_dOA,29487
18
18
  ngpt/cli/modes/text.py,sha256=7t5WWXMFxGkBM5HMP4irbN9aQwxE2YgywjiVPep710k,6417
19
- ngpt/utils/__init__.py,sha256=qu_66I1Vtav2f1LDiPn5J3DUsbK7o1CSScMcTkYqxoM,1179
19
+ ngpt/utils/__init__.py,sha256=_92f8eGMMOtQQA3uwgSRVwUEl1EIRFjWPUjcfGgI-eI,1244
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
+ ngpt/utils/pipe.py,sha256=qRHF-Ma7bbU0cOcb1Yhe4S-kBavivtnnvLA3EYS4FY4,2162
23
24
  ngpt/utils/web_search.py,sha256=w5ke4KJMRxq7r5jtbUXvspja6XhjoPZloVkZ0IvBXIE,30731
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,,
25
+ ngpt-3.8.0.dist-info/METADATA,sha256=O_lTxBgnFYmKUftGTShWc7MFdHzjXRPURp2QJVhtbmo,24503
26
+ ngpt-3.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ ngpt-3.8.0.dist-info/entry_points.txt,sha256=SqAAvLhMrsEpkIr4YFRdUeyuXQ9o0IBCeYgE6AVojoI,44
28
+ ngpt-3.8.0.dist-info/licenses/LICENSE,sha256=mQkpWoADxbHqE0HRefYLJdm7OpdrXBr3vNv5bZ8w72M,1065
29
+ ngpt-3.8.0.dist-info/RECORD,,
File without changes