code-puppy 0.0.60__py3-none-any.whl → 0.0.62__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.
- code_puppy/agent_prompts.py +1 -0
- code_puppy/command_line/meta_command_handler.py +2 -3
- code_puppy/tools/common.py +51 -0
- code_puppy/tools/file_operations.py +19 -45
- code_puppy/tools/ts_code_map.py +387 -0
- {code_puppy-0.0.60.dist-info → code_puppy-0.0.62.dist-info}/METADATA +3 -1
- {code_puppy-0.0.60.dist-info → code_puppy-0.0.62.dist-info}/RECORD +11 -11
- code_puppy/tools/code_map.py +0 -92
- {code_puppy-0.0.60.data → code_puppy-0.0.62.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.60.dist-info → code_puppy-0.0.62.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.60.dist-info → code_puppy-0.0.62.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.60.dist-info → code_puppy-0.0.62.dist-info}/licenses/LICENSE +0 -0
code_puppy/agent_prompts.py
CHANGED
|
@@ -30,6 +30,7 @@ File Operations:
|
|
|
30
30
|
- edit_file(path, diff): Use this single tool to create new files, overwrite entire files, perform targeted replacements, or delete snippets depending on the JSON/raw payload provided.
|
|
31
31
|
- delete_file(file_path): Use this to remove files when needed
|
|
32
32
|
- grep(search_string, directory="."): Use this to recursively search for a string across files starting from the specified directory, capping results at 200 matches.
|
|
33
|
+
- code_map(directory="."): Use this to generate a code map for the specified directory.
|
|
33
34
|
|
|
34
35
|
Tool Usage Instructions:
|
|
35
36
|
|
|
@@ -29,7 +29,7 @@ def handle_meta_command(command: str, console: Console) -> bool:
|
|
|
29
29
|
|
|
30
30
|
# ~codemap (code structure visualization)
|
|
31
31
|
if command.startswith("~codemap"):
|
|
32
|
-
from code_puppy.tools.
|
|
32
|
+
from code_puppy.tools.ts_code_map import make_code_map
|
|
33
33
|
|
|
34
34
|
tokens = command.split()
|
|
35
35
|
if len(tokens) > 1:
|
|
@@ -37,8 +37,7 @@ def handle_meta_command(command: str, console: Console) -> bool:
|
|
|
37
37
|
else:
|
|
38
38
|
target_dir = os.getcwd()
|
|
39
39
|
try:
|
|
40
|
-
|
|
41
|
-
console.print(tree)
|
|
40
|
+
make_code_map(target_dir, ignore_tests=True)
|
|
42
41
|
except Exception as e:
|
|
43
42
|
console.print(f"[red]Error generating code map:[/red] {e}")
|
|
44
43
|
return True
|
code_puppy/tools/common.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import fnmatch
|
|
2
3
|
|
|
3
4
|
from typing import Optional, Tuple
|
|
4
5
|
|
|
@@ -8,6 +9,56 @@ from rich.console import Console
|
|
|
8
9
|
NO_COLOR = bool(int(os.environ.get("CODE_PUPPY_NO_COLOR", "0")))
|
|
9
10
|
console = Console(no_color=NO_COLOR)
|
|
10
11
|
|
|
12
|
+
|
|
13
|
+
# -------------------
|
|
14
|
+
# Shared ignore patterns/helpers
|
|
15
|
+
# -------------------
|
|
16
|
+
IGNORE_PATTERNS = [
|
|
17
|
+
"**/node_modules/**",
|
|
18
|
+
"**/node_modules/**/*.js",
|
|
19
|
+
"node_modules/**",
|
|
20
|
+
"node_modules",
|
|
21
|
+
"**/.git/**",
|
|
22
|
+
"**/.git",
|
|
23
|
+
".git/**",
|
|
24
|
+
".git",
|
|
25
|
+
"**/__pycache__/**",
|
|
26
|
+
"**/__pycache__",
|
|
27
|
+
"__pycache__/**",
|
|
28
|
+
"__pycache__",
|
|
29
|
+
"**/.DS_Store",
|
|
30
|
+
".DS_Store",
|
|
31
|
+
"**/.env",
|
|
32
|
+
".env",
|
|
33
|
+
"**/.venv/**",
|
|
34
|
+
"**/.venv",
|
|
35
|
+
"**/venv/**",
|
|
36
|
+
"**/venv",
|
|
37
|
+
"**/.idea/**",
|
|
38
|
+
"**/.idea",
|
|
39
|
+
"**/.vscode/**",
|
|
40
|
+
"**/.vscode",
|
|
41
|
+
"**/dist/**",
|
|
42
|
+
"**/dist",
|
|
43
|
+
"**/build/**",
|
|
44
|
+
"**/build",
|
|
45
|
+
"**/*.pyc",
|
|
46
|
+
"**/*.pyo",
|
|
47
|
+
"**/*.pyd",
|
|
48
|
+
"**/*.so",
|
|
49
|
+
"**/*.dll",
|
|
50
|
+
"**/.*",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def should_ignore_path(path: str) -> bool:
|
|
55
|
+
"""Return True if *path* matches any pattern in IGNORE_PATTERNS."""
|
|
56
|
+
for pattern in IGNORE_PATTERNS:
|
|
57
|
+
if fnmatch.fnmatch(path, pattern):
|
|
58
|
+
return True
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
|
|
11
62
|
JW_THRESHOLD = 0.95
|
|
12
63
|
|
|
13
64
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# file_operations.py
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import os
|
|
4
4
|
from typing import Any, Dict, List
|
|
5
5
|
|
|
@@ -10,50 +10,7 @@ from code_puppy.tools.common import console
|
|
|
10
10
|
# ---------------------------------------------------------------------------
|
|
11
11
|
# Module-level helper functions (exposed for unit tests _and_ used as tools)
|
|
12
12
|
# ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
"**/node_modules/**",
|
|
15
|
-
"**/node_modules/**/*.js",
|
|
16
|
-
"node_modules/**",
|
|
17
|
-
"node_modules",
|
|
18
|
-
"**/.git/**",
|
|
19
|
-
"**/.git",
|
|
20
|
-
".git/**",
|
|
21
|
-
".git",
|
|
22
|
-
"**/__pycache__/**",
|
|
23
|
-
"**/__pycache__",
|
|
24
|
-
"__pycache__/**",
|
|
25
|
-
"__pycache__",
|
|
26
|
-
"**/.DS_Store",
|
|
27
|
-
".DS_Store",
|
|
28
|
-
"**/.env",
|
|
29
|
-
".env",
|
|
30
|
-
"**/.venv/**",
|
|
31
|
-
"**/.venv",
|
|
32
|
-
"**/venv/**",
|
|
33
|
-
"**/venv",
|
|
34
|
-
"**/.idea/**",
|
|
35
|
-
"**/.idea",
|
|
36
|
-
"**/.vscode/**",
|
|
37
|
-
"**/.vscode",
|
|
38
|
-
"**/dist/**",
|
|
39
|
-
"**/dist",
|
|
40
|
-
"**/build/**",
|
|
41
|
-
"**/build",
|
|
42
|
-
"**/*.pyc",
|
|
43
|
-
"**/*.pyo",
|
|
44
|
-
"**/*.pyd",
|
|
45
|
-
"**/*.so",
|
|
46
|
-
"**/*.dll",
|
|
47
|
-
"**/*.exe",
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def should_ignore_path(path: str) -> bool:
|
|
52
|
-
"""Return True if *path* matches any pattern in IGNORE_PATTERNS."""
|
|
53
|
-
for pattern in IGNORE_PATTERNS:
|
|
54
|
-
if fnmatch.fnmatch(path, pattern):
|
|
55
|
-
return True
|
|
56
|
-
return False
|
|
13
|
+
from code_puppy.tools.common import should_ignore_path
|
|
57
14
|
|
|
58
15
|
|
|
59
16
|
def _list_files(
|
|
@@ -323,3 +280,20 @@ def register_file_operations_tools(agent):
|
|
|
323
280
|
context: RunContext, search_string: str, directory: str = "."
|
|
324
281
|
) -> List[Dict[str, Any]]:
|
|
325
282
|
return _grep(context, search_string, directory)
|
|
283
|
+
|
|
284
|
+
@agent.tool
|
|
285
|
+
def code_map(context: RunContext, directory: str = ".") -> str:
|
|
286
|
+
"""Generate a code map for the specified directory.
|
|
287
|
+
This will have a list of all function / class names and nested structure
|
|
288
|
+
Args:
|
|
289
|
+
context: The context object.
|
|
290
|
+
directory: The directory to generate the code map for.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
A string containing the code map.
|
|
294
|
+
"""
|
|
295
|
+
console.print("[bold white on blue] CODE MAP [/bold white on blue]")
|
|
296
|
+
from code_puppy.tools.ts_code_map import make_code_map
|
|
297
|
+
|
|
298
|
+
result = make_code_map(directory, ignore_tests=True)
|
|
299
|
+
return result
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from code_puppy.tools.common import should_ignore_path
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from rich.text import Text
|
|
5
|
+
from rich.tree import Tree as RichTree
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from tree_sitter_language_pack import get_parser
|
|
8
|
+
|
|
9
|
+
from functools import partial, wraps
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _f(fmt): # helper to keep the table tidy
|
|
13
|
+
return lambda name, _fmt=fmt: _fmt.format(name=name)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def mark_export(label_fn, default=False):
|
|
17
|
+
"""Decorator to prefix 'export ' (or 'export default ') when requested."""
|
|
18
|
+
|
|
19
|
+
@wraps(label_fn)
|
|
20
|
+
def _wrap(name, *, exported=False):
|
|
21
|
+
prefix = "export default " if default else "export " if exported else ""
|
|
22
|
+
return prefix + label_fn(name)
|
|
23
|
+
|
|
24
|
+
return _wrap
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
LANGS = {
|
|
28
|
+
".py": {
|
|
29
|
+
"lang": "python",
|
|
30
|
+
"name_field": "name",
|
|
31
|
+
"nodes": {
|
|
32
|
+
"function_definition": partial(_f("def {name}()"), style="green"),
|
|
33
|
+
"class_definition": partial(_f("class {name}"), style="magenta"),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
".rb": {
|
|
37
|
+
"lang": "ruby",
|
|
38
|
+
"name_field": "name",
|
|
39
|
+
"nodes": {
|
|
40
|
+
"method": partial(_f("def {name}"), style="green"),
|
|
41
|
+
"class": partial(_f("class {name}"), style="magenta"),
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
".php": {
|
|
45
|
+
"lang": "php",
|
|
46
|
+
"name_field": "name",
|
|
47
|
+
"nodes": {
|
|
48
|
+
"function_definition": partial(_f("function {name}()"), style="green"),
|
|
49
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
".lua": {
|
|
53
|
+
"lang": "lua",
|
|
54
|
+
"name_field": "name",
|
|
55
|
+
"nodes": {
|
|
56
|
+
"function_declaration": partial(_f("function {name}()"), style="green")
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
".pl": {
|
|
60
|
+
"lang": "perl",
|
|
61
|
+
"name_field": "name",
|
|
62
|
+
"nodes": {"sub_definition": partial(_f("sub {name}()"), style="green")},
|
|
63
|
+
},
|
|
64
|
+
".r": {
|
|
65
|
+
"lang": "r",
|
|
66
|
+
"name_field": "name",
|
|
67
|
+
"nodes": {"function_definition": partial(_f("func {name}()"), style="green")},
|
|
68
|
+
},
|
|
69
|
+
".js": {
|
|
70
|
+
"lang": "javascript",
|
|
71
|
+
"name_field": "name",
|
|
72
|
+
"nodes": {
|
|
73
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
74
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
75
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
76
|
+
"export_default_statement": partial(
|
|
77
|
+
_f("export default {name}"), style="yellow"
|
|
78
|
+
),
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
".mjs": {
|
|
82
|
+
"lang": "javascript",
|
|
83
|
+
"name_field": "name",
|
|
84
|
+
"nodes": {
|
|
85
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
86
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
87
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
88
|
+
"export_default_statement": partial(
|
|
89
|
+
_f("export default {name}"), style="yellow"
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
".cjs": {
|
|
94
|
+
"lang": "javascript",
|
|
95
|
+
"name_field": "name",
|
|
96
|
+
"nodes": {
|
|
97
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
98
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
99
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
100
|
+
"export_default_statement": partial(
|
|
101
|
+
_f("export default {name}"), style="yellow"
|
|
102
|
+
),
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
".jsx": {
|
|
106
|
+
"lang": "jsx",
|
|
107
|
+
"name_field": None,
|
|
108
|
+
"nodes": {
|
|
109
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
110
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
111
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
".ts": {
|
|
115
|
+
"lang": "tsx",
|
|
116
|
+
"name_field": None,
|
|
117
|
+
"nodes": {
|
|
118
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
119
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
120
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
".tsx": {
|
|
124
|
+
"lang": "tsx",
|
|
125
|
+
"name_field": None,
|
|
126
|
+
"nodes": {
|
|
127
|
+
"function_declaration": partial(_f("function {name}()"), style="green"),
|
|
128
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
129
|
+
"export_statement": partial(_f("export {name}"), style="yellow"),
|
|
130
|
+
"interface_declaration": partial(_f("interface {name}"), style="green"),
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
# ───────── systems / compiled ────────────────────────────────────
|
|
134
|
+
".c": {
|
|
135
|
+
"lang": "c",
|
|
136
|
+
"name_field": "declarator", # struct ident is under declarator
|
|
137
|
+
"nodes": {
|
|
138
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
139
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
".h": {
|
|
143
|
+
"lang": "c",
|
|
144
|
+
"name_field": "declarator", # struct ident is under declarator
|
|
145
|
+
"nodes": {
|
|
146
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
147
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
".cpp": {
|
|
151
|
+
"lang": "cpp",
|
|
152
|
+
"name_field": "declarator",
|
|
153
|
+
"nodes": {
|
|
154
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
155
|
+
"class_specifier": partial(_f("class {name}"), style="magenta"),
|
|
156
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
".hpp": {
|
|
160
|
+
"lang": "cpp",
|
|
161
|
+
"name_field": "declarator",
|
|
162
|
+
"nodes": {
|
|
163
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
164
|
+
"class_specifier": partial(_f("class {name}"), style="magenta"),
|
|
165
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
".cc": {
|
|
169
|
+
"lang": "cpp",
|
|
170
|
+
"name_field": "declarator",
|
|
171
|
+
"nodes": {
|
|
172
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
173
|
+
"class_specifier": partial(_f("class {name}"), style="magenta"),
|
|
174
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
".hh": {
|
|
178
|
+
"lang": "cpp",
|
|
179
|
+
"name_field": "declarator",
|
|
180
|
+
"nodes": {
|
|
181
|
+
"function_definition": partial(_f("fn {name}()"), style="green"),
|
|
182
|
+
"class_specifier": partial(_f("class {name}"), style="magenta"),
|
|
183
|
+
"struct_specifier": partial(_f("struct {name}"), style="magenta"),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
".cs": {
|
|
187
|
+
"lang": "c_sharp",
|
|
188
|
+
"name_field": "name",
|
|
189
|
+
"nodes": {
|
|
190
|
+
"method_declaration": partial(_f("method {name}()"), style="green"),
|
|
191
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
".java": {
|
|
195
|
+
"lang": "java",
|
|
196
|
+
"name_field": "name",
|
|
197
|
+
"nodes": {
|
|
198
|
+
"method_declaration": partial(_f("method {name}()"), style="green"),
|
|
199
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
".kt": {
|
|
203
|
+
"lang": "kotlin",
|
|
204
|
+
"name_field": "name",
|
|
205
|
+
"nodes": {
|
|
206
|
+
"function_declaration": partial(_f("fun {name}()"), style="green"),
|
|
207
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
".swift": {
|
|
211
|
+
"lang": "swift",
|
|
212
|
+
"name_field": "name",
|
|
213
|
+
"nodes": {
|
|
214
|
+
"function_declaration": partial(_f("func {name}()"), style="green"),
|
|
215
|
+
"class_declaration": partial(_f("class {name}"), style="magenta"),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
".go": {
|
|
219
|
+
"lang": "go",
|
|
220
|
+
"name_field": "name",
|
|
221
|
+
"nodes": {
|
|
222
|
+
"function_declaration": partial(_f("func {name}()"), style="green"),
|
|
223
|
+
"type_spec": partial(_f("type {name}"), style="magenta"),
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
".rs": {
|
|
227
|
+
"lang": "rust",
|
|
228
|
+
"name_field": "name",
|
|
229
|
+
"nodes": {
|
|
230
|
+
"function_item": partial(_f("fn {name}()"), style="green"),
|
|
231
|
+
"struct_item": partial(_f("struct {name}"), style="magenta"),
|
|
232
|
+
"trait_item": partial(_f("trait {name}"), style="magenta"),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
".zig": {
|
|
236
|
+
"lang": "zig",
|
|
237
|
+
"name_field": "name",
|
|
238
|
+
"nodes": {
|
|
239
|
+
"fn_proto": partial(_f("fn {name}()"), style="green"),
|
|
240
|
+
"struct_decl": partial(_f("struct {name}"), style="magenta"),
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
".scala": {
|
|
244
|
+
"lang": "scala",
|
|
245
|
+
"name_field": "name",
|
|
246
|
+
"nodes": {
|
|
247
|
+
"function_definition": partial(_f("def {name}()"), style="green"),
|
|
248
|
+
"class_definition": partial(_f("class {name}"), style="magenta"),
|
|
249
|
+
"object_definition": partial(_f("object {name}"), style="magenta"),
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
".hs": {
|
|
253
|
+
"lang": "haskell",
|
|
254
|
+
"name_field": "name",
|
|
255
|
+
"nodes": {
|
|
256
|
+
"function_declaration": partial(_f("fun {name}"), style="green"),
|
|
257
|
+
"type_declaration": partial(_f("type {name}"), style="magenta"),
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
".jl": {
|
|
261
|
+
"lang": "julia",
|
|
262
|
+
"name_field": "name",
|
|
263
|
+
"nodes": {
|
|
264
|
+
"function_definition": partial(_f("function {name}()"), style="green"),
|
|
265
|
+
"abstract_type_definition": partial(_f("abstract {name}"), style="magenta"),
|
|
266
|
+
"struct_definition": partial(_f("struct {name}"), style="magenta"),
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
# ───────── scripting (shell / infra) ─────────────────────────────
|
|
270
|
+
".sh": {
|
|
271
|
+
"lang": "bash",
|
|
272
|
+
"name_field": "name",
|
|
273
|
+
"nodes": {"function_definition": partial(_f("fn {name}()"), style="green")},
|
|
274
|
+
},
|
|
275
|
+
".ps1": {
|
|
276
|
+
"lang": "powershell",
|
|
277
|
+
"name_field": "name",
|
|
278
|
+
"nodes": {
|
|
279
|
+
"function_definition": partial(_f("function {name}()"), style="green")
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
".css": {
|
|
283
|
+
"lang": "css",
|
|
284
|
+
"name_field": "name",
|
|
285
|
+
"nodes": {
|
|
286
|
+
"class_selector": partial(_f(".{name}"), style="yellow"),
|
|
287
|
+
"id_selector": partial(_f("#{name}"), style="yellow"),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
# Cache parsers so we don’t re-create them file-after-file
|
|
293
|
+
_PARSER_CACHE = {}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def parser_for(lang_name):
|
|
297
|
+
if lang_name not in _PARSER_CACHE:
|
|
298
|
+
_PARSER_CACHE[lang_name] = get_parser(lang_name)
|
|
299
|
+
return _PARSER_CACHE[lang_name]
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# ----------------------------------------------------------------------
|
|
303
|
+
# helper: breadth-first search for an identifier-ish node
|
|
304
|
+
# ----------------------------------------------------------------------
|
|
305
|
+
def _first_identifier(node):
|
|
306
|
+
from collections import deque
|
|
307
|
+
|
|
308
|
+
q = deque([node])
|
|
309
|
+
while q:
|
|
310
|
+
n = q.popleft()
|
|
311
|
+
if n.type in {"identifier", "property_identifier", "type_identifier"}:
|
|
312
|
+
return n
|
|
313
|
+
q.extend(n.children)
|
|
314
|
+
return None
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _span(node):
|
|
318
|
+
"""Return "[start:end]" lines (1‑based, inclusive)."""
|
|
319
|
+
start_line = node.start_point[0] + 1
|
|
320
|
+
end_line = node.end_point[0] + 1
|
|
321
|
+
return Text(f" [{start_line}:{end_line}]", style="bold white")
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _walk(ts_node, rich_parent, info):
|
|
325
|
+
nodes_cfg = info["nodes"]
|
|
326
|
+
name_field = info["name_field"]
|
|
327
|
+
|
|
328
|
+
for child in ts_node.children:
|
|
329
|
+
t = child.type
|
|
330
|
+
if t in nodes_cfg:
|
|
331
|
+
style = nodes_cfg[t].keywords["style"]
|
|
332
|
+
|
|
333
|
+
if name_field:
|
|
334
|
+
ident = child.child_by_field_name(name_field)
|
|
335
|
+
else:
|
|
336
|
+
ident = _first_identifier(child)
|
|
337
|
+
|
|
338
|
+
label_text = ident.text.decode() if ident else "<anon>"
|
|
339
|
+
label = nodes_cfg[t].func(label_text)
|
|
340
|
+
branch = rich_parent.add(Text(label, style=style) + _span(child))
|
|
341
|
+
_walk(child, branch, info)
|
|
342
|
+
else:
|
|
343
|
+
_walk(child, rich_parent, info)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def map_code_file(filepath):
|
|
347
|
+
ext = Path(filepath).suffix
|
|
348
|
+
info = LANGS.get(ext)
|
|
349
|
+
if not info:
|
|
350
|
+
return None
|
|
351
|
+
|
|
352
|
+
code = Path(filepath).read_bytes()
|
|
353
|
+
parser = parser_for(info["lang"])
|
|
354
|
+
tree = parser.parse(code)
|
|
355
|
+
|
|
356
|
+
root_label = Path(filepath).name
|
|
357
|
+
base = RichTree(Text(root_label, style="bold cyan"))
|
|
358
|
+
|
|
359
|
+
if tree.root_node.has_error:
|
|
360
|
+
base.add(Text("⚠️ syntax error", style="bold red"))
|
|
361
|
+
|
|
362
|
+
_walk(tree.root_node, base, info)
|
|
363
|
+
return base
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def make_code_map(directory: str, ignore_tests: bool = True) -> str:
|
|
367
|
+
base_tree = RichTree(Text(Path(directory).name, style="bold magenta"))
|
|
368
|
+
|
|
369
|
+
for root, dirs, files in os.walk(directory):
|
|
370
|
+
dirs[:] = [d for d in dirs if not d.startswith(".")]
|
|
371
|
+
for f in files:
|
|
372
|
+
if (
|
|
373
|
+
should_ignore_path(os.path.join(root, f))
|
|
374
|
+
or ignore_tests
|
|
375
|
+
and "test" in f
|
|
376
|
+
):
|
|
377
|
+
continue
|
|
378
|
+
try:
|
|
379
|
+
file_tree = map_code_file(os.path.join(root, f))
|
|
380
|
+
if file_tree is not None:
|
|
381
|
+
base_tree.add(file_tree)
|
|
382
|
+
except Exception:
|
|
383
|
+
base_tree.add(Text(f"[error reading {f}]", style="bold red"))
|
|
384
|
+
|
|
385
|
+
buf = Console(record=True, width=120)
|
|
386
|
+
buf.print(base_tree)
|
|
387
|
+
return buf.export_text()[-1000:]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-puppy
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.62
|
|
4
4
|
Summary: Code generation agent
|
|
5
5
|
Author: Michael Pfaffenberger
|
|
6
6
|
License: MIT
|
|
@@ -27,6 +27,8 @@ Requires-Dist: python-dotenv>=1.0.0
|
|
|
27
27
|
Requires-Dist: rapidfuzz>=3.13.0
|
|
28
28
|
Requires-Dist: rich>=13.4.2
|
|
29
29
|
Requires-Dist: ruff>=0.11.11
|
|
30
|
+
Requires-Dist: tree-sitter-language-pack>=0.8.0
|
|
31
|
+
Requires-Dist: tree-sitter-typescript>=0.23.2
|
|
30
32
|
Description-Content-Type: text/markdown
|
|
31
33
|
|
|
32
34
|
# 🐶 Code Puppy 🐶
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
code_puppy/__init__.py,sha256=-ANvE6Xe5NlWDIRCIfL1x-rgtCZ6zM2Ye9NphFoULSY,82
|
|
2
2
|
code_puppy/agent.py,sha256=QRBid--LcJv8fbhS9u6Foavgt3jI1B-K_1Arv-kXQsc,3420
|
|
3
|
-
code_puppy/agent_prompts.py,sha256=
|
|
3
|
+
code_puppy/agent_prompts.py,sha256=DnPNubiFtD6Ot76cIsxedJEaEVYS5AaxR3JWJM5nXNg,6925
|
|
4
4
|
code_puppy/config.py,sha256=LTcZe5eHTT0zq-YJzSeNg8LCwhHb1HGN4tOkITtb7Eo,3941
|
|
5
5
|
code_puppy/main.py,sha256=qDbDB123MAv5r_kH91x1LxUROI9I28y2tvuYo2YxEbQ,10323
|
|
6
6
|
code_puppy/model_factory.py,sha256=jZH96zE5GMTK3RWjUpZB4HjbSO3fSaAzjRoQh8Y8lfg,7711
|
|
@@ -9,20 +9,20 @@ code_puppy/session_memory.py,sha256=4sgAAjbXdLSi8hETpd56tgtrG6hqMUuZWDlJOu6BQjA,
|
|
|
9
9
|
code_puppy/version_checker.py,sha256=aRGulzuY4C4CdFvU1rITduyL-1xTFsn4GiD1uSfOl_Y,396
|
|
10
10
|
code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZv_YRmU,45
|
|
11
11
|
code_puppy/command_line/file_path_completion.py,sha256=gw8NpIxa6GOpczUJRyh7VNZwoXKKn-yvCqit7h2y6Gg,2931
|
|
12
|
-
code_puppy/command_line/meta_command_handler.py,sha256=
|
|
12
|
+
code_puppy/command_line/meta_command_handler.py,sha256=aKSPu_aB98TAuaFwyI4HmrFd45dk6mrhnvR-WvnQuQA,5713
|
|
13
13
|
code_puppy/command_line/model_picker_completion.py,sha256=NkyZZG7IhcVWSJ3ADytwCA5f8DpNeVs759Qtqs4fQtY,3733
|
|
14
14
|
code_puppy/command_line/prompt_toolkit_completion.py,sha256=_gP0FIOgHDNHTTWLNL0XNzr6sO0ISe7Mec1uQNo9kcM,8337
|
|
15
15
|
code_puppy/command_line/utils.py,sha256=7eyxDHjPjPB9wGDJQQcXV_zOsGdYsFgI0SGCetVmTqE,1251
|
|
16
16
|
code_puppy/tools/__init__.py,sha256=J_HCbkivdr1rP5vfucxttXhGmTBx0S2LNoDMrbaE-Fc,558
|
|
17
|
-
code_puppy/tools/code_map.py,sha256=5vzKBUddY0z9kMfHZmLiewUMJofDOONJIaXCWVhbE5E,3201
|
|
18
17
|
code_puppy/tools/command_runner.py,sha256=cEphrDyr12CoqpKizzaxhMHhG1HiVcqXArTkwc6lvxU,6760
|
|
19
|
-
code_puppy/tools/common.py,sha256=
|
|
18
|
+
code_puppy/tools/common.py,sha256=4AmPc_jx45nhJBS1TMhqF-i9pJZzmyOmYBcrqFAhSIE,2275
|
|
20
19
|
code_puppy/tools/file_modifications.py,sha256=nEHvdHJ1ige82gguf7-dybD0_e5AYFVARsYFgaTGLfY,13056
|
|
21
|
-
code_puppy/tools/file_operations.py,sha256=
|
|
20
|
+
code_puppy/tools/file_operations.py,sha256=xqj7MiK6sGTaC8NMrMRTybWlXG32S-DjybqJtnufRcI,11715
|
|
21
|
+
code_puppy/tools/ts_code_map.py,sha256=tVUhSmStB548l1MMlbi0m0EPphuaY4f-vLhd-hnC5vU,13222
|
|
22
22
|
code_puppy/tools/web_search.py,sha256=sA2ierjuuYA517-uhb5s53SgeVsyOe1nExoZsrU1Fps,1284
|
|
23
|
-
code_puppy-0.0.
|
|
24
|
-
code_puppy-0.0.
|
|
25
|
-
code_puppy-0.0.
|
|
26
|
-
code_puppy-0.0.
|
|
27
|
-
code_puppy-0.0.
|
|
28
|
-
code_puppy-0.0.
|
|
23
|
+
code_puppy-0.0.62.data/data/code_puppy/models.json,sha256=3tiU7_ra39CK8aOFRsVqG7veZDdzq8hAmI5BcfZEWck,1393
|
|
24
|
+
code_puppy-0.0.62.dist-info/METADATA,sha256=6K7yEUuD-BGC6Nc94vrH3A2vSXxhAglEkNGoXbk3lII,4878
|
|
25
|
+
code_puppy-0.0.62.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
code_puppy-0.0.62.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
|
|
27
|
+
code_puppy-0.0.62.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
|
28
|
+
code_puppy-0.0.62.dist-info/RECORD,,
|
code_puppy/tools/code_map.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import ast
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
import pathspec
|
|
5
|
-
from rich.text import Text
|
|
6
|
-
from rich.tree import Tree
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def summarize_node(node: ast.AST) -> str:
|
|
10
|
-
if isinstance(node, ast.ClassDef):
|
|
11
|
-
return f"class {node.name}"
|
|
12
|
-
if isinstance(node, ast.FunctionDef):
|
|
13
|
-
return f"def {node.name}()"
|
|
14
|
-
return ""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def get_docstring(node: ast.AST) -> str:
|
|
18
|
-
doc = ast.get_docstring(node)
|
|
19
|
-
if doc:
|
|
20
|
-
lines = doc.strip().split("\n")
|
|
21
|
-
return lines[0] if lines else doc.strip()
|
|
22
|
-
return ""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def map_python_file(file_path: str, show_doc: bool = True) -> Tree:
|
|
26
|
-
tree = Tree(Text(file_path, style="bold cyan"))
|
|
27
|
-
with open(file_path, "r", encoding="utf-8") as f:
|
|
28
|
-
root = ast.parse(f.read(), filename=file_path)
|
|
29
|
-
for node in root.body:
|
|
30
|
-
summary = summarize_node(node)
|
|
31
|
-
if summary:
|
|
32
|
-
t = Tree(summary)
|
|
33
|
-
if show_doc:
|
|
34
|
-
doc = get_docstring(node)
|
|
35
|
-
if doc:
|
|
36
|
-
t.add(Text(f'"{doc}"', style="dim"))
|
|
37
|
-
# Add inner functions
|
|
38
|
-
if hasattr(node, "body"):
|
|
39
|
-
for subnode in getattr(node, "body"):
|
|
40
|
-
subsum = summarize_node(subnode)
|
|
41
|
-
if subsum:
|
|
42
|
-
sub_t = Tree(subsum)
|
|
43
|
-
doc2 = get_docstring(subnode)
|
|
44
|
-
if doc2:
|
|
45
|
-
sub_t.add(Text(f'"{doc2}"', style="dim"))
|
|
46
|
-
t.add(sub_t)
|
|
47
|
-
tree.add(t)
|
|
48
|
-
return tree
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def load_gitignore(directory: str):
|
|
52
|
-
gitignore_file = os.path.join(directory, ".gitignore")
|
|
53
|
-
if os.path.exists(gitignore_file):
|
|
54
|
-
with open(gitignore_file, "r") as f:
|
|
55
|
-
spec = pathspec.PathSpec.from_lines("gitwildmatch", f)
|
|
56
|
-
return spec
|
|
57
|
-
else:
|
|
58
|
-
return pathspec.PathSpec.from_lines("gitwildmatch", [])
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def make_code_map(directory: str, show_doc: bool = True) -> Tree:
|
|
62
|
-
"""
|
|
63
|
-
Recursively build a Tree displaying the code structure of all .py files in a directory,
|
|
64
|
-
ignoring files listed in .gitignore if present.
|
|
65
|
-
"""
|
|
66
|
-
base_tree = Tree(Text(directory, style="bold magenta"))
|
|
67
|
-
|
|
68
|
-
spec = load_gitignore(directory)
|
|
69
|
-
abs_directory = os.path.abspath(directory)
|
|
70
|
-
|
|
71
|
-
for root, dirs, files in os.walk(directory):
|
|
72
|
-
rel_root = os.path.relpath(root, abs_directory)
|
|
73
|
-
# Remove ignored directories in-place for os.walk to not descend
|
|
74
|
-
dirs[:] = [
|
|
75
|
-
d
|
|
76
|
-
for d in dirs
|
|
77
|
-
if not spec.match_file(os.path.normpath(os.path.join(rel_root, d)))
|
|
78
|
-
]
|
|
79
|
-
for fname in files:
|
|
80
|
-
rel_file = os.path.normpath(os.path.join(rel_root, fname))
|
|
81
|
-
if fname.endswith(".py") and not fname.startswith("__"):
|
|
82
|
-
if not spec.match_file(rel_file):
|
|
83
|
-
fpath = os.path.join(root, fname)
|
|
84
|
-
try:
|
|
85
|
-
file_tree = map_python_file(fpath, show_doc=show_doc)
|
|
86
|
-
base_tree.add(file_tree)
|
|
87
|
-
except Exception as e:
|
|
88
|
-
err = Tree(
|
|
89
|
-
Text(f"[error reading {fname}: {e}]", style="bold red")
|
|
90
|
-
)
|
|
91
|
-
base_tree.add(err)
|
|
92
|
-
return base_tree
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|