kopipasta 0.1.0__tar.gz → 0.2.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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mikko Korpela
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kopipasta
3
- Version: 0.1.0
3
+ Version: 0.2.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
@@ -21,10 +21,13 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: pyperclip==1.9.0
23
23
 
24
+
24
25
  # kopipasta
25
26
 
26
27
  A CLI tool to generate prompts with project structure and file contents.
27
28
 
29
+ <img src="kopipasta.jpg" alt="kopipasta" width="300">
30
+
28
31
  ## Installation
29
32
 
30
33
  You can install kopipasta using pipx (or pip):
@@ -45,7 +48,7 @@ Replace `[files_or_directories]` with the paths to the files or directories you
45
48
 
46
49
  Example:
47
50
  ```
48
- kopipasta . src/ config.json
51
+ kopipasta src/ config.json
49
52
  ```
50
53
 
51
54
  This will generate a prompt including the project structure and contents of the specified files and directories, ignoring files and directories typically excluded in version control (based on common .gitignore patterns).
@@ -59,6 +62,61 @@ The generated prompt will be displayed in the console and automatically copied t
59
62
  - Allows interactive selection of files to include
60
63
  - Automatically copies the generated prompt to the clipboard
61
64
 
65
+ ## Example output
66
+
67
+ ```bash
68
+ ❯ kopipasta .
69
+
70
+ Directory: .
71
+ Files:
72
+ - __init__.py
73
+ - main.py
74
+
75
+ (y)es add all / (n)o ignore all / (s)elect individually / (q)uit? s
76
+ __init__.py (y/n/q)? y
77
+ main.py (y/n/q)? n
78
+ Added 1 files from .
79
+
80
+ File selection complete.
81
+ Summary: Added 1 files from 1 directories.
82
+ Enter the task instructions: Do my work
83
+
84
+ Generated prompt:
85
+ # Project Overview
86
+
87
+ ## Summary of Included Files
88
+
89
+ - __init__.py
90
+
91
+ ## Project Structure
92
+
93
+ ```
94
+ |-- ./
95
+ |-- __init__.py
96
+ |-- main.py
97
+ ```
98
+
99
+ ## File Contents
100
+
101
+ ### __init__.py
102
+
103
+ ```python
104
+
105
+ ```
106
+
107
+ ## Task Instructions
108
+
109
+ Do my work
110
+
111
+ ## Task Analysis and Planning
112
+
113
+ Before starting, explain the task back to me in your own words. Ask for any clarifications if needed. Once you're clear, ask to proceed.
114
+
115
+ Then, outline a plan for the task. Finally, use your plan to complete the task.
116
+
117
+ Prompt has been copied to clipboard.
118
+ ```
119
+
62
120
  ## License
63
121
 
64
122
  This project is licensed under the MIT License.
@@ -0,0 +1,99 @@
1
+
2
+ # kopipasta
3
+
4
+ A CLI tool to generate prompts with project structure and file contents.
5
+
6
+ <img src="kopipasta.jpg" alt="kopipasta" width="300">
7
+
8
+ ## Installation
9
+
10
+ You can install kopipasta using pipx (or pip):
11
+
12
+ ```
13
+ pipx install kopipasta
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ To use kopipasta, run the following command in your terminal:
19
+
20
+ ```
21
+ kopipasta [files_or_directories]
22
+ ```
23
+
24
+ Replace `[files_or_directories]` with the paths to the files or directories you want to include in the prompt.
25
+
26
+ Example:
27
+ ```
28
+ kopipasta src/ config.json
29
+ ```
30
+
31
+ This will generate a prompt including the project structure and contents of the specified files and directories, ignoring files and directories typically excluded in version control (based on common .gitignore patterns).
32
+
33
+ The generated prompt will be displayed in the console and automatically copied to your clipboard.
34
+
35
+ ## Features
36
+
37
+ - Generates a structured prompt with project overview, file contents, and task instructions
38
+ - Ignores files and directories based on common .gitignore patterns
39
+ - Allows interactive selection of files to include
40
+ - Automatically copies the generated prompt to the clipboard
41
+
42
+ ## Example output
43
+
44
+ ```bash
45
+ ❯ kopipasta .
46
+
47
+ Directory: .
48
+ Files:
49
+ - __init__.py
50
+ - main.py
51
+
52
+ (y)es add all / (n)o ignore all / (s)elect individually / (q)uit? s
53
+ __init__.py (y/n/q)? y
54
+ main.py (y/n/q)? n
55
+ Added 1 files from .
56
+
57
+ File selection complete.
58
+ Summary: Added 1 files from 1 directories.
59
+ Enter the task instructions: Do my work
60
+
61
+ Generated prompt:
62
+ # Project Overview
63
+
64
+ ## Summary of Included Files
65
+
66
+ - __init__.py
67
+
68
+ ## Project Structure
69
+
70
+ ```
71
+ |-- ./
72
+ |-- __init__.py
73
+ |-- main.py
74
+ ```
75
+
76
+ ## File Contents
77
+
78
+ ### __init__.py
79
+
80
+ ```python
81
+
82
+ ```
83
+
84
+ ## Task Instructions
85
+
86
+ Do my work
87
+
88
+ ## Task Analysis and Planning
89
+
90
+ Before starting, explain the task back to me in your own words. Ask for any clarifications if needed. Once you're clear, ask to proceed.
91
+
92
+ Then, outline a plan for the task. Finally, use your plan to complete the task.
93
+
94
+ Prompt has been copied to clipboard.
95
+ ```
96
+
97
+ ## License
98
+
99
+ This project is licensed under the MIT License.
@@ -11,11 +11,17 @@ def read_gitignore():
11
11
  '.terraform', 'output', 'poetry.lock', 'package-lock.json', '.env',
12
12
  '*.log', '*.bak', '*.swp', '*.swo', '*.tmp', 'tmp', 'temp', 'logs',
13
13
  'build', 'target', '.DS_Store', 'Thumbs.db', '*.class', '*.jar',
14
- '*.war', '*.ear', '*.sqlite', '*.db', '.github', '.gitignore'
14
+ '*.war', '*.ear', '*.sqlite', '*.db', '.github', '.gitignore',
15
+ '*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp', '*.tiff',
16
+ '*.ico', '*.svg', '*.webp', '*.mp3', '*.mp4', '*.avi',
17
+ '*.mov', '*.wmv', '*.flv', '*.pdf', '*.doc', '*.docx',
18
+ '*.xls', '*.xlsx', '*.ppt', '*.pptx', '*.zip', '*.rar',
19
+ '*.tar', '*.gz', '*.7z', '*.exe', '*.dll', '*.so', '*.dylib'
15
20
  ]
16
21
  gitignore_patterns = default_ignore_patterns.copy()
17
22
 
18
23
  if os.path.exists('.gitignore'):
24
+ print(".gitignore detected.")
19
25
  with open('.gitignore', 'r') as file:
20
26
  for line in file:
21
27
  line = line.strip()
@@ -30,12 +36,23 @@ def is_ignored(path, ignore_patterns):
30
36
  return True
31
37
  return False
32
38
 
39
+ def is_binary(file_path):
40
+ try:
41
+ with open(file_path, 'rb') as file:
42
+ return b'\0' in file.read(1024)
43
+ except IOError:
44
+ return False
45
+
46
+ def get_human_readable_size(size):
47
+ for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
48
+ if size < 1024.0:
49
+ return f"{size:.2f} {unit}"
50
+ size /= 1024.0
51
+
33
52
  def get_project_structure(ignore_patterns):
34
53
  tree = []
35
54
  for root, dirs, files in os.walk('.'):
36
- # Remove ignored directories
37
55
  dirs[:] = [d for d in dirs if not is_ignored(os.path.join(root, d), ignore_patterns)]
38
- # Remove ignored files
39
56
  files = [f for f in files if not is_ignored(os.path.join(root, f), ignore_patterns)]
40
57
  level = root.replace('.', '').count(os.sep)
41
58
  indent = ' ' * 4 * level + '|-- '
@@ -77,104 +94,111 @@ def get_language_for_file(file_path):
77
94
  }
78
95
  return language_map.get(extension, '')
79
96
 
80
- def select_files_in_directory(directory, ignore_patterns):
97
+ def select_files_in_directory(directory, ignore_patterns, current_char_count=0):
81
98
  files = [f for f in os.listdir(directory)
82
- if os.path.isfile(os.path.join(directory, f)) and not is_ignored(os.path.join(directory, f), ignore_patterns)]
99
+ 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))]
83
100
 
84
101
  if not files:
85
- return []
102
+ return [], current_char_count
86
103
 
87
104
  print(f"\nDirectory: {directory}")
88
105
  print("Files:")
89
106
  for file in files:
90
- print(f"- {file}")
107
+ file_path = os.path.join(directory, file)
108
+ file_size = os.path.getsize(file_path)
109
+ file_size_readable = get_human_readable_size(file_size)
110
+ file_char_estimate = file_size # Assuming 1 byte ≈ 1 character for text files
111
+ file_token_estimate = file_char_estimate // 4
112
+ print(f"- {file} ({file_size_readable}, ~{file_char_estimate} chars, ~{file_token_estimate} tokens)")
91
113
 
92
114
  while True:
93
- choice = input("\n(y)es add all / (n)o ignore all / (s)elect individually / (q)uit? ").lower()
115
+ print_char_count(current_char_count)
116
+ choice = input("(y)es add all / (n)o ignore all / (s)elect individually / (q)uit? ").lower()
94
117
  if choice == 'y':
118
+ for file in files:
119
+ current_char_count += os.path.getsize(os.path.join(directory, file))
95
120
  print(f"Added all files from {directory}")
96
- return files
121
+ return files, current_char_count
97
122
  elif choice == 'n':
98
123
  print(f"Ignored all files from {directory}")
99
- return []
124
+ return [], current_char_count
100
125
  elif choice == 's':
101
126
  selected_files = []
102
127
  for file in files:
128
+ file_path = os.path.join(directory, file)
129
+ file_size = os.path.getsize(file_path)
130
+ file_size_readable = get_human_readable_size(file_size)
131
+ file_char_estimate = file_size
132
+ file_token_estimate = file_char_estimate // 4
103
133
  while True:
104
- file_choice = input(f"{file} (y/n/q)? ").lower()
134
+ if current_char_count > 0:
135
+ print_char_count(current_char_count)
136
+ file_choice = input(f"{file} ({file_size_readable}, ~{file_char_estimate} chars, ~{file_token_estimate} tokens) (y/n/q)? ").lower()
105
137
  if file_choice == 'y':
106
138
  selected_files.append(file)
139
+ current_char_count += file_char_estimate
107
140
  break
108
141
  elif file_choice == 'n':
109
142
  break
110
143
  elif file_choice == 'q':
111
144
  print(f"Quitting selection for {directory}")
112
- return selected_files
145
+ return selected_files, current_char_count
113
146
  else:
114
147
  print("Invalid choice. Please enter 'y', 'n', or 'q'.")
115
148
  print(f"Added {len(selected_files)} files from {directory}")
116
- return selected_files
149
+ return selected_files, current_char_count
117
150
  elif choice == 'q':
118
151
  print(f"Quitting selection for {directory}")
119
- return []
152
+ return [], current_char_count
120
153
  else:
121
154
  print("Invalid choice. Please try again.")
122
155
 
123
- def process_directory(directory, ignore_patterns):
156
+ def process_directory(directory, ignore_patterns, current_char_count=0):
124
157
  files_to_include = []
125
158
  processed_dirs = set()
126
159
 
127
160
  for root, dirs, files in os.walk(directory):
128
- # Remove ignored directories
129
161
  dirs[:] = [d for d in dirs if not is_ignored(os.path.join(root, d), ignore_patterns)]
130
- # Remove ignored files
131
- files = [f for f in files if not is_ignored(os.path.join(root, f), ignore_patterns)]
162
+ files = [f for f in files if not is_ignored(os.path.join(root, f), ignore_patterns) and not is_binary(os.path.join(root, f))]
132
163
 
133
164
  if root in processed_dirs:
134
165
  continue
135
166
 
136
- selected_files = select_files_in_directory(root, ignore_patterns)
167
+ selected_files, current_char_count = select_files_in_directory(root, ignore_patterns, current_char_count)
137
168
  full_paths = [os.path.join(root, f) for f in selected_files]
138
169
  files_to_include.extend(full_paths)
139
170
  processed_dirs.add(root)
140
171
 
141
- return files_to_include, processed_dirs
172
+ return files_to_include, processed_dirs, current_char_count
142
173
 
143
174
  def generate_prompt(files_to_include, ignore_patterns):
144
175
  prompt = "# Project Overview\n\n"
145
- prompt += "## Summary of Included Files\n\n"
146
- for file in files_to_include:
147
- relative_path = get_relative_path(file)
148
- prompt += f"- {relative_path}\n"
149
- prompt += "\n"
150
-
151
176
  prompt += "## Project Structure\n\n"
152
177
  prompt += "```\n"
153
178
  prompt += get_project_structure(ignore_patterns)
154
179
  prompt += "\n```\n\n"
155
-
156
180
  prompt += "## File Contents\n\n"
157
181
  for file in files_to_include:
158
182
  relative_path = get_relative_path(file)
159
183
  language = get_language_for_file(file)
160
- prompt += f"### {relative_path}\n\n"
161
- prompt += f"```{language}\n"
162
- prompt += read_file_contents(file)
163
- prompt += "\n```\n\n"
164
-
184
+ file_content = f"### {relative_path}\n\n```{language}\n{read_file_contents(file)}\n```\n\n"
185
+ prompt += file_content
165
186
  prompt += "## Task Instructions\n\n"
166
187
  task_instructions = input("Enter the task instructions: ")
167
188
  prompt += f"{task_instructions}\n\n"
168
-
169
189
  prompt += "## Task Analysis and Planning\n\n"
170
- prompt += (
190
+ analysis_text = (
171
191
  "Before starting, explain the task back to me in your own words. "
172
192
  "Ask for any clarifications if needed. Once you're clear, ask to proceed.\n\n"
173
193
  "Then, outline a plan for the task. Finally, use your plan to complete the task."
174
194
  )
175
-
195
+ prompt += analysis_text
176
196
  return prompt
177
197
 
198
+ def print_char_count(count):
199
+ token_estimate = count // 4
200
+ print(f"\rCurrent prompt size: {count} characters (~ {token_estimate} tokens)", flush=True)
201
+
178
202
  def main():
179
203
  parser = argparse.ArgumentParser(description="Generate a prompt with project structure and file contents.")
180
204
  parser.add_argument('inputs', nargs='+', help='Files or directories to include in the prompt')
@@ -184,16 +208,17 @@ def main():
184
208
 
185
209
  files_to_include = []
186
210
  processed_dirs = set()
211
+ current_char_count = 0
187
212
 
188
213
  for input_path in args.inputs:
189
214
  if os.path.isfile(input_path):
190
- if not is_ignored(input_path, ignore_patterns):
215
+ if not is_ignored(input_path, ignore_patterns) and not is_binary(input_path):
191
216
  files_to_include.append(input_path)
192
217
  print(f"Added file: {input_path}")
193
218
  else:
194
219
  print(f"Ignored file: {input_path}")
195
220
  elif os.path.isdir(input_path):
196
- dir_files, dir_processed = process_directory(input_path, ignore_patterns)
221
+ dir_files, dir_processed, current_char_count = process_directory(input_path, ignore_patterns, current_char_count)
197
222
  files_to_include.extend(dir_files)
198
223
  processed_dirs.update(dir_processed)
199
224
  else:
@@ -204,19 +229,23 @@ def main():
204
229
  return
205
230
 
206
231
  print("\nFile selection complete.")
232
+ print_char_count(current_char_count)
207
233
  print(f"Summary: Added {len(files_to_include)} files from {len(processed_dirs)} directories.")
208
234
 
209
235
  prompt = generate_prompt(files_to_include, ignore_patterns)
210
- print("\nGenerated prompt:")
236
+ print("\n\nGenerated prompt:")
211
237
  print(prompt)
212
238
 
213
239
  # Copy the prompt to clipboard
214
240
  try:
215
241
  pyperclip.copy(prompt)
216
- print("\nPrompt has been copied to clipboard.")
242
+ separator = "\n" + "=" * 40 + "\n☕🍝 Kopipasta Complete! 🍝☕\n" + "=" * 40 + "\n"
243
+ print(separator)
244
+ final_char_count = len(prompt)
245
+ final_token_estimate = final_char_count // 4
246
+ print(f"Prompt has been copied to clipboard. Final size: {final_char_count} characters (~ {final_token_estimate} tokens)")
217
247
  except pyperclip.PyperclipException as e:
218
248
  print(f"Failed to copy to clipboard: {e}")
219
249
 
220
250
  if __name__ == "__main__":
221
- main()
222
-
251
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kopipasta
3
- Version: 0.1.0
3
+ Version: 0.2.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
@@ -21,10 +21,13 @@ Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: pyperclip==1.9.0
23
23
 
24
+
24
25
  # kopipasta
25
26
 
26
27
  A CLI tool to generate prompts with project structure and file contents.
27
28
 
29
+ <img src="kopipasta.jpg" alt="kopipasta" width="300">
30
+
28
31
  ## Installation
29
32
 
30
33
  You can install kopipasta using pipx (or pip):
@@ -45,7 +48,7 @@ Replace `[files_or_directories]` with the paths to the files or directories you
45
48
 
46
49
  Example:
47
50
  ```
48
- kopipasta . src/ config.json
51
+ kopipasta src/ config.json
49
52
  ```
50
53
 
51
54
  This will generate a prompt including the project structure and contents of the specified files and directories, ignoring files and directories typically excluded in version control (based on common .gitignore patterns).
@@ -59,6 +62,61 @@ The generated prompt will be displayed in the console and automatically copied t
59
62
  - Allows interactive selection of files to include
60
63
  - Automatically copies the generated prompt to the clipboard
61
64
 
65
+ ## Example output
66
+
67
+ ```bash
68
+ ❯ kopipasta .
69
+
70
+ Directory: .
71
+ Files:
72
+ - __init__.py
73
+ - main.py
74
+
75
+ (y)es add all / (n)o ignore all / (s)elect individually / (q)uit? s
76
+ __init__.py (y/n/q)? y
77
+ main.py (y/n/q)? n
78
+ Added 1 files from .
79
+
80
+ File selection complete.
81
+ Summary: Added 1 files from 1 directories.
82
+ Enter the task instructions: Do my work
83
+
84
+ Generated prompt:
85
+ # Project Overview
86
+
87
+ ## Summary of Included Files
88
+
89
+ - __init__.py
90
+
91
+ ## Project Structure
92
+
93
+ ```
94
+ |-- ./
95
+ |-- __init__.py
96
+ |-- main.py
97
+ ```
98
+
99
+ ## File Contents
100
+
101
+ ### __init__.py
102
+
103
+ ```python
104
+
105
+ ```
106
+
107
+ ## Task Instructions
108
+
109
+ Do my work
110
+
111
+ ## Task Analysis and Planning
112
+
113
+ Before starting, explain the task back to me in your own words. Ask for any clarifications if needed. Once you're clear, ask to proceed.
114
+
115
+ Then, outline a plan for the task. Finally, use your plan to complete the task.
116
+
117
+ Prompt has been copied to clipboard.
118
+ ```
119
+
62
120
  ## License
63
121
 
64
122
  This project is licensed under the MIT License.
@@ -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.1.0",
8
+ version="0.2.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",
kopipasta-0.1.0/LICENSE DELETED
File without changes
kopipasta-0.1.0/README.md DELETED
@@ -1,41 +0,0 @@
1
- # kopipasta
2
-
3
- A CLI tool to generate prompts with project structure and file contents.
4
-
5
- ## Installation
6
-
7
- You can install kopipasta using pipx (or pip):
8
-
9
- ```
10
- pipx install kopipasta
11
- ```
12
-
13
- ## Usage
14
-
15
- To use kopipasta, run the following command in your terminal:
16
-
17
- ```
18
- kopipasta [files_or_directories]
19
- ```
20
-
21
- Replace `[files_or_directories]` with the paths to the files or directories you want to include in the prompt.
22
-
23
- Example:
24
- ```
25
- kopipasta . src/ config.json
26
- ```
27
-
28
- This will generate a prompt including the project structure and contents of the specified files and directories, ignoring files and directories typically excluded in version control (based on common .gitignore patterns).
29
-
30
- The generated prompt will be displayed in the console and automatically copied to your clipboard.
31
-
32
- ## Features
33
-
34
- - Generates a structured prompt with project overview, file contents, and task instructions
35
- - Ignores files and directories based on common .gitignore patterns
36
- - Allows interactive selection of files to include
37
- - Automatically copies the generated prompt to the clipboard
38
-
39
- ## License
40
-
41
- This project is licensed under the MIT License.
File without changes
File without changes
File without changes