code-puppy 0.0.45__py3-none-any.whl → 0.0.47__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.
@@ -27,52 +27,49 @@ YOU MUST USE THESE TOOLS to complete tasks (do not just describe what should be
27
27
  File Operations:
28
28
  - list_files(directory=".", recursive=True): ALWAYS use this to explore directories before trying to read/modify files
29
29
  - read_file(file_path): ALWAYS use this to read existing files before modifying them.
30
- - write_to_file(path, content): Use this to write or overwrite files with complete content.
31
- - replace_in_file(path, diff): Use this to make exact replacements in a file using JSON format.
32
- - delete_snippet_from_file(file_path, snippet): Use this to remove specific code snippets from files
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.
33
31
  - delete_file(file_path): Use this to remove files when needed
34
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.
35
- - grab_json_from_url(url: str): Use this to grab JSON data from a specified URL, ensuring the response is of type application/json. It raises an error if the response type is not application/json and limits the output to 1000 lines.
36
33
 
37
34
  Tool Usage Instructions:
38
35
 
39
- ## write_to_file
40
- Use this when you need to create a new file or completely replace an existing file's contents.
41
- - path: The path to the file (required)
42
- - content: The COMPLETE content of the file (required)
36
+ ## edit_file
37
+ This is an all-in-one file-modification tool. It supports the following payload shapes for the `diff` argument:
38
+ 1. {{ "content": "…", "overwrite": true|false }} → Treated as full-file content when the target file does **not** exist.
39
+ 2. {{ "content": "…", "overwrite": true|false }} → Create or overwrite a file with the provided content.
40
+ 3. {{ "replacements": [ {{ "old_str": "…", "new_str": "…" }}, … ] }} → Perform exact text replacements inside an existing file.
41
+ 4. {{ "delete_snippet": "…" }} → Remove a snippet of text from an existing file.
43
42
 
44
- Example:
45
- ```
46
- write_to_file(
47
- path="path/to/file.txt",
48
- content="Complete content of the file here..."
49
- )
50
- ```
43
+ Arguments:
44
+ - path (required): Target file path.
45
+ - diff (required): One of the payloads above (raw string or JSON string).
51
46
 
52
- ## replace_in_file
53
- Use this to make targeted replacements in an existing file. Each replacement must match exactly what's in the file.
54
- - path: The path to the file (required)
55
- - diff: JSON string with replacements (required)
56
-
57
- The diff parameter should be a JSON string in this format:
47
+ Example (create):
58
48
  ```json
59
- {{
60
- "replacements": [
61
- {{
62
- "old_str": "exact string from file",
63
- "new_str": "replacement string"
64
- }}
65
- ]
66
- }}
49
+ edit_file("src/example.py", "print('hello')\n")
67
50
  ```
68
51
 
69
- For grab_json_from_url, this is super useful for hitting a swagger doc or openapi doc. That will allow you to
70
- write correct code to hit the API.
52
+ Example (replacement):
53
+ ```json
54
+ edit_file(
55
+ "src/example.py",
56
+ "{{"replacements":[{{"old_str":"foo","new_str":"bar"}}]}}"
57
+ )
58
+ ```
71
59
 
72
- NEVER output an entire file, this is very expensive.
60
+ NEVER output an entire file this is very expensive.
73
61
  You may not edit file extensions: [.ipynb]
74
62
  You should specify the following arguments before the others: [TargetFile]
75
63
 
64
+ Remember: ONE argument = ONE JSON string.
65
+
66
+ Best-practice guidelines for `edit_file`:
67
+ • Keep each diff small – ideally between 100-300 lines.
68
+ • Apply multiple sequential `edit_file` calls when you need to refactor large files instead of sending one massive diff.
69
+ • Never paste an entire file inside `old_str`; target only the minimal snippet you want changed.
70
+ • If the resulting file would grow beyond 600 lines, split logic into additional files and create them with separate `edit_file` calls.
71
+
72
+
76
73
  System Operations:
77
74
  - run_shell_command(command, cwd=None, timeout=60): Use this to execute commands, run tests, or start services
78
75
 
@@ -88,6 +85,8 @@ In the event that you want to see the entire output for the test, run a single t
88
85
 
89
86
  npm test -- ./path/to/test/file.tsx # or something like this.
90
87
 
88
+ DONT USE THE TERMINAL TOOL TO RUN THE CODE WE WROTE UNLESS THE USER ASKS YOU TO.
89
+
91
90
  Reasoning & Explanation:
92
91
  - share_your_reasoning(reasoning, next_steps=None): Use this to explicitly share your thought process and planned next steps
93
92
 
@@ -95,7 +94,7 @@ Important rules:
95
94
  - You MUST use tools to accomplish tasks - DO NOT just output code or descriptions
96
95
  - Before every other tool use, you must use "share_your_reasoning" to explain your thought process and planned next steps
97
96
  - Check if files exist before trying to modify or delete them
98
- - Whenever possible, prefer to MODIFY existing files first (use `replace_in_file`, `delete_snippet_from_file`, or `write_to_file`) before creating brand-new files or deleting existing ones.
97
+ - Whenever possible, prefer to MODIFY existing files first (use `edit_file`) before creating brand-new files or deleting existing ones.
99
98
  - After using system operations tools, always explain the results
100
99
  - You're encouraged to loop between share_your_reasoning, file tools, and run_shell_command to test output in order to write programs
101
100
  - Aim to continue operations independently unless user input is definitively required.
@@ -90,6 +90,10 @@ async def get_input_with_combined_completion(prompt_str = '>>> ', history_file:
90
90
  @bindings.add(Keys.Escape, 'm') # Alt+M
91
91
  def _(event):
92
92
  event.app.current_buffer.insert_text('\n')
93
+ @bindings.add(Keys.Escape)
94
+ def _(event):
95
+ """Cancel the current prompt when the user presses the ESC key alone."""
96
+ event.app.exit(exception=KeyboardInterrupt)
93
97
 
94
98
  session = PromptSession(
95
99
  completer=completer,
@@ -6,8 +6,104 @@ from code_puppy.tools.common import console
6
6
  from typing import Dict, Any, List
7
7
  from pydantic_ai import RunContext
8
8
 
9
+ # ---------------------------------------------------------------------------
10
+ # Module-level helper functions (exposed for unit tests; *not* registered)
11
+ # ---------------------------------------------------------------------------
12
+
13
+ def delete_snippet_from_file(context: RunContext | None, file_path: str, snippet: str) -> Dict[str, Any]:
14
+ """Remove *snippet* from *file_path* if present, returning a diff summary."""
15
+ file_path = os.path.abspath(file_path)
16
+ try:
17
+ if not os.path.exists(file_path) or not os.path.isfile(file_path):
18
+ return {"error": f"File '{file_path}' does not exist."}
19
+ with open(file_path, "r", encoding="utf-8") as f:
20
+ content = f.read()
21
+ if snippet not in content:
22
+ return {"error": f"Snippet not found in file '{file_path}'."}
23
+ modified_content = content.replace(snippet, "")
24
+ with open(file_path, "w", encoding="utf-8") as f:
25
+ f.write(modified_content)
26
+ return {"success": True, "path": file_path, "message": "Snippet deleted from file."}
27
+ except PermissionError:
28
+ return {"error": f"Permission denied to modify '{file_path}'."}
29
+ except FileNotFoundError:
30
+ return {"error": f"File '{file_path}' does not exist."}
31
+ except Exception as exc:
32
+ return {"error": str(exc)}
33
+
34
+
35
+ def write_to_file(context: RunContext | None, path: str, content: str) -> Dict[str, Any]:
36
+ file_path = os.path.abspath(path)
37
+ if os.path.exists(file_path):
38
+ return {
39
+ "success": False,
40
+ "path": file_path,
41
+ "message": f"Cowardly refusing to overwrite existing file: {file_path}",
42
+ "changed": False,
43
+ }
44
+ os.makedirs(os.path.dirname(file_path) or ".", exist_ok=True)
45
+ with open(file_path, "w", encoding="utf-8") as f:
46
+ f.write(content)
47
+ return {
48
+ "success": True,
49
+ "path": file_path,
50
+ "message": f"File '{file_path}' created successfully.",
51
+ "changed": True,
52
+ }
53
+
54
+
55
+ def replace_in_file(context: RunContext | None, path: str, diff: str) -> Dict[str, Any]:
56
+ file_path = os.path.abspath(path)
57
+ if not os.path.exists(file_path):
58
+ return {"error": f"File '{file_path}' does not exist"}
59
+ try:
60
+ import json, ast, difflib
61
+ preview = (diff[:200] + '...') if len(diff) > 200 else diff
62
+ try:
63
+ replacements_data = json.loads(diff)
64
+ except json.JSONDecodeError as e1:
65
+ try:
66
+ replacements_data = json.loads(diff.replace("'", '"'))
67
+ except Exception as e2:
68
+ return {
69
+ "error": "Could not parse diff as JSON.",
70
+ "reason": str(e2),
71
+ "received": preview,
72
+ }
73
+ # If still not a dict -> maybe python literal
74
+ if not isinstance(replacements_data, dict):
75
+ try:
76
+ replacements_data = ast.literal_eval(diff)
77
+ except Exception as e3:
78
+ return {
79
+ "error": "Diff is neither valid JSON nor Python literal.",
80
+ "reason": str(e3),
81
+ "received": preview,
82
+ }
83
+ replacements = replacements_data.get("replacements", []) if isinstance(replacements_data, dict) else []
84
+ if not replacements:
85
+ return {
86
+ "error": "No valid replacements found in diff.",
87
+ "received": preview,
88
+ }
89
+ with open(file_path, "r", encoding="utf-8") as f:
90
+ original = f.read()
91
+ modified = original
92
+ for rep in replacements:
93
+ modified = modified.replace(rep.get("old_str", ""), rep.get("new_str", ""))
94
+ if modified == original:
95
+ return {"success": False, "path": file_path, "message": "No changes to apply.", "changed": False}
96
+ with open(file_path, "w", encoding="utf-8") as f:
97
+ f.write(modified)
98
+ diff_text = "".join(difflib.unified_diff(original.splitlines(keepends=True), modified.splitlines(keepends=True)))
99
+ return {"success": True, "path": file_path, "message": "Replacements applied.", "diff": diff_text, "changed": True}
100
+ except Exception as exc:
101
+ return {"error": str(exc)}
102
+
103
+ # ---------------------------------------------------------------------------
104
+
9
105
  def register_file_modifications_tools(agent):
10
- @agent.tool
106
+ # @agent.tool
11
107
  def delete_snippet_from_file(context: RunContext, file_path: str, snippet: str) -> Dict[str, Any]:
12
108
  console.log(f"🗑️ Deleting snippet from file [bold red]{file_path}[/bold red]")
13
109
  file_path = os.path.abspath(file_path)
@@ -53,7 +149,7 @@ def register_file_modifications_tools(agent):
53
149
  except Exception as e:
54
150
  return {"error": f"Error deleting file '{file_path}': {str(e)}"}
55
151
 
56
- @agent.tool
152
+ # @agent.tool
57
153
  def write_to_file(context: RunContext, path: str, content: str) -> Dict[str, Any]:
58
154
  try:
59
155
  file_path = os.path.abspath(path)
@@ -78,7 +174,7 @@ def register_file_modifications_tools(agent):
78
174
  console.print(f"[bold red]Error:[/bold red] {str(e)}")
79
175
  return {"error": f"Error writing to file '{path}': {str(e)}"}
80
176
 
81
- @agent.tool(retries=5)
177
+ # @agent.tool(retries=5)
82
178
  def replace_in_file(context: RunContext, path: str, diff: str) -> Dict[str, Any]:
83
179
  try:
84
180
  file_path = os.path.abspath(path)
@@ -94,44 +190,34 @@ def register_file_modifications_tools(agent):
94
190
  # The agent sometimes sends single-quoted or otherwise invalid JSON.
95
191
  # Attempt to recover by trying several strategies before giving up.
96
192
  # ------------------------------------------------------------------
97
- parsed_successfully = False
98
- replacements: List[Dict[str, str]] = []
193
+ preview = (diff[:200] + '...') if len(diff) > 200 else diff
99
194
  try:
100
195
  replacements_data = json.loads(diff)
101
- replacements = replacements_data.get("replacements", [])
102
- parsed_successfully = True
103
- except json.JSONDecodeError:
104
- # Fallback 1: convert single quotes to double quotes and retry
196
+ except json.JSONDecodeError as e1:
105
197
  try:
106
- sanitized = diff.replace("'", '"')
107
- replacements_data = json.loads(sanitized)
108
- replacements = replacements_data.get("replacements", [])
109
- parsed_successfully = True
110
- except json.JSONDecodeError:
111
- # Fallback 2: attempt Python literal eval
112
- try:
113
- import ast
114
- replacements_data = ast.literal_eval(diff)
115
- if isinstance(replacements_data, dict):
116
- replacements = replacements_data.get("replacements", []) if "replacements" in replacements_data else []
117
- # If dict keys look like a single replacement, wrap it
118
- if not replacements:
119
- # maybe it's already {"old_str": ..., "new_str": ...}
120
- if all(k in replacements_data for k in ("old_str", "new_str")):
121
- replacements = [
122
- {
123
- "old_str": replacements_data["old_str"],
124
- "new_str": replacements_data["new_str"],
125
- }
126
- ]
127
- parsed_successfully = True
128
- except Exception as e2:
129
- console.print(
130
- f"[bold red]Error:[/bold red] Could not parse diff as JSON or Python literal. Reason: {e2}"
131
- )
132
- if not parsed_successfully or not replacements:
133
- console.print("[bold red]Error:[/bold red] No valid replacements found in the diff after all parsing attempts")
134
- return {"error": "No valid replacements found in the diff"}
198
+ replacements_data = json.loads(diff.replace("'", '"'))
199
+ except Exception as e2:
200
+ return {
201
+ "error": "Could not parse diff as JSON.",
202
+ "reason": str(e2),
203
+ "received": preview,
204
+ }
205
+ # If still not a dict -> maybe python literal
206
+ if not isinstance(replacements_data, dict):
207
+ try:
208
+ replacements_data = ast.literal_eval(diff)
209
+ except Exception as e3:
210
+ return {
211
+ "error": "Diff is neither valid JSON nor Python literal.",
212
+ "reason": str(e3),
213
+ "received": preview,
214
+ }
215
+ replacements = replacements_data.get("replacements", []) if isinstance(replacements_data, dict) else []
216
+ if not replacements:
217
+ return {
218
+ "error": "No valid replacements found in diff.",
219
+ "received": preview,
220
+ }
135
221
  with open(file_path, "r", encoding="utf-8") as f:
136
222
  current_content = f.read()
137
223
  modified_content = current_content
@@ -189,3 +275,86 @@ def register_file_modifications_tools(agent):
189
275
  return {"error": f"File '{file_path}' does not exist."}
190
276
  except Exception as e:
191
277
  return {"error": f"Error deleting file '{file_path}': {str(e)}"}
278
+
279
+ @agent.tool(retries=5)
280
+ def edit_file(context: RunContext, path: str, diff: str) -> Dict[str, Any]:
281
+ """
282
+ Unified file editing tool that can:
283
+ - Create/write a new file when the target does not exist (using raw content or a JSON payload with a "content" key)
284
+ - Replace text within an existing file via a JSON payload with "replacements" (delegates to internal replace logic)
285
+ - Delete a snippet from an existing file via a JSON payload with "delete_snippet"
286
+
287
+ Parameters
288
+ ----------
289
+ path : str
290
+ Path to the target file (relative or absolute)
291
+ diff : str
292
+ Either:
293
+ * Raw file content (for file creation)
294
+ * A JSON string with one of the following shapes:
295
+ {"content": "full file contents", "overwrite": true}
296
+ {"replacements": [ {"old_str": "foo", "new_str": "bar"}, ... ] }
297
+ {"delete_snippet": "text to remove"}
298
+
299
+ The function auto-detects the payload type and routes to the appropriate internal helper.
300
+ """
301
+ file_path = os.path.abspath(path)
302
+
303
+ # 1. Attempt to parse the incoming `diff` as JSON (robustly, allowing single quotes)
304
+ parsed_payload: Dict[str, Any] | None = None
305
+ try:
306
+ parsed_payload = json.loads(diff)
307
+ except json.JSONDecodeError:
308
+ # Fallback: try to sanitise single quotes
309
+ try:
310
+ parsed_payload = json.loads(diff.replace("'", '"'))
311
+ except Exception:
312
+ parsed_payload = None
313
+
314
+ # ------------------------------------------------------------------
315
+ # Case A: JSON payload recognised
316
+ # ------------------------------------------------------------------
317
+ if isinstance(parsed_payload, dict):
318
+ # Delete-snippet mode
319
+ if "delete_snippet" in parsed_payload:
320
+ snippet = parsed_payload["delete_snippet"]
321
+ return delete_snippet_from_file(context, file_path, snippet)
322
+
323
+ # Replacement mode
324
+ if "replacements" in parsed_payload:
325
+ # Forward the ORIGINAL diff string (not parsed) so that the existing logic
326
+ # which handles various JSON quirks can run unchanged.
327
+ return replace_in_file(context, file_path, diff)
328
+
329
+ # Write / create mode via content field
330
+ if "content" in parsed_payload:
331
+ content = parsed_payload["content"]
332
+ overwrite = bool(parsed_payload.get("overwrite", False))
333
+ file_exists = os.path.exists(file_path)
334
+ if file_exists and not overwrite:
335
+ return {"success": False, "path": file_path, "message": f"File '{file_path}' exists. Set 'overwrite': true to replace.", "changed": False}
336
+ if file_exists and overwrite:
337
+ # Overwrite directly
338
+ try:
339
+ with open(file_path, "w", encoding="utf-8") as f:
340
+ f.write(content)
341
+ return {"success": True, "path": file_path, "message": f"File '{file_path}' overwritten successfully.", "changed": True}
342
+ except Exception as e:
343
+ return {"error": f"Error overwriting file '{file_path}': {str(e)}"}
344
+ # File does not exist -> create
345
+ return write_to_file(context, file_path, content)
346
+
347
+ # ------------------------------------------------------------------
348
+ # Case B: Not JSON or unrecognised structure.
349
+ # Treat `diff` as raw content for file creation OR as replacement diff.
350
+ # ------------------------------------------------------------------
351
+ if not os.path.exists(file_path):
352
+ # Create new file with provided raw content
353
+ return write_to_file(context, file_path, diff)
354
+
355
+ # If file exists, attempt to treat the raw input as a replacement diff spec.
356
+ replacement_result = replace_in_file(context, file_path, diff)
357
+ if replacement_result.get("error"):
358
+ # Fallback: refuse to overwrite blindly
359
+ return {"success": False, "path": file_path, "message": "Unrecognised payload and cannot derive edit instructions.", "changed": False}
360
+ return replacement_result
@@ -5,6 +5,83 @@ from typing import List, Dict, Any
5
5
  from code_puppy.tools.common import console
6
6
  from pydantic_ai import RunContext
7
7
 
8
+ # ---------------------------------------------------------------------------
9
+ # Module-level helper functions (exposed for unit tests _and_ used as tools)
10
+ # ---------------------------------------------------------------------------
11
+ IGNORE_PATTERNS = [
12
+ "**/node_modules/**",
13
+ "**/.git/**",
14
+ "**/__pycache__/**",
15
+ "**/.DS_Store",
16
+ "**/.env",
17
+ "**/.venv/**",
18
+ "**/venv/**",
19
+ "**/.idea/**",
20
+ "**/.vscode/**",
21
+ "**/dist/**",
22
+ "**/build/**",
23
+ "**/*.pyc",
24
+ "**/*.pyo",
25
+ "**/*.pyd",
26
+ "**/*.so",
27
+ "**/*.dll",
28
+ "**/*.exe",
29
+ ]
30
+
31
+ def should_ignore_path(path: str) -> bool:
32
+ """Return True if *path* matches any pattern in IGNORE_PATTERNS."""
33
+ for pattern in IGNORE_PATTERNS:
34
+ if fnmatch.fnmatch(path, pattern):
35
+ return True
36
+ return False
37
+
38
+ def list_files(context: RunContext | None, directory: str = ".", recursive: bool = True) -> List[Dict[str, Any]]:
39
+ """Light-weight `list_files` implementation sufficient for unit-tests and agent tooling."""
40
+ directory = os.path.abspath(directory)
41
+ results: List[Dict[str, Any]] = []
42
+ if not os.path.exists(directory) or not os.path.isdir(directory):
43
+ return [{"error": f"Directory '{directory}' does not exist or is not a directory"}]
44
+ for root, dirs, files in os.walk(directory):
45
+ rel_root = os.path.relpath(root, directory)
46
+ if rel_root == ".":
47
+ rel_root = ""
48
+ for f in files:
49
+ fp = os.path.join(rel_root, f) if rel_root else f
50
+ results.append({"path": fp, "type": "file"})
51
+ if not recursive:
52
+ break
53
+ return results
54
+
55
+ def read_file(context: RunContext | None, file_path: str) -> Dict[str, Any]:
56
+ file_path = os.path.abspath(file_path)
57
+ if not os.path.exists(file_path):
58
+ return {"error": f"File '{file_path}' does not exist"}
59
+ if not os.path.isfile(file_path):
60
+ return {"error": f"'{file_path}' is not a file"}
61
+ try:
62
+ with open(file_path, "r", encoding="utf-8") as f:
63
+ content = f.read()
64
+ return {"content": content, "path": file_path, "total_lines": len(content.splitlines())}
65
+ except Exception as exc:
66
+ return {"error": str(exc)}
67
+
68
+ def grep(context: RunContext | None, search_string: str, directory: str = ".") -> List[Dict[str, Any]]:
69
+ matches: List[Dict[str, Any]] = []
70
+ directory = os.path.abspath(directory)
71
+ for root, dirs, files in os.walk(directory):
72
+ for f in files:
73
+ file_path = os.path.join(root, f)
74
+ try:
75
+ with open(file_path, "r", encoding="utf-8") as fh:
76
+ for ln, line in enumerate(fh, 1):
77
+ if search_string in line:
78
+ matches.append({"file_path": file_path, "line_number": ln})
79
+ if len(matches) >= 200:
80
+ return matches
81
+ except Exception:
82
+ continue
83
+ return matches
84
+
8
85
  def register_file_operations_tools(agent):
9
86
  # Constants for file operations
10
87
  IGNORE_PATTERNS = [
@@ -145,26 +222,6 @@ def register_file_operations_tools(agent):
145
222
  console.print("[dim]" + "-" * 60 + "[/dim]\n")
146
223
  return results
147
224
 
148
- @agent.tool
149
- def create_file(context: RunContext, file_path: str, content: str = "") -> Dict[str, Any]:
150
- file_path = os.path.abspath(file_path)
151
- if os.path.exists(file_path):
152
- return {"error": f"File '{file_path}' already exists. Use replace_in_file or write_to_file to edit it."}
153
- directory = os.path.dirname(file_path)
154
- if directory and not os.path.exists(directory):
155
- try:
156
- os.makedirs(directory)
157
- except Exception as e:
158
- return {"error": f"Error creating directory '{directory}': {str(e)}"}
159
- try:
160
- with open(file_path, "w", encoding="utf-8") as f:
161
- console.print("[yellow]Writing to file:[/yellow]")
162
- console.print(content)
163
- f.write(content)
164
- return {"success": True, "path": file_path, "message": f"File created at '{file_path}'", "content_length": len(content)}
165
- except Exception as e:
166
- return {"error": f"Error creating file '{file_path}': {str(e)}"}
167
-
168
225
  @agent.tool
169
226
  def read_file(context: RunContext, file_path: str) -> Dict[str, Any]:
170
227
  file_path = os.path.abspath(file_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: code-puppy
3
- Version: 0.0.45
3
+ Version: 0.0.47
4
4
  Summary: Code generation agent
5
5
  Author: Michael Pfaffenberger
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  code_puppy/__init__.py,sha256=orgffM-uGp8g1XCqXGKWaFB4tCCz8TVgsLMPKCOGNx0,81
2
2
  code_puppy/agent.py,sha256=avoOEorAYUyQYnYVVCezLz1QKDm0Qgx3i_C-BqgDiQQ,3198
3
- code_puppy/agent_prompts.py,sha256=O8vzlEPct6qRNhKSRctJRwq3aDOhT_-vyVozS5DtmVA,6554
3
+ code_puppy/agent_prompts.py,sha256=A6ADydqbHozIAtwOy_UY-fYZ2XDE9_5oV3oxOMtVCFA,6782
4
4
  code_puppy/config.py,sha256=vBC4JFKNNXUSJ8l4yHF-Vp4lPwMlHNRafb1yxjIpxAE,2202
5
5
  code_puppy/main.py,sha256=bc27bk6rFge95H2BumTTzRLtOx43z5FnsmjIjQx_RpU,10052
6
6
  code_puppy/model_factory.py,sha256=Pi46jRr7JaBtJfNpSFk-fK2DlBy0Dv6tG2RcXLf4ZVI,11560
@@ -11,18 +11,18 @@ code_puppy/command_line/__init__.py,sha256=y7WeRemfYppk8KVbCGeAIiTuiOszIURCDjOMZ
11
11
  code_puppy/command_line/file_path_completion.py,sha256=HAlOu9XVYgJ7FbjdrhKBL0rFmCVFxSGGewdcfKTUsPw,2865
12
12
  code_puppy/command_line/meta_command_handler.py,sha256=w9aZoMZVhPQj2Vix3nqW_K9Psou6nU_Jb2dFwr37yKs,3453
13
13
  code_puppy/command_line/model_picker_completion.py,sha256=5VEa6OE9shsF0EafJXzBLpFXhFIkqevJi9RRD-eUvZA,3718
14
- code_puppy/command_line/prompt_toolkit_completion.py,sha256=pOX3gd2-nn42ReM3_pp3g-NxvslZ9tBSd0S_IeyNelk,5156
14
+ code_puppy/command_line/prompt_toolkit_completion.py,sha256=QtXqqEzc6C8ODHTfvIDD1sY0fLaX40S7Sfbol43A9i8,5338
15
15
  code_puppy/command_line/utils.py,sha256=L1PnV9tNupEW1zeziyb5aGAq8DYP8sMiuQbFYLO5Nus,1236
16
16
  code_puppy/tools/__init__.py,sha256=48BVpMt0HAMtz8G_z9SQhX6LnRqR83_AVfMQMf7bY0g,557
17
17
  code_puppy/tools/code_map.py,sha256=BghDHaebhGDfDGvA34gwO_5r92Py4O0Q3J4RV-WtnWs,3155
18
18
  code_puppy/tools/command_runner.py,sha256=XDUdVo-9lIegYcYirdbI3qSuJgijBgLnujoYQq5MSe4,4755
19
19
  code_puppy/tools/common.py,sha256=dbmyZTrTBQh_0WWpaYN6jEync62W2mMrzNS8UFK0co4,146
20
- code_puppy/tools/file_modifications.py,sha256=V8as4Pp_qBJSUFuFl5Y-jpiJKH0tUEQ8dNdkGHzpQM0,11645
21
- code_puppy/tools/file_operations.py,sha256=9M-JntSSTElsLZ1EMLo9Xu2ifje-N0hjXbwARoBm2Tw,9722
20
+ code_puppy/tools/file_modifications.py,sha256=nT87uAoY14RTAapnFCgkLTmW9g9P9bymxts2MpSpoo0,19297
21
+ code_puppy/tools/file_operations.py,sha256=LJU_1b3WCXTAHa2B5VAbckrn1VVWb-HhcI3TF3BxYWs,11625
22
22
  code_puppy/tools/web_search.py,sha256=HhcwX0MMvMDPFO8gr8gzgesD5wPXOypjkxyLZeNwL5g,589
23
- code_puppy-0.0.45.data/data/code_puppy/models.json,sha256=7H-y97YK9BXhag5wJU19rtg24JtZWYx60RsBLBW3WiI,2162
24
- code_puppy-0.0.45.dist-info/METADATA,sha256=4Z0WVe0KJCQULd1GaPPLptRYEmk7MNGkJP5J_SByFKw,4716
25
- code_puppy-0.0.45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- code_puppy-0.0.45.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
27
- code_puppy-0.0.45.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
28
- code_puppy-0.0.45.dist-info/RECORD,,
23
+ code_puppy-0.0.47.data/data/code_puppy/models.json,sha256=7H-y97YK9BXhag5wJU19rtg24JtZWYx60RsBLBW3WiI,2162
24
+ code_puppy-0.0.47.dist-info/METADATA,sha256=X_fhgMeSahe4T6IeiwYvUIhWpaDTXqy14Oubz0I5Lsg,4716
25
+ code_puppy-0.0.47.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ code_puppy-0.0.47.dist-info/entry_points.txt,sha256=d8YkBvIUxF-dHNJAj-x4fPEqizbY5d_TwvYpc01U5kw,58
27
+ code_puppy-0.0.47.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
28
+ code_puppy-0.0.47.dist-info/RECORD,,