kopipasta 0.41.0__tar.gz → 0.45.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.
- {kopipasta-0.41.0/kopipasta.egg-info → kopipasta-0.45.0}/PKG-INFO +51 -30
- {kopipasta-0.41.0 → kopipasta-0.45.0}/README.md +50 -29
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/main.py +7 -4
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/ops.py +11 -37
- kopipasta-0.45.0/kopipasta/patcher.py +447 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/prompt.py +39 -17
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/tree_selector.py +21 -18
- {kopipasta-0.41.0 → kopipasta-0.45.0/kopipasta.egg-info}/PKG-INFO +51 -30
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta.egg-info/SOURCES.txt +2 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/setup.py +1 -1
- {kopipasta-0.41.0 → kopipasta-0.45.0}/tests/test_patcher_edge_cases.py +6 -2
- {kopipasta-0.41.0 → kopipasta-0.45.0}/tests/test_patcher_regex.py +40 -7
- kopipasta-0.45.0/tests/test_patcher_repro.py +100 -0
- kopipasta-0.45.0/tests/test_patcher_repro_failures.py +118 -0
- kopipasta-0.41.0/kopipasta/patcher.py +0 -245
- {kopipasta-0.41.0 → kopipasta-0.45.0}/LICENSE +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/MANIFEST.in +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/__init__.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/cache.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/file.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta/import_parser.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta.egg-info/dependency_links.txt +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta.egg-info/entry_points.txt +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta.egg-info/requires.txt +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/kopipasta.egg-info/top_level.txt +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/requirements.txt +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/setup.cfg +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/tests/test_file.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/tests/test_patcher.py +0 -0
- {kopipasta-0.41.0 → kopipasta-0.45.0}/tests/test_tree_selector.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: kopipasta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.45.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
|
|
@@ -35,27 +35,41 @@ Requires-Dist: prompt-toolkit==3.0.52
|
|
|
35
35
|
|
|
36
36
|
A CLI tool for taking **full, transparent control** of your prompt. No black boxes.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
```text
|
|
39
|
+
➜ ~ kopipasta
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
📁 Project Files
|
|
42
|
+
|-- 📂 src/
|
|
43
|
+
| |-- ● 📄 main.py (4.2 KB)
|
|
44
|
+
| |-- ○ 📄 utils.py (1.5 KB)
|
|
45
|
+
|-- ○ 📄 README.md (2.1 KB)
|
|
41
46
|
|
|
42
|
-
|
|
47
|
+
Current: src/main.py | Selected: 1 full | ~4,200 chars
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## You Control the Context
|
|
43
51
|
|
|
44
|
-
Many AI coding assistants
|
|
52
|
+
Many AI coding assistants automatically find what *they think* is relevant context. This is a black box. When the LLM gives a bad answer, you can't debug it because you don't know what context it was actually given.
|
|
45
53
|
|
|
46
|
-
**`kopipasta` is the opposite.** I built it
|
|
54
|
+
**`kopipasta` is the opposite.** I built it on the principle of **explicit context control**. You are in the driver's seat. You decide *exactly* what files, functions, and snippets go into the prompt.
|
|
47
55
|
|
|
48
56
|
It's a "smart copy" command for your project, not a magic wand.
|
|
49
57
|
|
|
50
58
|
## How It Works
|
|
51
59
|
|
|
52
|
-
The workflow is
|
|
60
|
+
The workflow is a fast, iterative cycle:
|
|
61
|
+
|
|
62
|
+
1. **Context:** Run `kopipasta` to select files and define your task.
|
|
63
|
+
2. **Generate:** Paste the prompt into your LLM (ChatGPT, Claude, etc.).
|
|
64
|
+
3. **Patch:** Press `p` in `kopipasta` and paste the LLM's response to apply changes locally.
|
|
65
|
+
4. **Iterate:** Review with `git diff`, then repeat for the next step.
|
|
66
|
+
|
|
67
|
+
## Use Cases
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
5. **Apply:** Inside the file selector, press `p`, paste the LLM's markdown response, and the tool will automatically patch your local files.
|
|
69
|
+
* **Targeted Refactoring:** Select just the module you are cleaning up and its immediate dependencies.
|
|
70
|
+
* **Test Generation:** Pipe your implementation file and a similar existing test file to the LLM to generate consistent new tests.
|
|
71
|
+
* **Docs to Code:** Select an API documentation file (or web URL) and your source file to implement a feature against a spec.
|
|
72
|
+
* **Bug Fixing:** Grab the relevant traceback files and the config to diagnose issues without distracting the LLM with the whole repo.
|
|
59
73
|
|
|
60
74
|
## Installation
|
|
61
75
|
|
|
@@ -73,6 +87,9 @@ pip install kopipasta
|
|
|
73
87
|
|
|
74
88
|
### Creating a Prompt
|
|
75
89
|
|
|
90
|
+
By default `kopipasta` opens tree selector on the current dir.
|
|
91
|
+
|
|
92
|
+
You may also use the command line arguments:
|
|
76
93
|
```bash
|
|
77
94
|
kopipasta [options] [files_or_directories_or_urls...]
|
|
78
95
|
```
|
|
@@ -90,16 +107,33 @@ kopipasta [options] [files_or_directories_or_urls...]
|
|
|
90
107
|
`kopipasta` automatically injects strict instructions into your prompt, teaching the LLM how to format code for this tool.
|
|
91
108
|
`kopipasta` can apply changes suggested by an LLM directly to your codebase, assuming you are in a Git repository.
|
|
92
109
|
|
|
93
|
-
1.
|
|
94
|
-
2. Paste the entire markdown response from your LLM
|
|
95
|
-
3. The tool
|
|
96
|
-
4.
|
|
110
|
+
1. Press `p` in the file selector.
|
|
111
|
+
2. Paste the **entire** markdown response from your LLM.
|
|
112
|
+
3. The tool robustly detects code blocks, handles indentation quirks, and applies changes (full files or diffs).
|
|
113
|
+
4. If a patch fails, the tool provides **diagnostic feedback** telling you exactly why (e.g., missing headers).
|
|
114
|
+
5. **Always** review changes with `git diff` before committing.
|
|
115
|
+
|
|
116
|
+
**Example of supported LLM output formats:**
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# FILE: src/utils.py
|
|
120
|
+
def new_feature():
|
|
121
|
+
print("kopipasta handles full file creation")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```diff
|
|
125
|
+
# FILE: src/main.py
|
|
126
|
+
@@ -10,2 +10,3 @@
|
|
127
|
+
def main():
|
|
128
|
+
- pass
|
|
129
|
+
+ new_feature()
|
|
130
|
+
```
|
|
97
131
|
|
|
98
132
|
## Key Features
|
|
99
133
|
|
|
100
134
|
* **Total Context Control:** Interactively select files, directories, or snippets. You see everything that goes into the prompt.
|
|
101
135
|
* **Smart Dependency Analysis:** Press `d` on a Python or TypeScript/JavaScript file, and `kopipasta` will scan imports to find and add related local files to your context automatically.
|
|
102
|
-
* **
|
|
136
|
+
* **Robust Code Patcher:** Applies LLM suggestions directly. Handles indentation, various comment styles (`#`, `//`, `<!--`), and multiple files per block.
|
|
103
137
|
* **Built-in Search:** Press `g` to grep for text patterns inside directories to find relevant files.
|
|
104
138
|
* **Transparent & Explicit:** No hidden RAG. You know exactly what's in the prompt because you built it. This makes debugging LLM failures possible.
|
|
105
139
|
* **Web-Aware:** Pulls in content directly from URLs—perfect for API documentation.
|
|
@@ -125,16 +159,3 @@ kopipasta [options] [files_or_directories_or_urls...]
|
|
|
125
159
|
| `r` | Reuse selection from previous run |
|
|
126
160
|
| `Enter` | Expand/Collapse directory |
|
|
127
161
|
| `q` | Quit and finalize selection |
|
|
128
|
-
|
|
129
|
-
## A Real-World Example
|
|
130
|
-
|
|
131
|
-
I had a bug where my `setup.py` didn't include all the dependencies from `requirements.txt`.
|
|
132
|
-
|
|
133
|
-
1. I ran `kopipasta -t "Update setup.py to read dependencies dynamically from requirements.txt" setup.py requirements.txt`.
|
|
134
|
-
2. The tool confirmed the inclusion of both files and copied the complete prompt to my clipboard.
|
|
135
|
-
3. I pasted the prompt into my LLM chat window.
|
|
136
|
-
4. I copied the LLM's response (which included a modified `setup.py` in a markdown code block).
|
|
137
|
-
5. Inside `kopipasta`, I pressed `p`, pasted the response, and my local `setup.py` was updated.
|
|
138
|
-
6. I ran `git diff` to review the changes, then tested and committed.
|
|
139
|
-
|
|
140
|
-
No manual file reading, no clumsy copy-pasting, just a clean, context-rich prompt that I had full control over, and a seamless way to apply the results.
|
|
@@ -7,27 +7,41 @@
|
|
|
7
7
|
|
|
8
8
|
A CLI tool for taking **full, transparent control** of your prompt. No black boxes.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
```text
|
|
11
|
+
➜ ~ kopipasta
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
📁 Project Files
|
|
14
|
+
|-- 📂 src/
|
|
15
|
+
| |-- ● 📄 main.py (4.2 KB)
|
|
16
|
+
| |-- ○ 📄 utils.py (1.5 KB)
|
|
17
|
+
|-- ○ 📄 README.md (2.1 KB)
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
Current: src/main.py | Selected: 1 full | ~4,200 chars
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## You Control the Context
|
|
15
23
|
|
|
16
|
-
Many AI coding assistants
|
|
24
|
+
Many AI coding assistants automatically find what *they think* is relevant context. This is a black box. When the LLM gives a bad answer, you can't debug it because you don't know what context it was actually given.
|
|
17
25
|
|
|
18
|
-
**`kopipasta` is the opposite.** I built it
|
|
26
|
+
**`kopipasta` is the opposite.** I built it on the principle of **explicit context control**. You are in the driver's seat. You decide *exactly* what files, functions, and snippets go into the prompt.
|
|
19
27
|
|
|
20
28
|
It's a "smart copy" command for your project, not a magic wand.
|
|
21
29
|
|
|
22
30
|
## How It Works
|
|
23
31
|
|
|
24
|
-
The workflow is
|
|
32
|
+
The workflow is a fast, iterative cycle:
|
|
33
|
+
|
|
34
|
+
1. **Context:** Run `kopipasta` to select files and define your task.
|
|
35
|
+
2. **Generate:** Paste the prompt into your LLM (ChatGPT, Claude, etc.).
|
|
36
|
+
3. **Patch:** Press `p` in `kopipasta` and paste the LLM's response to apply changes locally.
|
|
37
|
+
4. **Iterate:** Review with `git diff`, then repeat for the next step.
|
|
38
|
+
|
|
39
|
+
## Use Cases
|
|
25
40
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
5. **Apply:** Inside the file selector, press `p`, paste the LLM's markdown response, and the tool will automatically patch your local files.
|
|
41
|
+
* **Targeted Refactoring:** Select just the module you are cleaning up and its immediate dependencies.
|
|
42
|
+
* **Test Generation:** Pipe your implementation file and a similar existing test file to the LLM to generate consistent new tests.
|
|
43
|
+
* **Docs to Code:** Select an API documentation file (or web URL) and your source file to implement a feature against a spec.
|
|
44
|
+
* **Bug Fixing:** Grab the relevant traceback files and the config to diagnose issues without distracting the LLM with the whole repo.
|
|
31
45
|
|
|
32
46
|
## Installation
|
|
33
47
|
|
|
@@ -45,6 +59,9 @@ pip install kopipasta
|
|
|
45
59
|
|
|
46
60
|
### Creating a Prompt
|
|
47
61
|
|
|
62
|
+
By default `kopipasta` opens tree selector on the current dir.
|
|
63
|
+
|
|
64
|
+
You may also use the command line arguments:
|
|
48
65
|
```bash
|
|
49
66
|
kopipasta [options] [files_or_directories_or_urls...]
|
|
50
67
|
```
|
|
@@ -62,16 +79,33 @@ kopipasta [options] [files_or_directories_or_urls...]
|
|
|
62
79
|
`kopipasta` automatically injects strict instructions into your prompt, teaching the LLM how to format code for this tool.
|
|
63
80
|
`kopipasta` can apply changes suggested by an LLM directly to your codebase, assuming you are in a Git repository.
|
|
64
81
|
|
|
65
|
-
1.
|
|
66
|
-
2. Paste the entire markdown response from your LLM
|
|
67
|
-
3. The tool
|
|
68
|
-
4.
|
|
82
|
+
1. Press `p` in the file selector.
|
|
83
|
+
2. Paste the **entire** markdown response from your LLM.
|
|
84
|
+
3. The tool robustly detects code blocks, handles indentation quirks, and applies changes (full files or diffs).
|
|
85
|
+
4. If a patch fails, the tool provides **diagnostic feedback** telling you exactly why (e.g., missing headers).
|
|
86
|
+
5. **Always** review changes with `git diff` before committing.
|
|
87
|
+
|
|
88
|
+
**Example of supported LLM output formats:**
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# FILE: src/utils.py
|
|
92
|
+
def new_feature():
|
|
93
|
+
print("kopipasta handles full file creation")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```diff
|
|
97
|
+
# FILE: src/main.py
|
|
98
|
+
@@ -10,2 +10,3 @@
|
|
99
|
+
def main():
|
|
100
|
+
- pass
|
|
101
|
+
+ new_feature()
|
|
102
|
+
```
|
|
69
103
|
|
|
70
104
|
## Key Features
|
|
71
105
|
|
|
72
106
|
* **Total Context Control:** Interactively select files, directories, or snippets. You see everything that goes into the prompt.
|
|
73
107
|
* **Smart Dependency Analysis:** Press `d` on a Python or TypeScript/JavaScript file, and `kopipasta` will scan imports to find and add related local files to your context automatically.
|
|
74
|
-
* **
|
|
108
|
+
* **Robust Code Patcher:** Applies LLM suggestions directly. Handles indentation, various comment styles (`#`, `//`, `<!--`), and multiple files per block.
|
|
75
109
|
* **Built-in Search:** Press `g` to grep for text patterns inside directories to find relevant files.
|
|
76
110
|
* **Transparent & Explicit:** No hidden RAG. You know exactly what's in the prompt because you built it. This makes debugging LLM failures possible.
|
|
77
111
|
* **Web-Aware:** Pulls in content directly from URLs—perfect for API documentation.
|
|
@@ -97,16 +131,3 @@ kopipasta [options] [files_or_directories_or_urls...]
|
|
|
97
131
|
| `r` | Reuse selection from previous run |
|
|
98
132
|
| `Enter` | Expand/Collapse directory |
|
|
99
133
|
| `q` | Quit and finalize selection |
|
|
100
|
-
|
|
101
|
-
## A Real-World Example
|
|
102
|
-
|
|
103
|
-
I had a bug where my `setup.py` didn't include all the dependencies from `requirements.txt`.
|
|
104
|
-
|
|
105
|
-
1. I ran `kopipasta -t "Update setup.py to read dependencies dynamically from requirements.txt" setup.py requirements.txt`.
|
|
106
|
-
2. The tool confirmed the inclusion of both files and copied the complete prompt to my clipboard.
|
|
107
|
-
3. I pasted the prompt into my LLM chat window.
|
|
108
|
-
4. I copied the LLM's response (which included a modified `setup.py` in a markdown code block).
|
|
109
|
-
5. Inside `kopipasta`, I pressed `p`, pasted the response, and my local `setup.py` was updated.
|
|
110
|
-
6. I ran `git diff` to review the changes, then tested and committed.
|
|
111
|
-
|
|
112
|
-
No manual file reading, no clumsy copy-pasting, just a clean, context-rich prompt that I had full control over, and a seamless way to apply the results.
|
|
@@ -22,6 +22,7 @@ from kopipasta.ops import (
|
|
|
22
22
|
print_char_count,
|
|
23
23
|
read_env_file,
|
|
24
24
|
read_gitignore,
|
|
25
|
+
sanitize_string,
|
|
25
26
|
)
|
|
26
27
|
from kopipasta.tree_selector import TreeSelector
|
|
27
28
|
from kopipasta.prompt import (
|
|
@@ -73,9 +74,9 @@ def fetch_web_content(
|
|
|
73
74
|
def main():
|
|
74
75
|
if sys.platform == "win32":
|
|
75
76
|
try:
|
|
76
|
-
sys.stdin.reconfigure(encoding="utf-8")
|
|
77
|
-
sys.stdout.reconfigure(encoding="utf-8")
|
|
78
|
-
sys.stderr.reconfigure(encoding="utf-8")
|
|
77
|
+
sys.stdin.reconfigure(encoding="utf-8", errors="replace")
|
|
78
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
79
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
|
79
80
|
except AttributeError:
|
|
80
81
|
pass
|
|
81
82
|
|
|
@@ -193,7 +194,7 @@ def main():
|
|
|
193
194
|
)
|
|
194
195
|
|
|
195
196
|
prompt_template, cursor_position = generate_prompt_template(
|
|
196
|
-
files_to_include, ignore_patterns, web_contents, env_vars
|
|
197
|
+
files_to_include, ignore_patterns, web_contents, env_vars, paths_for_tree
|
|
197
198
|
)
|
|
198
199
|
|
|
199
200
|
if args.task:
|
|
@@ -214,6 +215,8 @@ def main():
|
|
|
214
215
|
+ prompt_template[cursor_position:]
|
|
215
216
|
)
|
|
216
217
|
|
|
218
|
+
final_prompt = sanitize_string(final_prompt)
|
|
219
|
+
|
|
217
220
|
print("\n\nGenerated prompt:")
|
|
218
221
|
print("-" * 80)
|
|
219
222
|
print(final_prompt)
|
|
@@ -15,6 +15,17 @@ from kopipasta.prompt import get_file_snippet, get_language_for_file
|
|
|
15
15
|
import kopipasta.import_parser as import_parser
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def sanitize_string(text: str) -> str:
|
|
19
|
+
"""
|
|
20
|
+
Ensures the string contains valid unicode code points, fixing surrogate pairs
|
|
21
|
+
that might have been introduced by Windows terminal input.
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
return text.encode("utf-16", "surrogatepass").decode("utf-16")
|
|
25
|
+
except Exception:
|
|
26
|
+
return text
|
|
27
|
+
|
|
28
|
+
|
|
18
29
|
def print_char_count(count: int):
|
|
19
30
|
token_estimate = count // 4
|
|
20
31
|
print(
|
|
@@ -74,43 +85,6 @@ def read_gitignore() -> List[str]:
|
|
|
74
85
|
"target",
|
|
75
86
|
".DS_Store",
|
|
76
87
|
"Thumbs.db",
|
|
77
|
-
"*.class",
|
|
78
|
-
"*.jar",
|
|
79
|
-
"*.war",
|
|
80
|
-
"*.ear",
|
|
81
|
-
"*.sqlite",
|
|
82
|
-
"*.db",
|
|
83
|
-
"*.jpg",
|
|
84
|
-
"*.jpeg",
|
|
85
|
-
"*.png",
|
|
86
|
-
"*.gif",
|
|
87
|
-
"*.bmp",
|
|
88
|
-
"*.tiff",
|
|
89
|
-
"*.ico",
|
|
90
|
-
"*.svg",
|
|
91
|
-
"*.webp",
|
|
92
|
-
"*.mp3",
|
|
93
|
-
"*.mp4",
|
|
94
|
-
"*.avi",
|
|
95
|
-
"*.mov",
|
|
96
|
-
"*.wmv",
|
|
97
|
-
"*.flv",
|
|
98
|
-
"*.pdf",
|
|
99
|
-
"*.doc",
|
|
100
|
-
"*.docx",
|
|
101
|
-
"*.xls",
|
|
102
|
-
"*.xlsx",
|
|
103
|
-
"*.ppt",
|
|
104
|
-
"*.pptx",
|
|
105
|
-
"*.zip",
|
|
106
|
-
"*.rar",
|
|
107
|
-
"*.tar",
|
|
108
|
-
"*.gz",
|
|
109
|
-
"*.7z",
|
|
110
|
-
"*.exe",
|
|
111
|
-
"*.dll",
|
|
112
|
-
"*.so",
|
|
113
|
-
"*.dylib",
|
|
114
88
|
]
|
|
115
89
|
gitignore_patterns = default_ignore_patterns.copy()
|
|
116
90
|
|