kopipasta 0.15.0__tar.gz → 0.17.0__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.
Potentially problematic release.
This version of kopipasta might be problematic. Click here for more details.
- {kopipasta-0.15.0/kopipasta.egg-info → kopipasta-0.17.0}/PKG-INFO +1 -1
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta/main.py +77 -27
- {kopipasta-0.15.0 → kopipasta-0.17.0/kopipasta.egg-info}/PKG-INFO +1 -1
- {kopipasta-0.15.0 → kopipasta-0.17.0}/setup.py +1 -1
- {kopipasta-0.15.0 → kopipasta-0.17.0}/LICENSE +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/MANIFEST.in +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/README.md +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta/__init__.py +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta.egg-info/SOURCES.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta.egg-info/dependency_links.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta.egg-info/entry_points.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta.egg-info/requires.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/kopipasta.egg-info/top_level.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/requirements.txt +0 -0
- {kopipasta-0.15.0 → kopipasta-0.17.0}/setup.cfg +0 -0
|
@@ -5,7 +5,7 @@ import json
|
|
|
5
5
|
import os
|
|
6
6
|
import argparse
|
|
7
7
|
import re
|
|
8
|
-
from typing import Dict, List, Optional, Tuple
|
|
8
|
+
from typing import Dict, List, Optional, Set, Tuple
|
|
9
9
|
import pyperclip
|
|
10
10
|
import fnmatch
|
|
11
11
|
|
|
@@ -519,9 +519,9 @@ def select_files_in_directory(directory: str, ignore_patterns: List[str], curren
|
|
|
519
519
|
else:
|
|
520
520
|
print("Invalid choice. Please try again.")
|
|
521
521
|
|
|
522
|
-
def process_directory(directory, ignore_patterns, current_char_count=0):
|
|
522
|
+
def process_directory(directory: str, ignore_patterns: List[str], current_char_count: int = 0) -> Tuple[List[FileTuple], Set[str], int]:
|
|
523
523
|
files_to_include: List[FileTuple] = []
|
|
524
|
-
processed_dirs = set()
|
|
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,7 +671,7 @@ def handle_env_variables(content, env_vars):
|
|
|
670
671
|
|
|
671
672
|
return content
|
|
672
673
|
|
|
673
|
-
def generate_prompt(files_to_include: List[FileTuple], ignore_patterns: List[str], web_contents: Dict[str, Tuple[
|
|
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"
|
|
@@ -696,7 +697,8 @@ def generate_prompt(files_to_include: List[FileTuple], ignore_patterns: List[str
|
|
|
696
697
|
|
|
697
698
|
if web_contents:
|
|
698
699
|
prompt += "## Web Content\n\n"
|
|
699
|
-
for url, (
|
|
700
|
+
for url, (file_tuple, content) in web_contents.items():
|
|
701
|
+
_, is_snippet, _, content_type = file_tuple
|
|
700
702
|
content = handle_env_variables(content, env_vars)
|
|
701
703
|
language = content_type if content_type in ['json', 'csv'] else ''
|
|
702
704
|
prompt += f"### {url}{' (snippet)' if is_snippet else ''}\n\n```{language}\n{content}\n```\n\n"
|
|
@@ -704,15 +706,36 @@ def generate_prompt(files_to_include: List[FileTuple], ignore_patterns: List[str
|
|
|
704
706
|
prompt += "## Task Instructions\n\n"
|
|
705
707
|
task_instructions = input("Enter the task instructions: ")
|
|
706
708
|
prompt += f"{task_instructions}\n\n"
|
|
707
|
-
prompt += "##
|
|
709
|
+
prompt += "## Instructions for Achieving the Task\n\n"
|
|
708
710
|
analysis_text = (
|
|
709
|
-
"
|
|
710
|
-
"
|
|
711
|
-
"
|
|
711
|
+
"1. **Confirm and Understand the Task**:\n"
|
|
712
|
+
" - Rephrase the task in your own words to ensure understanding.\n"
|
|
713
|
+
" - Ask for any necessary clarifications.\n"
|
|
714
|
+
" - Once everything is clear, ask to proceed.\n\n"
|
|
715
|
+
"2. **Outline a Plan**:\n"
|
|
716
|
+
" - Provide a brief plan on how to approach the task.\n"
|
|
717
|
+
" - Make minimal incremental changes to maintain a working codebase at each step.\n"
|
|
718
|
+
" - This is an iterative process aimed at achieving the task step by step.\n\n"
|
|
719
|
+
"3. **Implement Changes Iteratively**:\n"
|
|
720
|
+
" - Apply changes in small, manageable increments.\n"
|
|
721
|
+
" - Ensure the codebase remains functional after each change.\n"
|
|
722
|
+
" - After each increment, verify stability before proceeding to the next step.\n\n"
|
|
723
|
+
"4. **Present Code Changes Clearly**:\n"
|
|
724
|
+
" - Specify the file being modified at the beginning of each code block.\n"
|
|
725
|
+
" - Format changes for clarity:\n"
|
|
726
|
+
" - For small changes: Show only the changed lines with clear comments.\n"
|
|
727
|
+
" - For larger changes: Provide the full new implementation of changed parts, using placeholders like `'// ... (rest of the function)'` for unchanged code.\n"
|
|
728
|
+
" - Provide context by including important unchanged parts as needed.\n"
|
|
729
|
+
" - Use clear comments to explain the changes and reference old code if helpful.\n\n"
|
|
730
|
+
"5. **Encourage User Testing and Collaboration**:\n"
|
|
731
|
+
" - Ask the user to test the code on their machine after each change.\n"
|
|
732
|
+
" - If debugging is needed, include debugging messages in the code.\n"
|
|
733
|
+
" - Request the user to share any error messages or outputs from debugging to assist further.\n"
|
|
712
734
|
)
|
|
713
735
|
prompt += analysis_text
|
|
714
736
|
return prompt
|
|
715
737
|
|
|
738
|
+
|
|
716
739
|
def main():
|
|
717
740
|
parser = argparse.ArgumentParser(description="Generate a prompt with project structure, file contents, and web content.")
|
|
718
741
|
parser.add_argument('inputs', nargs='+', help='Files, directories, or URLs to include in the prompt')
|
|
@@ -721,18 +744,45 @@ def main():
|
|
|
721
744
|
ignore_patterns = read_gitignore()
|
|
722
745
|
env_vars = read_env_file()
|
|
723
746
|
|
|
724
|
-
files_to_include:List[FileTuple] = []
|
|
747
|
+
files_to_include: List[FileTuple] = []
|
|
725
748
|
processed_dirs = set()
|
|
726
|
-
web_contents = {}
|
|
749
|
+
web_contents: Dict[str, Tuple[FileTuple, str]] = {}
|
|
727
750
|
current_char_count = 0
|
|
728
751
|
|
|
729
752
|
for input_path in args.inputs:
|
|
730
753
|
if input_path.startswith(('http://', 'https://')):
|
|
731
|
-
|
|
732
|
-
if
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
754
|
+
result = fetch_web_content(input_path)
|
|
755
|
+
if result:
|
|
756
|
+
file_tuple, full_content, snippet = result
|
|
757
|
+
is_large = len(full_content) > 10000
|
|
758
|
+
if is_large:
|
|
759
|
+
print(f"\nContent from {input_path} is large. Here's a snippet:\n")
|
|
760
|
+
print(snippet)
|
|
761
|
+
print("\n" + "-"*40 + "\n")
|
|
762
|
+
|
|
763
|
+
while True:
|
|
764
|
+
choice = input("Use (f)ull content or (s)nippet? ").lower()
|
|
765
|
+
if choice in ['f', 's']:
|
|
766
|
+
break
|
|
767
|
+
print("Invalid choice. Please enter 'f' or 's'.")
|
|
768
|
+
|
|
769
|
+
if choice == 'f':
|
|
770
|
+
content = full_content
|
|
771
|
+
is_snippet = False
|
|
772
|
+
print("Using full content.")
|
|
773
|
+
else:
|
|
774
|
+
content = snippet
|
|
775
|
+
is_snippet = True
|
|
776
|
+
print("Using snippet.")
|
|
777
|
+
else:
|
|
778
|
+
content = full_content
|
|
779
|
+
is_snippet = False
|
|
780
|
+
print(f"Content from {input_path} is not large. Using full content.")
|
|
781
|
+
|
|
782
|
+
file_tuple = (file_tuple[0], is_snippet, file_tuple[2], file_tuple[3])
|
|
783
|
+
web_contents[input_path] = (file_tuple, content)
|
|
784
|
+
current_char_count += len(content)
|
|
785
|
+
print(f"Added {'snippet of ' if is_snippet else ''}web content from: {input_path}")
|
|
736
786
|
elif os.path.isfile(input_path):
|
|
737
787
|
if not is_ignored(input_path, ignore_patterns) and not is_binary(input_path):
|
|
738
788
|
while True:
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="kopipasta",
|
|
8
|
-
version="0.
|
|
8
|
+
version="0.17.0",
|
|
9
9
|
author="Mikko Korpela",
|
|
10
10
|
author_email="mikko.korpela@gmail.com",
|
|
11
11
|
description="A CLI tool to generate prompts with project structure and file contents",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|