kopipasta 0.22.0__py3-none-any.whl → 0.24.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.

Potentially problematic release.


This version of kopipasta might be problematic. Click here for more details.

kopipasta/main.py CHANGED
@@ -22,6 +22,7 @@ from pydantic import BaseModel, Field
22
22
  import traceback
23
23
  from google import genai
24
24
  from google.genai.types import GenerateContentConfig
25
+ from prompt_toolkit import prompt # Added for multiline input
25
26
 
26
27
  FileTuple = Tuple[str, bool, Optional[List[str]], str]
27
28
 
@@ -682,65 +683,6 @@ def fetch_web_content(url: str) -> Tuple[Optional[FileTuple], Optional[str], Opt
682
683
  print(f"Error fetching content from {url}: {e}")
683
684
  return None, None, None
684
685
 
685
- def read_file_content(file_path):
686
- _, ext = os.path.splitext(file_path)
687
- if ext.lower() == '.json':
688
- with open(file_path, 'r') as f:
689
- return json.load(f), 'json'
690
- elif ext.lower() == '.csv':
691
- with open(file_path, 'r') as f:
692
- return f.read(), 'csv'
693
- else:
694
- with open(file_path, 'r') as f:
695
- return f.read(), 'text'
696
-
697
- def get_content_snippet(content, content_type, max_lines=50, max_chars=4096):
698
- if content_type == 'json':
699
- return json.dumps(content, indent=2)[:max_chars]
700
- elif content_type == 'csv':
701
- csv_content = content if isinstance(content, str) else content.getvalue()
702
- csv_reader = csv.reader(io.StringIO(csv_content))
703
- rows = list(csv_reader)[:max_lines]
704
- output = io.StringIO()
705
- csv.writer(output).writerows(rows)
706
- return output.getvalue()[:max_chars]
707
- else:
708
- return '\n'.join(content.split('\n')[:max_lines])[:max_chars]
709
-
710
- def handle_content(content, content_type, file_or_url):
711
- is_large = len(json.dumps(content)) > 102400 if content_type == 'json' else len(content) > 102400
712
-
713
- if is_large:
714
- while True:
715
- choice = input(f"{file_or_url} is large. View (f)ull content, (s)nippet, or (p)review? ").lower()
716
- if choice in ['f', 's', 'p']:
717
- break
718
- print("Invalid choice. Please enter 'f', 's', or 'p'.")
719
-
720
- if choice == 'f':
721
- return content, False
722
- elif choice == 's':
723
- return get_content_snippet(content, content_type), True
724
- else: # preview
725
- preview = get_content_preview(content, content_type)
726
- print(f"\nPreview of {file_or_url}:\n{preview}\n")
727
- return handle_content(content, content_type, file_or_url)
728
- else:
729
- return content, False
730
-
731
-
732
- def get_content_preview(content, content_type):
733
- if content_type == 'json':
734
- return json.dumps(content, indent=2)[:1000] + "\n..."
735
- elif content_type == 'csv':
736
- csv_content = content if isinstance(content, str) else content.getvalue()
737
- csv_reader = csv.reader(io.StringIO(csv_content))
738
- rows = list(csv_reader)[:10]
739
- output = io.StringIO()
740
- csv.writer(output).writerows(rows)
741
- return output.getvalue() + "\n..."
742
- else:
743
- return '\n'.join(content.split('\n')[:20]) + "\n..."
744
686
 
745
687
  def read_env_file():
746
688
  env_vars = {}
@@ -920,9 +862,14 @@ def start_chat_session(initial_prompt: str):
920
862
  print("\n" + "-"*20)
921
863
 
922
864
  while True:
865
+ is_patch_request = False
923
866
  try:
924
- user_input = input("👤 You: ")
925
- except EOFError: # Handle Ctrl+D
867
+ # Print the header on a separate line
868
+ print("👤 You (Submit with Esc+Enter):")
869
+ # Get input using prompt_toolkit with a minimal indicator
870
+ user_input = prompt(">> ", multiline=True)
871
+ # prompt_toolkit raises EOFError on Ctrl+D, so this handler remains correct.
872
+ except EOFError:
926
873
  print("\nExiting...")
927
874
  break
928
875
  except KeyboardInterrupt: # Handle Ctrl+C
@@ -931,19 +878,33 @@ def start_chat_session(initial_prompt: str):
931
878
 
932
879
  if user_input.lower() == '/q':
933
880
  break
934
- elif user_input.strip() == '/patch':
935
- print("\n🤖 Gemini: Thinking... (requesting code changes)")
936
- # Prompt instructing the model to use the new JSON format
881
+ elif user_input.endswith('/patch'):
882
+ is_patch_request = True
883
+ # Extract message before /patch
884
+ user_message = user_input[:-len('/patch')].strip()
885
+ print(f"\n🛠️ Requesting patches... (Context: '{user_message}' if provided)")
886
+ elif not user_input:
887
+ continue # Ignore empty input
888
+ else:
889
+ user_message = user_input # Regular message
890
+
891
+
892
+ # --- Handle Patch Request ---
893
+ if is_patch_request:
894
+ print("🤖 Gemini: Thinking... (generating code changes)")
895
+ # Include user message part if it exists
896
+ patch_context = f"Based on our conversation and specifically: \"{user_message}\"\n\n" if user_message else "Based on our conversation,\n\n"
897
+
937
898
  patch_request_prompt = (
938
- "Based on our conversation, generate the necessary code changes "
939
- "to fulfill my request. Provide the changes as a JSON list, where each item "
899
+ patch_context +
900
+ "Generate the necessary code changes to fulfill the request. Provide the changes as a JSON list, where each item "
940
901
  "is an object with the following keys:\n"
941
902
  "- 'reasoning': Explain why this specific change is needed.\n"
942
903
  "- 'file_path': The relative path to the file to modify.\n"
943
904
  "- 'original_text': The exact, unique block of text to replace.\n"
944
- "- 'new_text': The text to replace original_text with.\n"
905
+ "- 'new_text': The text to replace original_text with. Do not include any temporary comments like '// CHANGE BEGINS' or '/* PATCH START */'.\n"
945
906
  "Ensure 'original_text' is unique within the specified 'file_path'. "
946
- "Format the response strictly as: { \"patches\": [ { patch_item_1 }, { patch_item_2 }, ... ] }"
907
+ "Respond ONLY with the JSON object conforming to this structure: { \"patches\": [ { patch_item_1 }, { patch_item_2 }, ... ] }"
947
908
  )
948
909
 
949
910
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kopipasta
3
- Version: 0.22.0
3
+ Version: 0.24.0
4
4
  Summary: A CLI tool to generate prompts with project structure and file contents
5
5
  Home-page: https://github.com/mkorpela/kopipasta
6
6
  Author: Mikko Korpela
@@ -19,10 +19,11 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
- Requires-Dist: pyperclip ==1.9.0
23
- Requires-Dist: requests ==2.32.3
24
- Requires-Dist: Pygments ==2.18.0
25
- Requires-Dist: google-genai ==1.8.0
22
+ Requires-Dist: pyperclip==1.9.0
23
+ Requires-Dist: requests==2.32.3
24
+ Requires-Dist: Pygments==2.18.0
25
+ Requires-Dist: google-genai==1.8.0
26
+ Requires-Dist: prompt-toolkit==3.0.50
26
27
 
27
28
  # kopipasta
28
29
 
@@ -0,0 +1,8 @@
1
+ kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ kopipasta/main.py,sha256=iJFQFiN-3udjud2byspXl8RZwZnTg6FPw_HW1exjPbg,53035
3
+ kopipasta-0.24.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
4
+ kopipasta-0.24.0.dist-info/METADATA,sha256=EiMoa_TkI-4SMaMiyiUqroXAvwz1JFM8_ECINOw4fZM,8610
5
+ kopipasta-0.24.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
6
+ kopipasta-0.24.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
7
+ kopipasta-0.24.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
8
+ kopipasta-0.24.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- kopipasta/main.py,sha256=RSoKARL9jnyikiXB1DvZz_lpWLhhQUovQuT6YUj7RGY,54292
3
- kopipasta-0.22.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
4
- kopipasta-0.22.0.dist-info/METADATA,sha256=V2XrhJR8QIuCIa4iCDFuo5gyW1iJgihWK_Y6H3mVjMI,8576
5
- kopipasta-0.22.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
6
- kopipasta-0.22.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
7
- kopipasta-0.22.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
8
- kopipasta-0.22.0.dist-info/RECORD,,