kopipasta 0.31.0__py3-none-any.whl → 0.33.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/file.py +96 -14
- kopipasta/main.py +25 -5
- kopipasta/tree_selector.py +59 -48
- {kopipasta-0.31.0.dist-info → kopipasta-0.33.0.dist-info}/METADATA +1 -1
- kopipasta-0.33.0.dist-info/RECORD +13 -0
- kopipasta-0.31.0.dist-info/RECORD +0 -13
- {kopipasta-0.31.0.dist-info → kopipasta-0.33.0.dist-info}/LICENSE +0 -0
- {kopipasta-0.31.0.dist-info → kopipasta-0.33.0.dist-info}/WHEEL +0 -0
- {kopipasta-0.31.0.dist-info → kopipasta-0.33.0.dist-info}/entry_points.txt +0 -0
- {kopipasta-0.31.0.dist-info → kopipasta-0.33.0.dist-info}/top_level.txt +0 -0
kopipasta/file.py
CHANGED
|
@@ -1,10 +1,103 @@
|
|
|
1
1
|
import fnmatch
|
|
2
2
|
import os
|
|
3
3
|
from typing import List, Optional, Tuple
|
|
4
|
-
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
FileTuple = Tuple[str, bool, Optional[List[str]], str]
|
|
7
7
|
|
|
8
|
+
# --- Cache for .gitignore patterns ---
|
|
9
|
+
# Key: Directory path
|
|
10
|
+
# Value: List of patterns
|
|
11
|
+
_gitignore_cache: dict[str, list[str]] = {}
|
|
12
|
+
|
|
13
|
+
def _read_gitignore_patterns(gitignore_path: str) -> list[str]:
|
|
14
|
+
"""Reads patterns from a single .gitignore file and caches them."""
|
|
15
|
+
if gitignore_path in _gitignore_cache:
|
|
16
|
+
return _gitignore_cache[gitignore_path]
|
|
17
|
+
if not os.path.isfile(gitignore_path):
|
|
18
|
+
_gitignore_cache[gitignore_path] = []
|
|
19
|
+
return []
|
|
20
|
+
patterns = []
|
|
21
|
+
try:
|
|
22
|
+
with open(gitignore_path, 'r', encoding='utf-8') as f:
|
|
23
|
+
for line in f:
|
|
24
|
+
stripped_line = line.strip()
|
|
25
|
+
if stripped_line and not stripped_line.startswith('#'):
|
|
26
|
+
patterns.append(stripped_line)
|
|
27
|
+
except IOError:
|
|
28
|
+
pass
|
|
29
|
+
_gitignore_cache[gitignore_path] = patterns
|
|
30
|
+
return patterns
|
|
31
|
+
|
|
32
|
+
def is_ignored(path: str, default_ignore_patterns: list[str], project_root: Optional[str] = None) -> bool:
|
|
33
|
+
"""
|
|
34
|
+
Checks if a path should be ignored based on default patterns and .gitignore files.
|
|
35
|
+
Searches for .gitignore from the path's location up to the project_root.
|
|
36
|
+
"""
|
|
37
|
+
path_abs = os.path.abspath(path)
|
|
38
|
+
if project_root is None:
|
|
39
|
+
project_root = os.getcwd()
|
|
40
|
+
project_root_abs = os.path.abspath(project_root)
|
|
41
|
+
|
|
42
|
+
# --- Step 1: Gather all patterns from all relevant .gitignore files ---
|
|
43
|
+
all_patterns = set(default_ignore_patterns)
|
|
44
|
+
|
|
45
|
+
# Determine the directory to start searching for .gitignore files
|
|
46
|
+
search_start_dir = path_abs if os.path.isdir(path_abs) else os.path.dirname(path_abs)
|
|
47
|
+
|
|
48
|
+
current_dir = search_start_dir
|
|
49
|
+
while True:
|
|
50
|
+
gitignore_path = os.path.join(current_dir, ".gitignore")
|
|
51
|
+
patterns_from_file = _read_gitignore_patterns(gitignore_path)
|
|
52
|
+
|
|
53
|
+
if patterns_from_file:
|
|
54
|
+
gitignore_dir_rel = os.path.relpath(current_dir, project_root_abs)
|
|
55
|
+
if gitignore_dir_rel == '.': gitignore_dir_rel = ''
|
|
56
|
+
|
|
57
|
+
for p in patterns_from_file:
|
|
58
|
+
# Patterns with a '/' are relative to the .gitignore file's location.
|
|
59
|
+
# We construct a new pattern relative to the project root.
|
|
60
|
+
if '/' in p:
|
|
61
|
+
all_patterns.add(os.path.join(gitignore_dir_rel, p.lstrip('/')))
|
|
62
|
+
else:
|
|
63
|
+
# Patterns without a '/' (e.g., `*.log`) can match anywhere.
|
|
64
|
+
all_patterns.add(p)
|
|
65
|
+
|
|
66
|
+
if not current_dir.startswith(project_root_abs) or current_dir == project_root_abs:
|
|
67
|
+
break
|
|
68
|
+
parent = os.path.dirname(current_dir)
|
|
69
|
+
if parent == current_dir: break
|
|
70
|
+
current_dir = parent
|
|
71
|
+
|
|
72
|
+
# --- Step 2: Check the path and its parents against the patterns ---
|
|
73
|
+
try:
|
|
74
|
+
path_rel_to_root = os.path.relpath(path_abs, project_root_abs)
|
|
75
|
+
except ValueError:
|
|
76
|
+
return False # Path is outside the project root
|
|
77
|
+
|
|
78
|
+
path_parts = Path(path_rel_to_root).parts
|
|
79
|
+
|
|
80
|
+
for pattern in all_patterns:
|
|
81
|
+
# Check against basename for simple wildcards (e.g., `*.log`, `__pycache__`)
|
|
82
|
+
# This is a primary matching mechanism.
|
|
83
|
+
if fnmatch.fnmatch(os.path.basename(path_abs), pattern):
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
# Check the full path and its parent directories against the pattern.
|
|
87
|
+
# This handles directory ignores (`node_modules/`) and specific path ignores (`src/*.tmp`).
|
|
88
|
+
for i in range(len(path_parts)):
|
|
89
|
+
current_check_path = os.path.join(*path_parts[:i+1])
|
|
90
|
+
|
|
91
|
+
# Handle directory patterns like `node_modules/`
|
|
92
|
+
if pattern.endswith('/'):
|
|
93
|
+
if fnmatch.fnmatch(current_check_path, pattern.rstrip('/')):
|
|
94
|
+
return True
|
|
95
|
+
# Handle full path patterns
|
|
96
|
+
else:
|
|
97
|
+
if fnmatch.fnmatch(current_check_path, pattern):
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
return False
|
|
8
101
|
|
|
9
102
|
def read_file_contents(file_path):
|
|
10
103
|
try:
|
|
@@ -14,20 +107,11 @@ def read_file_contents(file_path):
|
|
|
14
107
|
print(f"Error reading {file_path}: {e}")
|
|
15
108
|
return ""
|
|
16
109
|
|
|
17
|
-
|
|
18
|
-
def is_ignored(path, ignore_patterns):
|
|
19
|
-
path = os.path.normpath(path)
|
|
20
|
-
for pattern in ignore_patterns:
|
|
21
|
-
if fnmatch.fnmatch(os.path.basename(path), pattern) or fnmatch.fnmatch(path, pattern):
|
|
22
|
-
return True
|
|
23
|
-
return False
|
|
24
|
-
|
|
25
|
-
|
|
26
110
|
def is_binary(file_path):
|
|
27
111
|
try:
|
|
28
112
|
with open(file_path, 'rb') as file:
|
|
29
113
|
chunk = file.read(1024)
|
|
30
|
-
if b'\0' in chunk:
|
|
114
|
+
if b'\0' in chunk:
|
|
31
115
|
return True
|
|
32
116
|
if file_path.lower().endswith(('.json', '.csv')):
|
|
33
117
|
return False
|
|
@@ -35,13 +119,11 @@ def is_binary(file_path):
|
|
|
35
119
|
except IOError:
|
|
36
120
|
return False
|
|
37
121
|
|
|
38
|
-
|
|
39
122
|
def get_human_readable_size(size):
|
|
40
123
|
for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
|
|
41
124
|
if size < 1024.0:
|
|
42
125
|
return f"{size:.2f} {unit}"
|
|
43
126
|
size /= 1024.0
|
|
44
127
|
|
|
45
|
-
|
|
46
|
-
def is_large_file(file_path, threshold=102400): # 100 KB threshold
|
|
128
|
+
def is_large_file(file_path, threshold=102400):
|
|
47
129
|
return os.path.getsize(file_path) > threshold
|
kopipasta/main.py
CHANGED
|
@@ -137,7 +137,7 @@ def read_gitignore():
|
|
|
137
137
|
'.terraform', 'output', 'poetry.lock', 'package-lock.json', '.env',
|
|
138
138
|
'*.log', '*.bak', '*.swp', '*.swo', '*.tmp', 'tmp', 'temp', 'logs',
|
|
139
139
|
'build', 'target', '.DS_Store', 'Thumbs.db', '*.class', '*.jar',
|
|
140
|
-
'*.war', '*.ear', '*.sqlite', '*.db',
|
|
140
|
+
'*.war', '*.ear', '*.sqlite', '*.db',
|
|
141
141
|
'*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp', '*.tiff',
|
|
142
142
|
'*.ico', '*.svg', '*.webp', '*.mp3', '*.mp4', '*.avi',
|
|
143
143
|
'*.mov', '*.wmv', '*.flv', '*.pdf', '*.doc', '*.docx',
|
|
@@ -490,7 +490,7 @@ def grep_files_in_directory(pattern: str, directory: str, ignore_patterns: List[
|
|
|
490
490
|
grep_results = []
|
|
491
491
|
|
|
492
492
|
for file in files:
|
|
493
|
-
if is_ignored(file, ignore_patterns) or is_binary(file):
|
|
493
|
+
if is_ignored(file, ignore_patterns, directory) or is_binary(file):
|
|
494
494
|
continue
|
|
495
495
|
|
|
496
496
|
# Get match count and preview lines
|
|
@@ -1012,6 +1012,7 @@ def main():
|
|
|
1012
1012
|
|
|
1013
1013
|
# Separate URLs from file/directory paths
|
|
1014
1014
|
paths_for_tree = []
|
|
1015
|
+
files_to_preselect = []
|
|
1015
1016
|
|
|
1016
1017
|
for input_path in args.inputs:
|
|
1017
1018
|
if input_path.startswith(('http://', 'https://')):
|
|
@@ -1050,9 +1051,11 @@ def main():
|
|
|
1050
1051
|
print(f"Added {'snippet of ' if is_snippet else ''}web content from: {input_path}")
|
|
1051
1052
|
print_char_count(current_char_count)
|
|
1052
1053
|
else:
|
|
1053
|
-
|
|
1054
|
-
if os.path.exists(
|
|
1054
|
+
abs_path = os.path.abspath(input_path)
|
|
1055
|
+
if os.path.exists(abs_path):
|
|
1055
1056
|
paths_for_tree.append(input_path)
|
|
1057
|
+
if os.path.isfile(abs_path):
|
|
1058
|
+
files_to_preselect.append(abs_path)
|
|
1056
1059
|
else:
|
|
1057
1060
|
print(f"Warning: {input_path} does not exist. Skipping.")
|
|
1058
1061
|
|
|
@@ -1063,7 +1066,7 @@ def main():
|
|
|
1063
1066
|
|
|
1064
1067
|
tree_selector = TreeSelector(ignore_patterns, project_root_abs)
|
|
1065
1068
|
try:
|
|
1066
|
-
selected_files, file_char_count = tree_selector.run(paths_for_tree)
|
|
1069
|
+
selected_files, file_char_count = tree_selector.run(paths_for_tree, files_to_preselect)
|
|
1067
1070
|
files_to_include.extend(selected_files)
|
|
1068
1071
|
current_char_count += file_char_count
|
|
1069
1072
|
except KeyboardInterrupt:
|
|
@@ -1107,8 +1110,25 @@ def main():
|
|
|
1107
1110
|
|
|
1108
1111
|
try:
|
|
1109
1112
|
pyperclip.copy(final_prompt)
|
|
1113
|
+
print("\n--- Included Files & Content ---\n")
|
|
1114
|
+
for file_path, is_snippet, chunks, _ in sorted(files_to_include, key=lambda x: x[0]):
|
|
1115
|
+
details = []
|
|
1116
|
+
if is_snippet:
|
|
1117
|
+
details.append("snippet")
|
|
1118
|
+
if chunks is not None:
|
|
1119
|
+
details.append(f"{len(chunks)} patches")
|
|
1120
|
+
|
|
1121
|
+
detail_str = f" ({', '.join(details)})" if details else ""
|
|
1122
|
+
print(f"- {os.path.relpath(file_path)}{detail_str}")
|
|
1123
|
+
|
|
1124
|
+
for url, (file_tuple, _) in sorted(web_contents.items()):
|
|
1125
|
+
is_snippet = file_tuple[1]
|
|
1126
|
+
detail_str = " (snippet)" if is_snippet else ""
|
|
1127
|
+
print(f"- {url}{detail_str}")
|
|
1128
|
+
|
|
1110
1129
|
separator = "\n" + "=" * 40 + "\n☕🍝 Kopipasta Complete! 🍝☕\n" + "=" * 40 + "\n"
|
|
1111
1130
|
print(separator)
|
|
1131
|
+
|
|
1112
1132
|
final_char_count = len(final_prompt)
|
|
1113
1133
|
final_token_estimate = final_char_count // 4
|
|
1114
1134
|
print(f"Prompt has been copied to clipboard. Final size: {final_char_count} characters (~ {final_token_estimate} tokens)")
|
kopipasta/tree_selector.py
CHANGED
|
@@ -14,27 +14,23 @@ from kopipasta.cache import load_selection_from_cache
|
|
|
14
14
|
|
|
15
15
|
class FileNode:
|
|
16
16
|
"""Represents a file or directory in the tree"""
|
|
17
|
-
def __init__(self, path: str, is_dir: bool, parent: Optional['FileNode'] = None):
|
|
18
|
-
self.path = os.path.abspath(path)
|
|
17
|
+
def __init__(self, path: str, is_dir: bool, parent: Optional['FileNode'] = None, is_scan_root: bool = False):
|
|
18
|
+
self.path = os.path.abspath(path)
|
|
19
19
|
self.is_dir = is_dir
|
|
20
20
|
self.parent = parent
|
|
21
21
|
self.children: List['FileNode'] = []
|
|
22
22
|
self.expanded = False
|
|
23
|
-
|
|
24
|
-
self.
|
|
23
|
+
# This flag marks the invisible root of the file tree, which is not meant to be displayed.
|
|
24
|
+
self.is_scan_root = is_scan_root
|
|
25
25
|
self.size = 0 if is_dir else os.path.getsize(self.path)
|
|
26
|
-
self.is_root = path == "." # Mark if this is the root node
|
|
27
26
|
|
|
28
27
|
@property
|
|
29
28
|
def name(self):
|
|
30
|
-
if self.is_root:
|
|
31
|
-
return "." # Show root as "." instead of directory name
|
|
32
29
|
return os.path.basename(self.path) or self.path
|
|
33
30
|
|
|
34
31
|
@property
|
|
35
32
|
def relative_path(self):
|
|
36
|
-
|
|
37
|
-
return "."
|
|
33
|
+
# os.path.relpath is relative to the current working directory by default
|
|
38
34
|
return os.path.relpath(self.path)
|
|
39
35
|
|
|
40
36
|
|
|
@@ -54,33 +50,33 @@ class TreeSelector:
|
|
|
54
50
|
self.viewport_offset = 0 # First visible item index
|
|
55
51
|
|
|
56
52
|
def build_tree(self, paths: List[str]) -> FileNode:
|
|
57
|
-
"""Build tree structure from given paths"""
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
"""Build tree structure from given paths."""
|
|
54
|
+
# If one directory is given, make its contents the top level of the tree.
|
|
55
|
+
if len(paths) == 1 and os.path.isdir(paths[0]):
|
|
56
|
+
root_path = os.path.abspath(paths[0])
|
|
57
|
+
root = FileNode(root_path, True, is_scan_root=True)
|
|
58
|
+
root.expanded = True
|
|
59
|
+
self._scan_directory(root_path, root)
|
|
60
|
+
return root
|
|
61
|
+
|
|
62
|
+
# Otherwise, create a virtual root to hold multiple items (e.g., `kopipasta file.py dir/`).
|
|
63
|
+
# This virtual root itself won't be displayed.
|
|
64
|
+
virtual_root_path = os.path.join(self.project_root_abs, "__kopipasta_virtual_root__")
|
|
65
|
+
root = FileNode(virtual_root_path, True, is_scan_root=True)
|
|
66
|
+
root.expanded = True
|
|
67
|
+
|
|
63
68
|
for path in paths:
|
|
64
69
|
abs_path = os.path.abspath(path)
|
|
65
|
-
|
|
70
|
+
node = None
|
|
66
71
|
if os.path.isfile(abs_path):
|
|
67
|
-
|
|
68
|
-
if not is_ignored(abs_path, self.ignore_patterns) and not is_binary(abs_path):
|
|
72
|
+
if not is_ignored(abs_path, self.ignore_patterns, self.project_root_abs) and not is_binary(abs_path):
|
|
69
73
|
node = FileNode(abs_path, False, root)
|
|
70
|
-
root.children.append(node)
|
|
71
74
|
elif os.path.isdir(abs_path):
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
dir_node = FileNode(abs_path, True, root)
|
|
78
|
-
root.children.append(dir_node)
|
|
79
|
-
# Auto-expand if it's the only child
|
|
80
|
-
if len(paths) == 1:
|
|
81
|
-
dir_node.expanded = True
|
|
82
|
-
self._scan_directory(abs_path, dir_node)
|
|
83
|
-
|
|
75
|
+
node = FileNode(abs_path, True, root)
|
|
76
|
+
|
|
77
|
+
if node:
|
|
78
|
+
root.children.append(node)
|
|
79
|
+
|
|
84
80
|
return root
|
|
85
81
|
|
|
86
82
|
def _scan_directory(self, dir_path: str, parent_node: FileNode):
|
|
@@ -102,7 +98,7 @@ class TreeSelector:
|
|
|
102
98
|
|
|
103
99
|
for item in items:
|
|
104
100
|
item_path = os.path.join(abs_dir_path, item)
|
|
105
|
-
if is_ignored(item_path, self.ignore_patterns):
|
|
101
|
+
if is_ignored(item_path, self.ignore_patterns, self.project_root_abs):
|
|
106
102
|
continue
|
|
107
103
|
|
|
108
104
|
if os.path.isdir(item_path):
|
|
@@ -131,23 +127,18 @@ class TreeSelector:
|
|
|
131
127
|
parent_node.children.append(file_node)
|
|
132
128
|
|
|
133
129
|
def _flatten_tree(self, node: FileNode, level: int = 0) -> List[Tuple[FileNode, int]]:
|
|
134
|
-
"""Flatten tree into a list of (node, level) tuples for display"""
|
|
130
|
+
"""Flatten tree into a list of (node, level) tuples for display."""
|
|
135
131
|
result = []
|
|
136
132
|
|
|
137
|
-
#
|
|
138
|
-
if node.
|
|
139
|
-
# Don't include the root node itself in the display
|
|
133
|
+
# If it's the special root node, don't display it. Display its children at the top level.
|
|
134
|
+
if node.is_scan_root:
|
|
140
135
|
for child in node.children:
|
|
141
|
-
result.extend(self._flatten_tree(child, 0))
|
|
136
|
+
result.extend(self._flatten_tree(child, 0))
|
|
142
137
|
else:
|
|
143
|
-
# Include this node
|
|
144
138
|
result.append((node, level))
|
|
145
|
-
|
|
146
139
|
if node.is_dir and node.expanded:
|
|
147
|
-
# Load children on demand if not loaded
|
|
148
140
|
if not node.children:
|
|
149
141
|
self._scan_directory(node.path, node)
|
|
150
|
-
|
|
151
142
|
for child in node.children:
|
|
152
143
|
result.extend(self._flatten_tree(child, level + 1))
|
|
153
144
|
|
|
@@ -156,10 +147,11 @@ class TreeSelector:
|
|
|
156
147
|
def _build_display_tree(self) -> Tree:
|
|
157
148
|
"""Build Rich tree for display with viewport"""
|
|
158
149
|
# Get terminal size
|
|
159
|
-
|
|
150
|
+
_, term_height = shutil.get_terminal_size()
|
|
160
151
|
|
|
161
152
|
# Reserve space for header, help panel, and status
|
|
162
|
-
|
|
153
|
+
reserved_space = 17
|
|
154
|
+
available_height = term_height - reserved_space
|
|
163
155
|
available_height = max(5, available_height) # Minimum height
|
|
164
156
|
|
|
165
157
|
# Flatten tree to get all visible nodes
|
|
@@ -338,10 +330,7 @@ q: Quit and finalize"""
|
|
|
338
330
|
# Unselect
|
|
339
331
|
is_snippet, _ = self.selected_files[abs_path]
|
|
340
332
|
del self.selected_files[abs_path]
|
|
341
|
-
if is_snippet
|
|
342
|
-
self.char_count -= len(get_file_snippet(node.path))
|
|
343
|
-
else:
|
|
344
|
-
self.char_count -= node.size
|
|
333
|
+
self.char_count -= len(get_file_snippet(node.path)) if is_snippet else node.size
|
|
345
334
|
else:
|
|
346
335
|
# Select
|
|
347
336
|
if snippet_mode or (node.size > 102400 and not self._confirm_large_file(node)):
|
|
@@ -539,10 +528,32 @@ q: Quit and finalize"""
|
|
|
539
528
|
|
|
540
529
|
self.char_count += deps_char_count
|
|
541
530
|
|
|
542
|
-
def
|
|
531
|
+
def _preselect_files(self, files_to_preselect: List[str]):
|
|
532
|
+
"""Pre-selects a list of files passed from the command line."""
|
|
533
|
+
if not files_to_preselect:
|
|
534
|
+
return
|
|
535
|
+
|
|
536
|
+
added_count = 0
|
|
537
|
+
for file_path in files_to_preselect:
|
|
538
|
+
abs_path = os.path.abspath(file_path)
|
|
539
|
+
if abs_path in self.selected_files:
|
|
540
|
+
continue
|
|
541
|
+
|
|
542
|
+
# This check is simpler than a full tree walk and sufficient here
|
|
543
|
+
if os.path.isfile(abs_path) and not is_binary(abs_path):
|
|
544
|
+
file_size = os.path.getsize(abs_path)
|
|
545
|
+
self.selected_files[abs_path] = (False, None) # (is_snippet=False, chunks=None)
|
|
546
|
+
self.char_count += file_size
|
|
547
|
+
added_count += 1
|
|
548
|
+
self._ensure_path_visible(abs_path)
|
|
549
|
+
|
|
550
|
+
def run(self, initial_paths: List[str], files_to_preselect: Optional[List[str]] = None) -> Tuple[List[FileTuple], int]:
|
|
543
551
|
"""Run the interactive tree selector"""
|
|
544
552
|
self.root = self.build_tree(initial_paths)
|
|
545
553
|
|
|
554
|
+
if files_to_preselect:
|
|
555
|
+
self._preselect_files(files_to_preselect)
|
|
556
|
+
|
|
546
557
|
# Don't use Live mode, instead manually control the display
|
|
547
558
|
while not self.quit_selection:
|
|
548
559
|
# Clear and redraw
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
kopipasta/cache.py,sha256=rH52yX3g7p22_1NXmrBDcBEntm3ivMjtV8n8zxTLY1k,1454
|
|
3
|
+
kopipasta/file.py,sha256=tPCLNvSXHzIvAXLB6fgJswLpCFce7T1QnbNdSWHbIso,4829
|
|
4
|
+
kopipasta/import_parser.py,sha256=yLzkMlQm2avKjfqcpMY0PxbA_2ihV9gSYJplreWIPEQ,12424
|
|
5
|
+
kopipasta/main.py,sha256=YzzEnDph1v3NojHYIjvD3Z4blnIEX5zqj9W6gNLlA7E,50058
|
|
6
|
+
kopipasta/prompt.py,sha256=fOCuJVTLUfR0fjKf5qIlnl_3pNsKNKsvt3C8f4tsmxk,6889
|
|
7
|
+
kopipasta/tree_selector.py,sha256=VIBmPq5cTHU-xUJuXV3K58nn1zS-1X079WAFzfS2vCA,28247
|
|
8
|
+
kopipasta-0.33.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
9
|
+
kopipasta-0.33.0.dist-info/METADATA,sha256=Fs44BJq3C5tqpIdzccr6APhyx97UU0JHjlcTb1uqzuI,4894
|
|
10
|
+
kopipasta-0.33.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
11
|
+
kopipasta-0.33.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
12
|
+
kopipasta-0.33.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
13
|
+
kopipasta-0.33.0.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
kopipasta/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
kopipasta/cache.py,sha256=rH52yX3g7p22_1NXmrBDcBEntm3ivMjtV8n8zxTLY1k,1454
|
|
3
|
-
kopipasta/file.py,sha256=2HEoNczbKH3TtLO0zYUcZfUoHqb0-o83xFUOgY9rG-Y,1244
|
|
4
|
-
kopipasta/import_parser.py,sha256=yLzkMlQm2avKjfqcpMY0PxbA_2ihV9gSYJplreWIPEQ,12424
|
|
5
|
-
kopipasta/main.py,sha256=3Sle6pFEko4HSCXClmEb1L4QsN41VEOd0ZuzxeUDFUk,49210
|
|
6
|
-
kopipasta/prompt.py,sha256=fOCuJVTLUfR0fjKf5qIlnl_3pNsKNKsvt3C8f4tsmxk,6889
|
|
7
|
-
kopipasta/tree_selector.py,sha256=Iov8KLFopb7OLvX-xhn0FmFy0Ee5rFpPYzV6JLxCR84,27614
|
|
8
|
-
kopipasta-0.31.0.dist-info/LICENSE,sha256=xw4C9TAU7LFu4r_MwSbky90uzkzNtRwAo3c51IWR8lk,1091
|
|
9
|
-
kopipasta-0.31.0.dist-info/METADATA,sha256=RO8xJpSYGdqzsr-A7a22wTJEu2EwF_xtzJaziMD9XUw,4894
|
|
10
|
-
kopipasta-0.31.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
11
|
-
kopipasta-0.31.0.dist-info/entry_points.txt,sha256=but54qDNz1-F8fVvGstq_QID5tHjczP7bO7rWLFkc6Y,50
|
|
12
|
-
kopipasta-0.31.0.dist-info/top_level.txt,sha256=iXohixMuCdw8UjGDUp0ouICLYBDrx207sgZIJ9lxn0o,10
|
|
13
|
-
kopipasta-0.31.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|