ngpt 3.8.1__tar.gz → 3.8.3__tar.gz

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.
Files changed (59) hide show
  1. {ngpt-3.8.1 → ngpt-3.8.3}/PKG-INFO +143 -1
  2. {ngpt-3.8.1 → ngpt-3.8.3}/README.md +142 -0
  3. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/chat.py +18 -13
  4. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/code.py +20 -15
  5. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/interactive.py +53 -46
  6. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/rewrite.py +18 -13
  7. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/shell.py +346 -63
  8. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/text.py +18 -13
  9. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/renderers.py +56 -50
  10. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/ui.py +33 -4
  11. {ngpt-3.8.1 → ngpt-3.8.3}/pyproject.toml +1 -1
  12. {ngpt-3.8.1 → ngpt-3.8.3}/uv.lock +1 -1
  13. {ngpt-3.8.1 → ngpt-3.8.3}/.github/workflows/aur-publish.yml +0 -0
  14. {ngpt-3.8.1 → ngpt-3.8.3}/.github/workflows/python-publish.yml +0 -0
  15. {ngpt-3.8.1 → ngpt-3.8.3}/.gitignore +0 -0
  16. {ngpt-3.8.1 → ngpt-3.8.3}/.python-version +0 -0
  17. {ngpt-3.8.1 → ngpt-3.8.3}/COMMIT_GUIDELINES.md +0 -0
  18. {ngpt-3.8.1 → ngpt-3.8.3}/CONTRIBUTING.md +0 -0
  19. {ngpt-3.8.1 → ngpt-3.8.3}/LICENSE +0 -0
  20. {ngpt-3.8.1 → ngpt-3.8.3}/PKGBUILD +0 -0
  21. {ngpt-3.8.1 → ngpt-3.8.3}/docs/CONTRIBUTING.md +0 -0
  22. {ngpt-3.8.1 → ngpt-3.8.3}/docs/LICENSE.md +0 -0
  23. {ngpt-3.8.1 → ngpt-3.8.3}/docs/_config.yml +0 -0
  24. {ngpt-3.8.1 → ngpt-3.8.3}/docs/_sass/custom/custom.scss +0 -0
  25. {ngpt-3.8.1 → ngpt-3.8.3}/docs/configuration.md +0 -0
  26. {ngpt-3.8.1 → ngpt-3.8.3}/docs/examples/advanced.md +0 -0
  27. {ngpt-3.8.1 → ngpt-3.8.3}/docs/examples/basic.md +0 -0
  28. {ngpt-3.8.1 → ngpt-3.8.3}/docs/examples.md +0 -0
  29. {ngpt-3.8.1 → ngpt-3.8.3}/docs/index.md +0 -0
  30. {ngpt-3.8.1 → ngpt-3.8.3}/docs/installation.md +0 -0
  31. {ngpt-3.8.1 → ngpt-3.8.3}/docs/overview.md +0 -0
  32. {ngpt-3.8.1 → ngpt-3.8.3}/docs/usage/cli_config.md +0 -0
  33. {ngpt-3.8.1 → ngpt-3.8.3}/docs/usage/cli_usage.md +0 -0
  34. {ngpt-3.8.1 → ngpt-3.8.3}/docs/usage/gitcommsg.md +0 -0
  35. {ngpt-3.8.1 → ngpt-3.8.3}/docs/usage/web_search.md +0 -0
  36. {ngpt-3.8.1 → ngpt-3.8.3}/docs/usage.md +0 -0
  37. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/__init__.py +0 -0
  38. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/__main__.py +0 -0
  39. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/__init__.py +0 -0
  40. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/args.py +0 -0
  41. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/config_manager.py +0 -0
  42. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/formatters.py +0 -0
  43. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/main.py +0 -0
  44. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/__init__.py +0 -0
  45. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/cli/modes/gitcommsg.py +0 -0
  46. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/client.py +0 -0
  47. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/__init__.py +0 -0
  48. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/cli_config.py +0 -0
  49. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/config.py +0 -0
  50. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/log.py +0 -0
  51. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/pipe.py +0 -0
  52. {ngpt-3.8.1 → ngpt-3.8.3}/ngpt/utils/web_search.py +0 -0
  53. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-g.png +0 -0
  54. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-i.png +0 -0
  55. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-s-c.png +0 -0
  56. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-sh-c-a.png +0 -0
  57. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-w-self.png +0 -0
  58. {ngpt-3.8.1 → ngpt-3.8.3}/previews/ngpt-w.png +0 -0
  59. {ngpt-3.8.1 → ngpt-3.8.3}/wiki.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ngpt
3
- Version: 3.8.1
3
+ Version: 3.8.3
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
@@ -493,6 +493,8 @@ In interactive mode:
493
493
  - For security, your API key is not displayed when editing configurations
494
494
  - When removing a configuration, you'll be asked to confirm before deletion
495
495
 
496
+ ![ngpt-sh-c-a](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-sh-c-a.png)
497
+
496
498
  For more details on configuring nGPT, see the [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration/).
497
499
 
498
500
  ### Configuration File
@@ -541,6 +543,146 @@ nGPT determines configuration values in the following order (highest priority fi
541
543
  4. Main configuration file `ngpt.conf` or `custom-config-file`
542
544
  5. Default values
543
545
 
546
+ ### Real-World Demonstrations with nGPT
547
+
548
+ Let's see nGPT in action! Here are some practical ways you can use it every day:
549
+
550
+ #### Quick Q&A and Coding
551
+
552
+ ```bash
553
+ # Get a quick explanation
554
+ ngpt "Explain the difference between threads and processes in Python"
555
+
556
+ # Generate code with real-time syntax highlighting
557
+ ngpt --code --stream-prettify "Write a Python function to reverse a linked list"
558
+ ```
559
+
560
+ With the `--code` flag, nGPT gives you clean code without explanations or markdown, just what you need to copy and paste into your project. The `--stream-prettify` option shows real-time syntax highlighting as the code comes in.
561
+
562
+ #### Shell Command Generation (OS-Aware)
563
+
564
+ ```bash
565
+ # Let nGPT generate the correct command for your OS
566
+ ngpt --shell "list all files in the current directory including hidden ones"
567
+ # On Linux/macOS: ls -la
568
+ # On Windows: dir /a
569
+ ```
570
+
571
+ One of my favorite features! No more Googling obscure command flags, nGPT generates the right command for your operating system. It'll even execute it for you if you approve.
572
+
573
+ ![ngpt-s-c](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-s-c.png)
574
+
575
+ #### Text Rewriting and Summarization
576
+
577
+ ```bash
578
+ # Pipe text to rewrite it (e.g., improve clarity)
579
+ echo "This is a rough draft of my email." | ngpt -r
580
+
581
+ # Summarize a file using the pipe placeholder
582
+ cat long-article.txt | ngpt --pipe "Summarize this document concisely: {}"
583
+ ```
584
+
585
+ The text rewriting feature is perfect for quickly improving documentation, emails, or reports. And with pipe placeholders, you can feed in content from files or other commands.
586
+
587
+ #### Git Commit Message Generation
588
+
589
+ ```bash
590
+ # Stage your changes
591
+ git add .
592
+
593
+ # Let nGPT generate a conventional commit message based on the diff
594
+ ngpt -g
595
+
596
+ # Generate git commit message from a diff file
597
+ ngpt -g --diff changes.diff
598
+ ```
599
+
600
+ This is a huge time-saver. nGPT analyzes your git diff and generates a properly formatted conventional commit message that actually describes what you changed. No more staring at the blank commit message prompt!
601
+
602
+ ![ngpt-g](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-g.png)
603
+
604
+ #### Web Search Integration
605
+
606
+ ```bash
607
+ # Ask questions that require up-to-date information
608
+ ngpt --web-search "What's the latest news about AI regulation?"
609
+ ```
610
+
611
+ The `--web-search` flag lets nGPT consult the web for recent information, making it useful for questions about current events or topics that might have changed since the AI's training data cutoff.
612
+
613
+ ![ngpt-w](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-w.png)
614
+
615
+ ### Real-World Integration Examples
616
+
617
+ Let's look at how nGPT can fit into your everyday workflow with some practical examples:
618
+
619
+ #### Developer Workflow
620
+
621
+ As a developer, I use nGPT throughout my day:
622
+
623
+ **Morning code review**:
624
+ ```bash
625
+ # Get explanations of complex code
626
+ git show | ngpt --pipe "Explain what this code change does and any potential issues: {}"
627
+ ```
628
+
629
+ **Debugging help**:
630
+ ```bash
631
+ # Help understand a cryptic error message
632
+ npm run build 2>&1 | grep Error | ngpt --pipe "What does this error mean and how can I fix it: {}"
633
+ ```
634
+
635
+ **Documentation generation**:
636
+ ```bash
637
+ # Generate JSDoc comments for functions
638
+ cat src/utils.js | ngpt --pipe "Write proper JSDoc comments for these functions: {}"
639
+ ```
640
+
641
+ **Commit messages**:
642
+ ```bash
643
+ # After finishing a feature
644
+ git add .
645
+ ngpt -g
646
+ ```
647
+
648
+ #### Writer's Assistant
649
+
650
+ For content creators and writers:
651
+
652
+ **Overcoming writer's block**:
653
+ ```bash
654
+ ngpt "Give me 5 different angles to approach an article about sustainable technology"
655
+ ```
656
+
657
+ **Editing assistance**:
658
+ ```bash
659
+ cat draft.md | ngpt -r
660
+ ```
661
+
662
+ **Research summaries**:
663
+ ```bash
664
+ curl -s https://example.com/research-paper.html | ngpt --pipe "Summarize the key findings from this research: {}"
665
+ ```
666
+
667
+ #### System Administrator
668
+
669
+ For sysadmins and DevOps folks:
670
+
671
+ **Generating complex commands**:
672
+ ```bash
673
+ ngpt -s "find all log files larger than 100MB that haven't been modified in the last 30 days"
674
+ ```
675
+
676
+ *Creating configuration files**:
677
+ ```bash
678
+ ngpt --code "Create a Docker Compose file for a Redis, PostgreSQL, and Node.js application"
679
+ ```
680
+
681
+ **Troubleshooting systems**:
682
+ ```bash
683
+ dmesg | tail -50 | ngpt --pipe "Explain what might be causing the issues based on these system logs: {}"
684
+ ```
685
+
544
686
  ## Contributing
545
687
 
546
688
  We welcome contributions to nGPT! Whether it's bug fixes, feature additions, or documentation improvements, your help is appreciated.
@@ -456,6 +456,8 @@ In interactive mode:
456
456
  - For security, your API key is not displayed when editing configurations
457
457
  - When removing a configuration, you'll be asked to confirm before deletion
458
458
 
459
+ ![ngpt-sh-c-a](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-sh-c-a.png)
460
+
459
461
  For more details on configuring nGPT, see the [Configuration Guide](https://nazdridoy.github.io/ngpt/configuration/).
460
462
 
461
463
  ### Configuration File
@@ -504,6 +506,146 @@ nGPT determines configuration values in the following order (highest priority fi
504
506
  4. Main configuration file `ngpt.conf` or `custom-config-file`
505
507
  5. Default values
506
508
 
509
+ ### Real-World Demonstrations with nGPT
510
+
511
+ Let's see nGPT in action! Here are some practical ways you can use it every day:
512
+
513
+ #### Quick Q&A and Coding
514
+
515
+ ```bash
516
+ # Get a quick explanation
517
+ ngpt "Explain the difference between threads and processes in Python"
518
+
519
+ # Generate code with real-time syntax highlighting
520
+ ngpt --code --stream-prettify "Write a Python function to reverse a linked list"
521
+ ```
522
+
523
+ With the `--code` flag, nGPT gives you clean code without explanations or markdown, just what you need to copy and paste into your project. The `--stream-prettify` option shows real-time syntax highlighting as the code comes in.
524
+
525
+ #### Shell Command Generation (OS-Aware)
526
+
527
+ ```bash
528
+ # Let nGPT generate the correct command for your OS
529
+ ngpt --shell "list all files in the current directory including hidden ones"
530
+ # On Linux/macOS: ls -la
531
+ # On Windows: dir /a
532
+ ```
533
+
534
+ One of my favorite features! No more Googling obscure command flags, nGPT generates the right command for your operating system. It'll even execute it for you if you approve.
535
+
536
+ ![ngpt-s-c](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-s-c.png)
537
+
538
+ #### Text Rewriting and Summarization
539
+
540
+ ```bash
541
+ # Pipe text to rewrite it (e.g., improve clarity)
542
+ echo "This is a rough draft of my email." | ngpt -r
543
+
544
+ # Summarize a file using the pipe placeholder
545
+ cat long-article.txt | ngpt --pipe "Summarize this document concisely: {}"
546
+ ```
547
+
548
+ The text rewriting feature is perfect for quickly improving documentation, emails, or reports. And with pipe placeholders, you can feed in content from files or other commands.
549
+
550
+ #### Git Commit Message Generation
551
+
552
+ ```bash
553
+ # Stage your changes
554
+ git add .
555
+
556
+ # Let nGPT generate a conventional commit message based on the diff
557
+ ngpt -g
558
+
559
+ # Generate git commit message from a diff file
560
+ ngpt -g --diff changes.diff
561
+ ```
562
+
563
+ This is a huge time-saver. nGPT analyzes your git diff and generates a properly formatted conventional commit message that actually describes what you changed. No more staring at the blank commit message prompt!
564
+
565
+ ![ngpt-g](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-g.png)
566
+
567
+ #### Web Search Integration
568
+
569
+ ```bash
570
+ # Ask questions that require up-to-date information
571
+ ngpt --web-search "What's the latest news about AI regulation?"
572
+ ```
573
+
574
+ The `--web-search` flag lets nGPT consult the web for recent information, making it useful for questions about current events or topics that might have changed since the AI's training data cutoff.
575
+
576
+ ![ngpt-w](https://raw.githubusercontent.com/nazdridoy/ngpt/main/previews/ngpt-w.png)
577
+
578
+ ### Real-World Integration Examples
579
+
580
+ Let's look at how nGPT can fit into your everyday workflow with some practical examples:
581
+
582
+ #### Developer Workflow
583
+
584
+ As a developer, I use nGPT throughout my day:
585
+
586
+ **Morning code review**:
587
+ ```bash
588
+ # Get explanations of complex code
589
+ git show | ngpt --pipe "Explain what this code change does and any potential issues: {}"
590
+ ```
591
+
592
+ **Debugging help**:
593
+ ```bash
594
+ # Help understand a cryptic error message
595
+ npm run build 2>&1 | grep Error | ngpt --pipe "What does this error mean and how can I fix it: {}"
596
+ ```
597
+
598
+ **Documentation generation**:
599
+ ```bash
600
+ # Generate JSDoc comments for functions
601
+ cat src/utils.js | ngpt --pipe "Write proper JSDoc comments for these functions: {}"
602
+ ```
603
+
604
+ **Commit messages**:
605
+ ```bash
606
+ # After finishing a feature
607
+ git add .
608
+ ngpt -g
609
+ ```
610
+
611
+ #### Writer's Assistant
612
+
613
+ For content creators and writers:
614
+
615
+ **Overcoming writer's block**:
616
+ ```bash
617
+ ngpt "Give me 5 different angles to approach an article about sustainable technology"
618
+ ```
619
+
620
+ **Editing assistance**:
621
+ ```bash
622
+ cat draft.md | ngpt -r
623
+ ```
624
+
625
+ **Research summaries**:
626
+ ```bash
627
+ curl -s https://example.com/research-paper.html | ngpt --pipe "Summarize the key findings from this research: {}"
628
+ ```
629
+
630
+ #### System Administrator
631
+
632
+ For sysadmins and DevOps folks:
633
+
634
+ **Generating complex commands**:
635
+ ```bash
636
+ ngpt -s "find all log files larger than 100MB that haven't been modified in the last 30 days"
637
+ ```
638
+
639
+ *Creating configuration files**:
640
+ ```bash
641
+ ngpt --code "Create a Docker Compose file for a Redis, PostgreSQL, and Node.js application"
642
+ ```
643
+
644
+ **Troubleshooting systems**:
645
+ ```bash
646
+ dmesg | tail -50 | ngpt --pipe "Explain what might be causing the issues based on these system logs: {}"
647
+ ```
648
+
507
649
  ## Contributing
508
650
 
509
651
  We welcome contributions to nGPT! Whether it's bug fixes, feature additions, or documentation improvements, your help is appreciated.
@@ -1,5 +1,5 @@
1
1
  from ..formatters import COLORS
2
- from ..renderers import prettify_markdown, prettify_streaming_markdown
2
+ from ..renderers import prettify_markdown, prettify_streaming_markdown, TERMINAL_RENDER_LOCK
3
3
  from ..ui import spinner
4
4
  from ...utils import enhance_prompt_with_web_search, process_piped_input
5
5
  import sys
@@ -53,9 +53,10 @@ def chat_mode(client, args, logger=None):
53
53
  stop_spinner.set()
54
54
  spinner_thread.join()
55
55
  # Clear the spinner line completely
56
- sys.stdout.write("\r" + " " * 100 + "\r")
57
- sys.stdout.flush()
58
- print("Enhanced input with web search results.")
56
+ with TERMINAL_RENDER_LOCK:
57
+ sys.stdout.write("\r" + " " * 100 + "\r")
58
+ sys.stdout.flush()
59
+ print("Enhanced input with web search results.")
59
60
  except Exception as e:
60
61
  # Stop the spinner before re-raising
61
62
  stop_spinner.set()
@@ -129,11 +130,14 @@ def chat_mode(client, args, logger=None):
129
130
  # On first content, stop the spinner
130
131
  if not first_content_received and stop_spinner_func:
131
132
  first_content_received = True
132
- # Stop the spinner
133
- stop_spinner_func()
134
- # Ensure spinner message is cleared with an extra blank line
135
- sys.stdout.write("\r" + " " * 100 + "\r")
136
- sys.stdout.flush()
133
+
134
+ # Use lock to prevent terminal rendering conflicts
135
+ with TERMINAL_RENDER_LOCK:
136
+ # Stop the spinner
137
+ stop_spinner_func()
138
+ # Ensure spinner message is cleared with an extra blank line
139
+ sys.stdout.write("\r" + " " * 100 + "\r")
140
+ sys.stdout.flush()
137
141
 
138
142
  # Call the original callback to update the display
139
143
  if original_callback:
@@ -165,7 +169,8 @@ def chat_mode(client, args, logger=None):
165
169
 
166
170
  # Handle non-stream response or regular prettify
167
171
  if (args.no_stream or args.prettify) and response:
168
- if args.prettify:
169
- prettify_markdown(response, args.renderer)
170
- else:
171
- print(response)
172
+ with TERMINAL_RENDER_LOCK:
173
+ if args.prettify:
174
+ prettify_markdown(response, args.renderer)
175
+ else:
176
+ print(response)
@@ -1,5 +1,5 @@
1
1
  from ..formatters import COLORS
2
- from ..renderers import prettify_markdown, prettify_streaming_markdown, has_markdown_renderer, show_available_renderers
2
+ from ..renderers import prettify_markdown, prettify_streaming_markdown, has_markdown_renderer, show_available_renderers, TERMINAL_RENDER_LOCK
3
3
  from ..ui import spinner, copy_to_clipboard
4
4
  from ...utils import enhance_prompt_with_web_search, process_piped_input
5
5
  import sys
@@ -126,9 +126,10 @@ def code_mode(client, args, logger=None):
126
126
  stop_spinner.set()
127
127
  spinner_thread.join()
128
128
  # Clear the spinner line completely
129
- sys.stdout.write("\r" + " " * 100 + "\r")
130
- sys.stdout.flush()
131
- print("Enhanced input with web search results.")
129
+ with TERMINAL_RENDER_LOCK:
130
+ sys.stdout.write("\r" + " " * 100 + "\r")
131
+ sys.stdout.flush()
132
+ print("Enhanced input with web search results.")
132
133
  except Exception as e:
133
134
  # Stop the spinner before re-raising
134
135
  stop_spinner.set()
@@ -211,11 +212,14 @@ def code_mode(client, args, logger=None):
211
212
  # On first content, stop the spinner
212
213
  if not first_content_received and stop_spinner_func:
213
214
  first_content_received = True
214
- # Stop the spinner
215
- stop_spinner_func()
216
- # Ensure spinner message is cleared with an extra blank line
217
- sys.stdout.write("\r" + " " * 100 + "\r")
218
- sys.stdout.flush()
215
+
216
+ # Use lock to prevent terminal rendering conflicts
217
+ with TERMINAL_RENDER_LOCK:
218
+ # Stop the spinner
219
+ stop_spinner_func()
220
+ # Ensure spinner message is cleared with an extra blank line
221
+ sys.stdout.write("\r" + " " * 100 + "\r")
222
+ sys.stdout.flush()
219
223
 
220
224
  # Call the original callback to update the display
221
225
  if original_callback:
@@ -297,12 +301,13 @@ def code_mode(client, args, logger=None):
297
301
 
298
302
  # Print non-streamed output if needed
299
303
  if generated_code and not should_stream:
300
- if use_regular_prettify:
301
- print("\nGenerated code:")
302
- prettify_markdown(generated_code, args.renderer)
303
- else:
304
- # Should only happen if --no-stream was used without prettify
305
- print(f"\nGenerated code:\n{generated_code}")
304
+ with TERMINAL_RENDER_LOCK:
305
+ if use_regular_prettify:
306
+ print("\nGenerated code:")
307
+ prettify_markdown(generated_code, args.renderer)
308
+ else:
309
+ # Should only happen if --no-stream was used without prettify
310
+ print(f"\nGenerated code:\n{generated_code}")
306
311
 
307
312
  # Offer to copy to clipboard
308
313
  if generated_code and not args.no_stream:
@@ -5,7 +5,7 @@ import threading
5
5
  import sys
6
6
  import time
7
7
  from ..formatters import COLORS
8
- from ..renderers import prettify_markdown, prettify_streaming_markdown
8
+ from ..renderers import prettify_markdown, prettify_streaming_markdown, TERMINAL_RENDER_LOCK
9
9
  from ..ui import spinner
10
10
  from ...utils import enhance_prompt_with_web_search
11
11
 
@@ -78,8 +78,9 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
78
78
  def print_separator():
79
79
  # Make sure there's exactly one newline before and after
80
80
  # Use sys.stdout.write for direct control, avoiding any extra newlines
81
- sys.stdout.write(f"\n{separator}\n")
82
- sys.stdout.flush()
81
+ with TERMINAL_RENDER_LOCK:
82
+ sys.stdout.write(f"\n{separator}\n")
83
+ sys.stdout.flush()
83
84
 
84
85
  # Initialize conversation history
85
86
  system_prompt = preprompt if preprompt else "You are a helpful assistant."
@@ -111,35 +112,37 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
111
112
 
112
113
  # Function to display conversation history
113
114
  def display_history():
114
- if len(conversation) <= 1: # Only system message
115
- print(f"\n{COLORS['yellow']}No conversation history yet.{COLORS['reset']}")
116
- return
117
-
118
- print(f"\n{COLORS['cyan']}{COLORS['bold']}Conversation History:{COLORS['reset']}")
119
- print(separator)
120
-
121
- # Skip system message
122
- message_count = 0
123
- for i, msg in enumerate(conversation):
124
- if msg["role"] == "system":
125
- continue
115
+ with TERMINAL_RENDER_LOCK:
116
+ if len(conversation) <= 1: # Only system message
117
+ print(f"\n{COLORS['yellow']}No conversation history yet.{COLORS['reset']}")
118
+ return
126
119
 
127
- if msg["role"] == "user":
128
- message_count += 1
129
- print(f"\n{user_header()}")
130
- print(f"{COLORS['cyan']}│ [{message_count}] {COLORS['reset']}{msg['content']}")
131
- elif msg["role"] == "assistant":
132
- print(f"\n{ngpt_header()}")
133
- print(f"{COLORS['green']}│ {COLORS['reset']}{msg['content']}")
134
-
135
- print(f"\n{separator}") # Consistent separator at the end
120
+ print(f"\n{COLORS['cyan']}{COLORS['bold']}Conversation History:{COLORS['reset']}")
121
+ print(separator)
122
+
123
+ # Skip system message
124
+ message_count = 0
125
+ for i, msg in enumerate(conversation):
126
+ if msg["role"] == "system":
127
+ continue
128
+
129
+ if msg["role"] == "user":
130
+ message_count += 1
131
+ print(f"\n{user_header()}")
132
+ print(f"{COLORS['cyan']}│ [{message_count}] {COLORS['reset']}{msg['content']}")
133
+ elif msg["role"] == "assistant":
134
+ print(f"\n{ngpt_header()}")
135
+ print(f"{COLORS['green']}│ {COLORS['reset']}{msg['content']}")
136
+
137
+ print(f"\n{separator}") # Consistent separator at the end
136
138
 
137
139
  # Function to clear conversation history
138
140
  def clear_history():
139
141
  nonlocal conversation
140
142
  conversation = [{"role": "system", "content": system_prompt}]
141
- print(f"\n{COLORS['yellow']}Conversation history cleared.{COLORS['reset']}")
142
- print(separator) # Add separator for consistency
143
+ with TERMINAL_RENDER_LOCK:
144
+ print(f"\n{COLORS['yellow']}Conversation history cleared.{COLORS['reset']}")
145
+ print(separator) # Add separator for consistency
143
146
 
144
147
  try:
145
148
  while True:
@@ -249,10 +252,11 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
249
252
 
250
253
  # Print the header if needed
251
254
  if should_print_header:
252
- if not no_stream and not stream_prettify:
253
- print(f"\n{ngpt_header()}: {COLORS['reset']}", end="", flush=True)
254
- elif not stream_prettify:
255
- print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
255
+ with TERMINAL_RENDER_LOCK:
256
+ if not no_stream and not stream_prettify:
257
+ print(f"\n{ngpt_header()}: {COLORS['reset']}", end="", flush=True)
258
+ elif not stream_prettify:
259
+ print(f"\n{ngpt_header()}: {COLORS['reset']}", flush=True)
256
260
 
257
261
  # Determine streaming behavior
258
262
  if prettify and not no_stream and not stream_prettify:
@@ -294,18 +298,20 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
294
298
  if not first_content_received:
295
299
  first_content_received = True
296
300
 
297
- # Stop the spinner if it's running
298
- if stop_spinner_func:
299
- stop_spinner_func()
301
+ # Use lock to prevent terminal rendering conflicts
302
+ with TERMINAL_RENDER_LOCK:
303
+ # Stop the spinner if it's running
304
+ if stop_spinner_func:
305
+ stop_spinner_func()
306
+
307
+ # Clear the spinner line completely without leaving extra newlines
308
+ # Use direct terminal control to ensure consistency
309
+ sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
310
+ sys.stdout.flush()
300
311
 
301
- # Clear the spinner line completely without leaving extra newlines
302
- # Use direct terminal control to ensure consistency
303
- sys.stdout.write("\r" + " " * shutil.get_terminal_size().columns + "\r")
304
- sys.stdout.flush()
305
-
306
- # Now start the live display
307
- if live_display:
308
- live_display.start()
312
+ # Now start the live display
313
+ if live_display:
314
+ live_display.start()
309
315
 
310
316
  # Call the original callback to update content
311
317
  if original_callback:
@@ -351,11 +357,12 @@ def interactive_chat_session(client, web_search=False, no_stream=False, temperat
351
357
 
352
358
  # Print response if not streamed (either due to no_stream or prettify)
353
359
  if no_stream or prettify:
354
- if prettify:
355
- # For pretty formatting with rich, don't print any header text as the rich renderer already includes it
356
- prettify_markdown(response, renderer)
357
- else:
358
- print(response)
360
+ with TERMINAL_RENDER_LOCK:
361
+ if prettify:
362
+ # For pretty formatting with rich, don't print any header text as the rich renderer already includes it
363
+ prettify_markdown(response, renderer)
364
+ else:
365
+ print(response)
359
366
 
360
367
  # Log AI response if logging is enabled
361
368
  if logger:
@@ -2,7 +2,7 @@ import sys
2
2
  import threading
3
3
  import time
4
4
  from ..formatters import COLORS
5
- from ..renderers import prettify_markdown, prettify_streaming_markdown
5
+ from ..renderers import prettify_markdown, prettify_streaming_markdown, TERMINAL_RENDER_LOCK
6
6
  from ..ui import get_multiline_input, spinner, copy_to_clipboard
7
7
  from ...utils import enhance_prompt_with_web_search, process_piped_input
8
8
 
@@ -127,9 +127,10 @@ def rewrite_mode(client, args, logger=None):
127
127
  stop_spinner.set()
128
128
  spinner_thread.join()
129
129
  # Clear the spinner line completely
130
- sys.stdout.write("\r" + " " * 100 + "\r")
131
- sys.stdout.flush()
132
- print("Enhanced input with web search results.")
130
+ with TERMINAL_RENDER_LOCK:
131
+ sys.stdout.write("\r" + " " * 100 + "\r")
132
+ sys.stdout.flush()
133
+ print("Enhanced input with web search results.")
133
134
  except Exception as e:
134
135
  # Stop the spinner before re-raising
135
136
  stop_spinner.set()
@@ -197,11 +198,14 @@ def rewrite_mode(client, args, logger=None):
197
198
  # On first content, stop the spinner
198
199
  if not first_content_received and stop_spinner_func:
199
200
  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\n")
204
- sys.stdout.flush()
201
+
202
+ # Use lock to prevent terminal rendering conflicts
203
+ with TERMINAL_RENDER_LOCK:
204
+ # Stop the spinner
205
+ stop_spinner_func()
206
+ # Ensure spinner message is cleared with an extra blank line
207
+ sys.stdout.write("\r" + " " * 100 + "\r\n")
208
+ sys.stdout.flush()
205
209
 
206
210
  # Call the original callback to update the display
207
211
  if original_callback:
@@ -241,10 +245,11 @@ def rewrite_mode(client, args, logger=None):
241
245
 
242
246
  # Handle non-stream response or regular prettify
243
247
  if (args.no_stream or args.prettify) and response:
244
- if args.prettify:
245
- prettify_markdown(response, args.renderer)
246
- else:
247
- print(response)
248
+ with TERMINAL_RENDER_LOCK:
249
+ if args.prettify:
250
+ prettify_markdown(response, args.renderer)
251
+ else:
252
+ print(response)
248
253
 
249
254
  # Offer to copy to clipboard if not in a redirected output
250
255
  if not args.no_stream and response: