kopipasta 0.14.0__py3-none-any.whl → 0.16.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 +74 -46
- {kopipasta-0.14.0.dist-info → kopipasta-0.16.0.dist-info}/METADATA +1 -1
- kopipasta-0.16.0.dist-info/RECORD +8 -0
- kopipasta-0.14.0.dist-info/RECORD +0 -8
- {kopipasta-0.14.0.dist-info → kopipasta-0.16.0.dist-info}/LICENSE +0 -0
- {kopipasta-0.14.0.dist-info → kopipasta-0.16.0.dist-info}/WHEEL +0 -0
- {kopipasta-0.14.0.dist-info → kopipasta-0.16.0.dist-info}/entry_points.txt +0 -0
- {kopipasta-0.14.0.dist-info → kopipasta-0.16.0.dist-info}/top_level.txt +0 -0
kopipasta/main.py
CHANGED
|
@@ -4,14 +4,15 @@ import io
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
6
|
import argparse
|
|
7
|
-
import ast
|
|
8
7
|
import re
|
|
9
|
-
from
|
|
8
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
10
9
|
import pyperclip
|
|
11
10
|
import fnmatch
|
|
12
11
|
|
|
13
12
|
import requests
|
|
14
13
|
|
|
14
|
+
FileTuple = Tuple[str, bool, Optional[List[str]], str]
|
|
15
|
+
|
|
15
16
|
def read_gitignore():
|
|
16
17
|
default_ignore_patterns = [
|
|
17
18
|
'.git', 'node_modules', 'venv', '.venv', 'dist', '.idea', '__pycache__',
|
|
@@ -425,7 +426,7 @@ def print_char_count(count):
|
|
|
425
426
|
token_estimate = count // 4
|
|
426
427
|
print(f"\rCurrent prompt size: {count} characters (~ {token_estimate} tokens)", flush=True)
|
|
427
428
|
|
|
428
|
-
def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
429
|
+
def select_files_in_directory(directory: str, ignore_patterns: List[str], current_char_count: int = 0) -> Tuple[List[FileTuple], int]:
|
|
429
430
|
files = [f for f in os.listdir(directory)
|
|
430
431
|
if os.path.isfile(os.path.join(directory, f)) and not is_ignored(os.path.join(directory, f), ignore_patterns) and not is_binary(os.path.join(directory, f))]
|
|
431
432
|
|
|
@@ -445,8 +446,8 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
445
446
|
while True:
|
|
446
447
|
print_char_count(current_char_count)
|
|
447
448
|
choice = input("(y)es add all / (n)o ignore all / (s)elect individually / (q)uit? ").lower()
|
|
449
|
+
selected_files: List[FileTuple] = []
|
|
448
450
|
if choice == 'y':
|
|
449
|
-
selected_files = []
|
|
450
451
|
for file in files:
|
|
451
452
|
file_path = os.path.join(directory, file)
|
|
452
453
|
if is_large_file(file_path):
|
|
@@ -456,13 +457,13 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
456
457
|
break
|
|
457
458
|
print("Invalid choice. Please enter 'f' or 's'.")
|
|
458
459
|
if snippet_choice == 's':
|
|
459
|
-
selected_files.append((file, True))
|
|
460
|
+
selected_files.append((file, True, None, get_language_for_file(file)))
|
|
460
461
|
current_char_count += len(get_file_snippet(file_path))
|
|
461
462
|
else:
|
|
462
|
-
selected_files.append((file, False))
|
|
463
|
+
selected_files.append((file, False, None, get_language_for_file(file)))
|
|
463
464
|
current_char_count += os.path.getsize(file_path)
|
|
464
465
|
else:
|
|
465
|
-
selected_files.append((file, False))
|
|
466
|
+
selected_files.append((file, False, None, get_language_for_file(file)))
|
|
466
467
|
current_char_count += os.path.getsize(file_path)
|
|
467
468
|
print(f"Added all files from {directory}")
|
|
468
469
|
return selected_files, current_char_count
|
|
@@ -470,7 +471,6 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
470
471
|
print(f"Ignored all files from {directory}")
|
|
471
472
|
return [], current_char_count
|
|
472
473
|
elif choice == 's':
|
|
473
|
-
selected_files = []
|
|
474
474
|
for file in files:
|
|
475
475
|
file_path = os.path.join(directory, file)
|
|
476
476
|
file_size = os.path.getsize(file_path)
|
|
@@ -489,13 +489,13 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
489
489
|
break
|
|
490
490
|
print("Invalid choice. Please enter 'f' or 's'.")
|
|
491
491
|
if snippet_choice == 's':
|
|
492
|
-
selected_files.append((file, True))
|
|
492
|
+
selected_files.append((file, True, None, get_language_for_file(file_path)))
|
|
493
493
|
current_char_count += len(get_file_snippet(file_path))
|
|
494
494
|
else:
|
|
495
|
-
selected_files.append((file, False))
|
|
495
|
+
selected_files.append((file, False, None, get_language_for_file(file_path)))
|
|
496
496
|
current_char_count += file_char_estimate
|
|
497
497
|
else:
|
|
498
|
-
selected_files.append((file, False))
|
|
498
|
+
selected_files.append((file, False, None, get_language_for_file(file_path)))
|
|
499
499
|
current_char_count += file_char_estimate
|
|
500
500
|
break
|
|
501
501
|
elif file_choice == 'n':
|
|
@@ -503,7 +503,7 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
503
503
|
elif file_choice == 'p':
|
|
504
504
|
chunks, char_count = select_file_patches(file_path)
|
|
505
505
|
if chunks:
|
|
506
|
-
selected_files.append((file_path, False, chunks))
|
|
506
|
+
selected_files.append((file_path, False, chunks, get_language_for_file(file_path)))
|
|
507
507
|
current_char_count += char_count
|
|
508
508
|
break
|
|
509
509
|
elif file_choice == 'q':
|
|
@@ -519,9 +519,9 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
519
519
|
else:
|
|
520
520
|
print("Invalid choice. Please try again.")
|
|
521
521
|
|
|
522
|
-
def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
523
|
-
files_to_include = []
|
|
524
|
-
processed_dirs = set()
|
|
522
|
+
def process_directory(directory: str, ignore_patterns: List[str], current_char_count: int = 0) -> Tuple[List[FileTuple], Set[str], int]:
|
|
523
|
+
files_to_include: List[FileTuple] = []
|
|
524
|
+
processed_dirs: Set[str] = set()
|
|
525
525
|
|
|
526
526
|
for root, dirs, files in os.walk(directory):
|
|
527
527
|
dirs[:] = [d for d in dirs if not is_ignored(os.path.join(root, d), ignore_patterns)]
|
|
@@ -535,12 +535,8 @@ def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
535
535
|
if choice == 'y':
|
|
536
536
|
selected_files, current_char_count = select_files_in_directory(root, ignore_patterns, current_char_count)
|
|
537
537
|
for file_tuple in selected_files:
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
files_to_include.append((os.path.join(root, f), use_snippet, chunks))
|
|
541
|
-
else:
|
|
542
|
-
f, use_snippet = file_tuple
|
|
543
|
-
files_to_include.append((os.path.join(root, f), use_snippet))
|
|
538
|
+
full_path = os.path.join(root, file_tuple[0])
|
|
539
|
+
files_to_include.append((full_path, file_tuple[1], file_tuple[2], file_tuple[3]))
|
|
544
540
|
processed_dirs.add(root)
|
|
545
541
|
elif choice == 'n':
|
|
546
542
|
dirs[:] = [] # Skip all subdirectories
|
|
@@ -553,20 +549,25 @@ def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
553
549
|
|
|
554
550
|
return files_to_include, processed_dirs, current_char_count
|
|
555
551
|
|
|
556
|
-
def fetch_web_content(url):
|
|
552
|
+
def fetch_web_content(url: str) -> Tuple[Optional[FileTuple], Optional[str], Optional[str]]:
|
|
557
553
|
try:
|
|
558
554
|
response = requests.get(url)
|
|
559
555
|
response.raise_for_status()
|
|
560
556
|
content_type = response.headers.get('content-type', '').lower()
|
|
557
|
+
full_content = response.text
|
|
558
|
+
snippet = full_content[:10000] + "..." if len(full_content) > 10000 else full_content
|
|
559
|
+
|
|
561
560
|
if 'json' in content_type:
|
|
562
|
-
|
|
561
|
+
content_type = 'json'
|
|
563
562
|
elif 'csv' in content_type:
|
|
564
|
-
|
|
563
|
+
content_type = 'csv'
|
|
565
564
|
else:
|
|
566
|
-
|
|
565
|
+
content_type = 'text'
|
|
566
|
+
|
|
567
|
+
return (url, False, None, content_type), full_content, snippet
|
|
567
568
|
except requests.RequestException as e:
|
|
568
569
|
print(f"Error fetching content from {url}: {e}")
|
|
569
|
-
return None, None
|
|
570
|
+
return None, None, None
|
|
570
571
|
|
|
571
572
|
def read_file_content(file_path):
|
|
572
573
|
_, ext = os.path.splitext(file_path)
|
|
@@ -670,34 +671,34 @@ def handle_env_variables(content, env_vars):
|
|
|
670
671
|
|
|
671
672
|
return content
|
|
672
673
|
|
|
673
|
-
def generate_prompt(files_to_include, ignore_patterns, web_contents, env_vars):
|
|
674
|
+
def generate_prompt(files_to_include: List[FileTuple], ignore_patterns: List[str], web_contents: Dict[str, Tuple[FileTuple, str]], env_vars: Dict[str, str]) -> str:
|
|
674
675
|
prompt = "# Project Overview\n\n"
|
|
675
676
|
prompt += "## Project Structure\n\n"
|
|
676
677
|
prompt += "```\n"
|
|
677
678
|
prompt += get_project_structure(ignore_patterns)
|
|
678
679
|
prompt += "\n```\n\n"
|
|
679
680
|
prompt += "## File Contents\n\n"
|
|
680
|
-
for
|
|
681
|
-
if len(file_tuple) == 4:
|
|
682
|
-
file, content, is_snippet, content_type = file_tuple
|
|
683
|
-
chunks = None
|
|
684
|
-
else:
|
|
685
|
-
file, content, is_snippet, content_type, chunks = file_tuple
|
|
681
|
+
for file, use_snippet, chunks, content_type in files_to_include:
|
|
686
682
|
relative_path = get_relative_path(file)
|
|
687
|
-
language =
|
|
683
|
+
language = content_type if content_type else get_language_for_file(file)
|
|
688
684
|
|
|
689
685
|
if chunks is not None:
|
|
690
686
|
prompt += f"### {relative_path} (selected patches)\n\n```{language}\n"
|
|
691
687
|
for chunk in chunks:
|
|
692
688
|
prompt += f"{chunk}\n"
|
|
693
689
|
prompt += "```\n\n"
|
|
690
|
+
elif use_snippet:
|
|
691
|
+
file_content = get_file_snippet(file)
|
|
692
|
+
prompt += f"### {relative_path} (snippet)\n\n```{language}\n{file_content}\n```\n\n"
|
|
694
693
|
else:
|
|
695
|
-
|
|
696
|
-
|
|
694
|
+
file_content = read_file_contents(file)
|
|
695
|
+
file_content = handle_env_variables(file_content, env_vars)
|
|
696
|
+
prompt += f"### {relative_path}\n\n```{language}\n{file_content}\n```\n\n"
|
|
697
697
|
|
|
698
698
|
if web_contents:
|
|
699
699
|
prompt += "## Web Content\n\n"
|
|
700
|
-
for url, (
|
|
700
|
+
for url, (file_tuple, content) in web_contents.items():
|
|
701
|
+
_, is_snippet, _, content_type = file_tuple
|
|
701
702
|
content = handle_env_variables(content, env_vars)
|
|
702
703
|
language = content_type if content_type in ['json', 'csv'] else ''
|
|
703
704
|
prompt += f"### {url}{' (snippet)' if is_snippet else ''}\n\n```{language}\n{content}\n```\n\n"
|
|
@@ -722,25 +723,52 @@ def main():
|
|
|
722
723
|
ignore_patterns = read_gitignore()
|
|
723
724
|
env_vars = read_env_file()
|
|
724
725
|
|
|
725
|
-
files_to_include = []
|
|
726
|
+
files_to_include: List[FileTuple] = []
|
|
726
727
|
processed_dirs = set()
|
|
727
|
-
web_contents = {}
|
|
728
|
+
web_contents: Dict[str, Tuple[FileTuple, str]] = {}
|
|
728
729
|
current_char_count = 0
|
|
729
730
|
|
|
730
731
|
for input_path in args.inputs:
|
|
731
732
|
if input_path.startswith(('http://', 'https://')):
|
|
732
|
-
|
|
733
|
-
if
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
733
|
+
result = fetch_web_content(input_path)
|
|
734
|
+
if result:
|
|
735
|
+
file_tuple, full_content, snippet = result
|
|
736
|
+
is_large = len(full_content) > 10000
|
|
737
|
+
if is_large:
|
|
738
|
+
print(f"\nContent from {input_path} is large. Here's a snippet:\n")
|
|
739
|
+
print(snippet)
|
|
740
|
+
print("\n" + "-"*40 + "\n")
|
|
741
|
+
|
|
742
|
+
while True:
|
|
743
|
+
choice = input("Use (f)ull content or (s)nippet? ").lower()
|
|
744
|
+
if choice in ['f', 's']:
|
|
745
|
+
break
|
|
746
|
+
print("Invalid choice. Please enter 'f' or 's'.")
|
|
747
|
+
|
|
748
|
+
if choice == 'f':
|
|
749
|
+
content = full_content
|
|
750
|
+
is_snippet = False
|
|
751
|
+
print("Using full content.")
|
|
752
|
+
else:
|
|
753
|
+
content = snippet
|
|
754
|
+
is_snippet = True
|
|
755
|
+
print("Using snippet.")
|
|
756
|
+
else:
|
|
757
|
+
content = full_content
|
|
758
|
+
is_snippet = False
|
|
759
|
+
print(f"Content from {input_path} is not large. Using full content.")
|
|
760
|
+
|
|
761
|
+
file_tuple = (file_tuple[0], is_snippet, file_tuple[2], file_tuple[3])
|
|
762
|
+
web_contents[input_path] = (file_tuple, content)
|
|
763
|
+
current_char_count += len(content)
|
|
764
|
+
print(f"Added {'snippet of ' if is_snippet else ''}web content from: {input_path}")
|
|
737
765
|
elif os.path.isfile(input_path):
|
|
738
766
|
if not is_ignored(input_path, ignore_patterns) and not is_binary(input_path):
|
|
739
767
|
while True:
|
|
740
768
|
file_choice = input(f"{input_path} (y)es include / (n)o skip / (p)atches / (q)uit? ").lower()
|
|
741
769
|
if file_choice == 'y':
|
|
742
770
|
use_snippet = is_large_file(input_path)
|
|
743
|
-
files_to_include.append((input_path, use_snippet))
|
|
771
|
+
files_to_include.append((input_path, use_snippet, None, get_language_for_file(input_path)))
|
|
744
772
|
if use_snippet:
|
|
745
773
|
current_char_count += len(get_file_snippet(input_path))
|
|
746
774
|
else:
|
|
@@ -752,7 +780,7 @@ def main():
|
|
|
752
780
|
elif file_choice == 'p':
|
|
753
781
|
chunks, char_count = select_file_patches(input_path)
|
|
754
782
|
if chunks:
|
|
755
|
-
files_to_include.append((input_path, False, chunks))
|
|
783
|
+
files_to_include.append((input_path, False, chunks, get_language_for_file(input_path)))
|
|
756
784
|
current_char_count += char_count
|
|
757
785
|
break
|
|
758
786
|
elif file_choice == 'q':
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
kopipasta/main.py,sha256=saG3HuptoAg7kPz-VqiGF9Bh3oznMmATxsQbRpZc_ZY,33134
|
|
3
|
+
kopipasta-0.16.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
+
kopipasta-0.16.0.dist-info/METADATA,sha256=e6kZrq96erWpl2HRyGiknXTRHQdnERd3eWccFEK0kC0,5646
|
|
5
|
+
kopipasta-0.16.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
6
|
+
kopipasta-0.16.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
+
kopipasta-0.16.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
+
kopipasta-0.16.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
kopipasta/main.py,sha256=xoH5I0vAbKbz51trNtqItc-d72s2J2f9b7dIKTTFSCE,31094
|
|
3
|
-
kopipasta-0.14.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
-
kopipasta-0.14.0.dist-info/METADATA,sha256=onX0C9YmxAPItIrKTlJfoSggPPKZJNs-M1bGaXIlCTY,5646
|
|
5
|
-
kopipasta-0.14.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
6
|
-
kopipasta-0.14.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
-
kopipasta-0.14.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
-
kopipasta-0.14.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|