kopipasta 0.19.0__py3-none-any.whl → 0.21.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 +70 -30
- {kopipasta-0.19.0.dist-info → kopipasta-0.21.0.dist-info}/METADATA +21 -5
- kopipasta-0.21.0.dist-info/RECORD +8 -0
- {kopipasta-0.19.0.dist-info → kopipasta-0.21.0.dist-info}/WHEEL +1 -1
- kopipasta-0.19.0.dist-info/RECORD +0 -8
- {kopipasta-0.19.0.dist-info → kopipasta-0.21.0.dist-info}/LICENSE +0 -0
- {kopipasta-0.19.0.dist-info → kopipasta-0.21.0.dist-info}/entry_points.txt +0 -0
- {kopipasta-0.19.0.dist-info → kopipasta-0.21.0.dist-info}/top_level.txt +0 -0
kopipasta/main.py
CHANGED
|
@@ -827,37 +827,66 @@ def main():
|
|
|
827
827
|
web_contents[input_path] = (file_tuple, content)
|
|
828
828
|
current_char_count += len(content)
|
|
829
829
|
print(f"Added {'snippet of ' if is_snippet else ''}web content from: {input_path}")
|
|
830
|
+
print_char_count(current_char_count)
|
|
830
831
|
elif os.path.isfile(input_path):
|
|
832
|
+
# Handle files provided directly via command line
|
|
831
833
|
if not is_ignored(input_path, ignore_patterns) and not is_binary(input_path):
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
834
|
+
file_size = os.path.getsize(input_path)
|
|
835
|
+
file_size_readable = get_human_readable_size(file_size)
|
|
836
|
+
file_char_estimate = file_size
|
|
837
|
+
language = get_language_for_file(input_path)
|
|
838
|
+
|
|
839
|
+
if is_large_file(input_path):
|
|
840
|
+
print(f"\nFile {input_path} ({file_size_readable}, ~{file_char_estimate} chars) is large.")
|
|
841
|
+
print("Preview (first ~50 lines or 4KB):")
|
|
842
|
+
print(get_colored_file_snippet(input_path))
|
|
843
|
+
print("-" * 40)
|
|
844
|
+
while True:
|
|
845
|
+
print_char_count(current_char_count)
|
|
846
|
+
choice = input(f"How to include large file {input_path}? (f)ull / (s)nippet / (p)atches / (n)o skip: ").lower()
|
|
847
|
+
if choice == 'f':
|
|
848
|
+
files_to_include.append((input_path, False, None, language))
|
|
849
|
+
current_char_count += file_char_estimate
|
|
850
|
+
print(f"Added full file: {input_path}")
|
|
851
|
+
break
|
|
852
|
+
elif choice == 's':
|
|
853
|
+
snippet_content = get_file_snippet(input_path)
|
|
854
|
+
files_to_include.append((input_path, True, None, language))
|
|
855
|
+
current_char_count += len(snippet_content)
|
|
856
|
+
print(f"Added snippet of file: {input_path}")
|
|
857
|
+
break
|
|
858
|
+
elif choice == 'p':
|
|
859
|
+
chunks, char_count = select_file_patches(input_path)
|
|
860
|
+
if chunks:
|
|
861
|
+
files_to_include.append((input_path, False, chunks, language))
|
|
862
|
+
current_char_count += char_count
|
|
863
|
+
print(f"Added selected patches from file: {input_path}")
|
|
864
|
+
else:
|
|
865
|
+
print(f"No patches selected for {input_path}. Skipping file.")
|
|
866
|
+
break
|
|
867
|
+
elif choice == 'n':
|
|
868
|
+
print(f"Skipped large file: {input_path}")
|
|
869
|
+
break
|
|
841
870
|
else:
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
break
|
|
853
|
-
elif file_choice == 'q':
|
|
854
|
-
print("Quitting.")
|
|
855
|
-
return
|
|
856
|
-
else:
|
|
857
|
-
print("Invalid choice. Please enter 'y', 'n', 'p', or 'q'.")
|
|
871
|
+
print("Invalid choice. Please enter 'f', 's', 'p', or 'n'.")
|
|
872
|
+
else:
|
|
873
|
+
# Automatically include non-large files
|
|
874
|
+
files_to_include.append((input_path, False, None, language))
|
|
875
|
+
current_char_count += file_char_estimate
|
|
876
|
+
print(f"Added file: {input_path} ({file_size_readable})")
|
|
877
|
+
|
|
878
|
+
# Display current count after processing the file
|
|
879
|
+
print_char_count(current_char_count)
|
|
880
|
+
|
|
858
881
|
else:
|
|
859
|
-
|
|
882
|
+
if is_ignored(input_path, ignore_patterns):
|
|
883
|
+
print(f"Ignoring file based on ignore patterns: {input_path}")
|
|
884
|
+
elif is_binary(input_path):
|
|
885
|
+
print(f"Ignoring binary file: {input_path}")
|
|
886
|
+
else:
|
|
887
|
+
print(f"Ignoring file: {input_path}") # Should not happen if logic is correct, but fallback.
|
|
860
888
|
elif os.path.isdir(input_path):
|
|
889
|
+
print(f"\nProcessing directory specified directly: {input_path}")
|
|
861
890
|
dir_files, dir_processed, current_char_count = process_directory(input_path, ignore_patterns, current_char_count)
|
|
862
891
|
files_to_include.extend(dir_files)
|
|
863
892
|
processed_dirs.update(dir_processed)
|
|
@@ -869,19 +898,29 @@ def main():
|
|
|
869
898
|
return
|
|
870
899
|
|
|
871
900
|
print("\nFile and web content selection complete.")
|
|
872
|
-
print_char_count(current_char_count)
|
|
873
|
-
print(f"Summary: Added {len(files_to_include)} files
|
|
901
|
+
print_char_count(current_char_count) # Print final count before prompt generation
|
|
902
|
+
print(f"Summary: Added {len(files_to_include)} files and {len(web_contents)} web sources.")
|
|
874
903
|
|
|
875
904
|
prompt_template, cursor_position = generate_prompt_template(files_to_include, ignore_patterns, web_contents, env_vars)
|
|
876
905
|
|
|
877
906
|
if args.task:
|
|
878
907
|
task_description = args.task
|
|
879
|
-
|
|
908
|
+
# Insert task description before "## Task Instructions"
|
|
909
|
+
task_marker = "## Task Instructions\n\n"
|
|
910
|
+
insertion_point = prompt_template.find(task_marker)
|
|
911
|
+
if insertion_point != -1:
|
|
912
|
+
final_prompt = prompt_template[:insertion_point + len(task_marker)] + task_description + "\n\n" + prompt_template[insertion_point + len(task_marker):]
|
|
913
|
+
else: # Fallback if marker not found
|
|
914
|
+
final_prompt = prompt_template[:cursor_position] + task_description + prompt_template[cursor_position:]
|
|
915
|
+
print("\nUsing task description from -t argument.")
|
|
880
916
|
else:
|
|
917
|
+
print("\nOpening editor for task instructions...")
|
|
881
918
|
final_prompt = open_editor_for_input(prompt_template, cursor_position)
|
|
882
919
|
|
|
883
920
|
print("\n\nGenerated prompt:")
|
|
921
|
+
print("-" * 80)
|
|
884
922
|
print(final_prompt)
|
|
923
|
+
print("-" * 80)
|
|
885
924
|
|
|
886
925
|
# Copy the prompt to clipboard
|
|
887
926
|
try:
|
|
@@ -892,7 +931,8 @@ def main():
|
|
|
892
931
|
final_token_estimate = final_char_count // 4
|
|
893
932
|
print(f"Prompt has been copied to clipboard. Final size: {final_char_count} characters (~ {final_token_estimate} tokens)")
|
|
894
933
|
except pyperclip.PyperclipException as e:
|
|
895
|
-
print(f"Failed to copy to clipboard: {e}")
|
|
934
|
+
print(f"\nWarning: Failed to copy to clipboard: {e}")
|
|
935
|
+
print("You can manually copy the prompt above.")
|
|
896
936
|
|
|
897
937
|
if __name__ == "__main__":
|
|
898
938
|
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kopipasta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.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,15 +19,18 @@ 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
|
|
22
|
+
Requires-Dist: pyperclip ==1.9.0
|
|
23
|
+
Requires-Dist: requests ==2.32.3
|
|
24
|
+
Requires-Dist: Pygments ==2.18.0
|
|
24
25
|
|
|
25
26
|
# kopipasta
|
|
26
27
|
|
|
27
28
|
[](https://pypi.python.org/pypi/kopipasta)
|
|
28
29
|
[](http://pepy.tech/project/kopipasta)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
Beyond TAB TAB TAB. Giving you full control of the context.
|
|
32
|
+
|
|
33
|
+
A CLI tool for generating code task prompts with project structure and file contents, using an interactive editor-based workflow. OR a very easy way to give a large context to an LLM.
|
|
31
34
|
|
|
32
35
|
<img src="kopipasta.jpg" alt="kopipasta" width="300">
|
|
33
36
|
|
|
@@ -51,7 +54,7 @@ kopipasta [files_or_directories_or_urls]
|
|
|
51
54
|
|
|
52
55
|
Replace `[files_or_directories_or_urls]` with the paths to the files or directories you want to include in the prompt, as well as any web URLs you want to fetch content from.
|
|
53
56
|
|
|
54
|
-
Example:
|
|
57
|
+
Example input:
|
|
55
58
|
```bash
|
|
56
59
|
kopipasta src/ config.json https://example.com/api-docs
|
|
57
60
|
```
|
|
@@ -76,3 +79,16 @@ The generated prompt will be displayed in the console and automatically copied t
|
|
|
76
79
|
- Allows interactive selection of files to include
|
|
77
80
|
- Supports various file types with syntax highlighting in the selection process
|
|
78
81
|
- Automatically copies the generated prompt to the clipboard
|
|
82
|
+
|
|
83
|
+
## Real life example
|
|
84
|
+
|
|
85
|
+
Context:
|
|
86
|
+
I had a bug that setup.py did not have all the dependencies. I wanted to make things easier:
|
|
87
|
+
|
|
88
|
+
1. `kopipasta -t "setup.py should take requirements from requirements.txt" requirements.txt setup.py`
|
|
89
|
+
2. Opened the service that provides the best LLM currently.
|
|
90
|
+
3. Pasted the prompt to their chat.
|
|
91
|
+
4. Reviewed the first message and typed "Proceed".
|
|
92
|
+
5. Got back the code that fixed the issue.
|
|
93
|
+
|
|
94
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
kopipasta/main.py,sha256=69m3sVRwqYfEsqk_IukhfrC_ZIx-bjvNc1z_hZQ2zrM,39318
|
|
3
|
+
kopipasta-0.21.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
+
kopipasta-0.21.0.dist-info/METADATA,sha256=NNROfNVFYI-1tllgg2_O-Cfua4cNKHXKlDN8GPhYMu4,3716
|
|
5
|
+
kopipasta-0.21.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
6
|
+
kopipasta-0.21.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
+
kopipasta-0.21.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
+
kopipasta-0.21.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
kopipasta/main.py,sha256=zHCgL8dIZ4qMDzaN4EgUltH7wRT8UVvLn0Fq5ZgEZ8s,36904
|
|
3
|
-
kopipasta-0.19.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
4
|
-
kopipasta-0.19.0.dist-info/METADATA,sha256=ipQ4jhKhnSgb2YKffbZZdBcx7FBZYodmS_g95Dx3aAY,3147
|
|
5
|
-
kopipasta-0.19.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
6
|
-
kopipasta-0.19.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
7
|
-
kopipasta-0.19.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
8
|
-
kopipasta-0.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|