kopipasta 0.13.0__py3-none-any.whl → 0.15.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 +64 -101
- {kopipasta-0.13.0.dist-info → kopipasta-0.15.0.dist-info}/METADATA +1 -1
- kopipasta-0.15.0.dist-info/RECORD +8 -0
- kopipasta-0.13.0.dist-info/RECORD +0 -8
- {kopipasta-0.13.0.dist-info → kopipasta-0.15.0.dist-info}/LICENSE +0 -0
- {kopipasta-0.13.0.dist-info → kopipasta-0.15.0.dist-info}/WHEEL +0 -0
- {kopipasta-0.13.0.dist-info → kopipasta-0.15.0.dist-info}/entry_points.txt +0 -0
- {kopipasta-0.13.0.dist-info → kopipasta-0.15.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, 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':
|
|
@@ -520,7 +520,7 @@ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
520
520
|
print("Invalid choice. Please try again.")
|
|
521
521
|
|
|
522
522
|
def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
523
|
-
files_to_include = []
|
|
523
|
+
files_to_include: List[FileTuple] = []
|
|
524
524
|
processed_dirs = set()
|
|
525
525
|
|
|
526
526
|
for root, dirs, files in os.walk(directory):
|
|
@@ -530,15 +530,26 @@ def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
|
530
530
|
if root in processed_dirs:
|
|
531
531
|
continue
|
|
532
532
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
533
|
+
print(f"\nExploring directory: {root}")
|
|
534
|
+
choice = input("(y)es explore / (n)o skip / (q)uit? ").lower()
|
|
535
|
+
if choice == 'y':
|
|
536
|
+
selected_files, current_char_count = select_files_in_directory(root, ignore_patterns, current_char_count)
|
|
537
|
+
for file_tuple in selected_files:
|
|
538
|
+
if len(file_tuple) == 3:
|
|
539
|
+
f, use_snippet, chunks = file_tuple
|
|
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))
|
|
544
|
+
processed_dirs.add(root)
|
|
545
|
+
elif choice == 'n':
|
|
546
|
+
dirs[:] = [] # Skip all subdirectories
|
|
547
|
+
continue
|
|
548
|
+
elif choice == 'q':
|
|
549
|
+
break
|
|
550
|
+
else:
|
|
551
|
+
print("Invalid choice. Skipping this directory.")
|
|
552
|
+
continue
|
|
542
553
|
|
|
543
554
|
return files_to_include, processed_dirs, current_char_count
|
|
544
555
|
|
|
@@ -659,30 +670,29 @@ def handle_env_variables(content, env_vars):
|
|
|
659
670
|
|
|
660
671
|
return content
|
|
661
672
|
|
|
662
|
-
def generate_prompt(files_to_include, ignore_patterns, web_contents, env_vars):
|
|
673
|
+
def generate_prompt(files_to_include: List[FileTuple], ignore_patterns: List[str], web_contents: Dict[str, Tuple[str, str]], env_vars: Dict[str, str]) -> str:
|
|
663
674
|
prompt = "# Project Overview\n\n"
|
|
664
675
|
prompt += "## Project Structure\n\n"
|
|
665
676
|
prompt += "```\n"
|
|
666
677
|
prompt += get_project_structure(ignore_patterns)
|
|
667
678
|
prompt += "\n```\n\n"
|
|
668
679
|
prompt += "## File Contents\n\n"
|
|
669
|
-
for
|
|
670
|
-
if len(file_tuple) == 4:
|
|
671
|
-
file, content, is_snippet, content_type = file_tuple
|
|
672
|
-
chunks = None
|
|
673
|
-
else:
|
|
674
|
-
file, content, is_snippet, content_type, chunks = file_tuple
|
|
680
|
+
for file, use_snippet, chunks, content_type in files_to_include:
|
|
675
681
|
relative_path = get_relative_path(file)
|
|
676
|
-
language =
|
|
682
|
+
language = content_type if content_type else get_language_for_file(file)
|
|
677
683
|
|
|
678
684
|
if chunks is not None:
|
|
679
685
|
prompt += f"### {relative_path} (selected patches)\n\n```{language}\n"
|
|
680
686
|
for chunk in chunks:
|
|
681
687
|
prompt += f"{chunk}\n"
|
|
682
688
|
prompt += "```\n\n"
|
|
689
|
+
elif use_snippet:
|
|
690
|
+
file_content = get_file_snippet(file)
|
|
691
|
+
prompt += f"### {relative_path} (snippet)\n\n```{language}\n{file_content}\n```\n\n"
|
|
683
692
|
else:
|
|
684
|
-
|
|
685
|
-
|
|
693
|
+
file_content = read_file_contents(file)
|
|
694
|
+
file_content = handle_env_variables(file_content, env_vars)
|
|
695
|
+
prompt += f"### {relative_path}\n\n```{language}\n{file_content}\n```\n\n"
|
|
686
696
|
|
|
687
697
|
if web_contents:
|
|
688
698
|
prompt += "## Web Content\n\n"
|
|
@@ -711,79 +721,38 @@ def main():
|
|
|
711
721
|
ignore_patterns = read_gitignore()
|
|
712
722
|
env_vars = read_env_file()
|
|
713
723
|
|
|
714
|
-
files_to_include = []
|
|
724
|
+
files_to_include:List[FileTuple] = []
|
|
725
|
+
processed_dirs = set()
|
|
715
726
|
web_contents = {}
|
|
716
|
-
|
|
717
|
-
def process_directory(directory):
|
|
718
|
-
files = [f for f in os.listdir(directory)
|
|
719
|
-
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))]
|
|
720
|
-
|
|
721
|
-
if not files:
|
|
722
|
-
return []
|
|
723
|
-
|
|
724
|
-
print(f"\nDirectory: {directory}")
|
|
725
|
-
print("Files:")
|
|
726
|
-
for file in files:
|
|
727
|
-
file_path = os.path.join(directory, file)
|
|
728
|
-
file_size = os.path.getsize(file_path)
|
|
729
|
-
file_size_readable = get_human_readable_size(file_size)
|
|
730
|
-
print(f"- {file} ({file_size_readable})")
|
|
731
|
-
|
|
732
|
-
while True:
|
|
733
|
-
choice = input("(y)es add all / (n)o ignore all / (s)elect individually / (q)uit? ").lower()
|
|
734
|
-
if choice == 'y':
|
|
735
|
-
return [(os.path.join(directory, f), False) for f in files]
|
|
736
|
-
elif choice == 'n':
|
|
737
|
-
return []
|
|
738
|
-
elif choice == 's':
|
|
739
|
-
selected_files = []
|
|
740
|
-
for file in files:
|
|
741
|
-
file_path = os.path.join(directory, file)
|
|
742
|
-
while True:
|
|
743
|
-
file_choice = input(f"{file} (y/n/p/q)? ").lower()
|
|
744
|
-
if file_choice == 'y':
|
|
745
|
-
selected_files.append((file_path, False))
|
|
746
|
-
break
|
|
747
|
-
elif file_choice == 'n':
|
|
748
|
-
break
|
|
749
|
-
elif file_choice == 'p':
|
|
750
|
-
chunks, _ = select_file_patches(file_path)
|
|
751
|
-
if chunks:
|
|
752
|
-
selected_files.append((file_path, True, chunks))
|
|
753
|
-
break
|
|
754
|
-
elif file_choice == 'q':
|
|
755
|
-
return selected_files
|
|
756
|
-
else:
|
|
757
|
-
print("Invalid choice. Please enter 'y', 'n', 'p', or 'q'.")
|
|
758
|
-
return selected_files
|
|
759
|
-
elif choice == 'q':
|
|
760
|
-
return []
|
|
761
|
-
else:
|
|
762
|
-
print("Invalid choice. Please try again.")
|
|
727
|
+
current_char_count = 0
|
|
763
728
|
|
|
764
729
|
for input_path in args.inputs:
|
|
765
730
|
if input_path.startswith(('http://', 'https://')):
|
|
766
|
-
|
|
767
|
-
if
|
|
768
|
-
|
|
769
|
-
|
|
731
|
+
full_content, snippet = fetch_web_content(input_path)
|
|
732
|
+
if full_content:
|
|
733
|
+
web_contents[input_path] = (full_content, snippet)
|
|
734
|
+
current_char_count += len(snippet if len(full_content) > 10000 else full_content)
|
|
770
735
|
print(f"Added web content from: {input_path}")
|
|
771
736
|
elif os.path.isfile(input_path):
|
|
772
737
|
if not is_ignored(input_path, ignore_patterns) and not is_binary(input_path):
|
|
773
738
|
while True:
|
|
774
739
|
file_choice = input(f"{input_path} (y)es include / (n)o skip / (p)atches / (q)uit? ").lower()
|
|
775
740
|
if file_choice == 'y':
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
741
|
+
use_snippet = is_large_file(input_path)
|
|
742
|
+
files_to_include.append((input_path, use_snippet, None, get_language_for_file(input_path)))
|
|
743
|
+
if use_snippet:
|
|
744
|
+
current_char_count += len(get_file_snippet(input_path))
|
|
745
|
+
else:
|
|
746
|
+
current_char_count += os.path.getsize(input_path)
|
|
747
|
+
print(f"Added file: {input_path}{' (snippet)' if use_snippet else ''}")
|
|
780
748
|
break
|
|
781
749
|
elif file_choice == 'n':
|
|
782
750
|
break
|
|
783
751
|
elif file_choice == 'p':
|
|
784
|
-
chunks,
|
|
752
|
+
chunks, char_count = select_file_patches(input_path)
|
|
785
753
|
if chunks:
|
|
786
|
-
files_to_include.append((input_path,
|
|
754
|
+
files_to_include.append((input_path, False, chunks, get_language_for_file(input_path)))
|
|
755
|
+
current_char_count += char_count
|
|
787
756
|
break
|
|
788
757
|
elif file_choice == 'q':
|
|
789
758
|
print("Quitting.")
|
|
@@ -793,16 +762,9 @@ def main():
|
|
|
793
762
|
else:
|
|
794
763
|
print(f"Ignored file: {input_path}")
|
|
795
764
|
elif os.path.isdir(input_path):
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
file_path, use_snippet = file_info
|
|
800
|
-
content, content_type = read_file_content(file_path)
|
|
801
|
-
content, is_snippet = handle_content(content, content_type, file_path)
|
|
802
|
-
files_to_include.append((file_path, content, is_snippet, content_type))
|
|
803
|
-
else:
|
|
804
|
-
file_path, _, chunks = file_info
|
|
805
|
-
files_to_include.append((file_path, None, False, 'text', chunks))
|
|
765
|
+
dir_files, dir_processed, current_char_count = process_directory(input_path, ignore_patterns, current_char_count)
|
|
766
|
+
files_to_include.extend(dir_files)
|
|
767
|
+
processed_dirs.update(dir_processed)
|
|
806
768
|
else:
|
|
807
769
|
print(f"Warning: {input_path} is not a valid file, directory, or URL. Skipping.")
|
|
808
770
|
|
|
@@ -811,7 +773,8 @@ def main():
|
|
|
811
773
|
return
|
|
812
774
|
|
|
813
775
|
print("\nFile and web content selection complete.")
|
|
814
|
-
|
|
776
|
+
print_char_count(current_char_count)
|
|
777
|
+
print(f"Summary: Added {len(files_to_include)} files from {len(processed_dirs)} directories and {len(web_contents)} web sources.")
|
|
815
778
|
|
|
816
779
|
prompt = generate_prompt(files_to_include, ignore_patterns, web_contents, env_vars)
|
|
817
780
|
print("\n\nGenerated prompt:")
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
kopipasta/main.py,sha256=51k6jZXChwZY_CYVnqCqeY_hoHCAj3flN8qnr0vEVQI,31652
|
|
3
|
+
kopipasta-0.15.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
+
kopipasta-0.15.0.dist-info/METADATA,sha256=ehTVHvX8IWh7hISa0O3bxJqj4a7gBqX9H0JtU1xU2ys,5646
|
|
5
|
+
kopipasta-0.15.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
6
|
+
kopipasta-0.15.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
+
kopipasta-0.15.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
+
kopipasta-0.15.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
kopipasta/main.py,sha256=Z5_iI0dDp8WeRLp_FMDBPzNWrUiHNIteOcPl_fh7e4k,32860
|
|
3
|
-
kopipasta-0.13.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
-
kopipasta-0.13.0.dist-info/METADATA,sha256=80uGAXCQckifJnEkXCsgu5uNkFRFO7CWuwSi9wAVbDE,5646
|
|
5
|
-
kopipasta-0.13.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
6
|
-
kopipasta-0.13.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
-
kopipasta-0.13.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
-
kopipasta-0.13.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|