pdd-cli 0.0.118__py3-none-any.whl → 0.0.121__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.
Files changed (39) hide show
  1. pdd/__init__.py +1 -1
  2. pdd/agentic_bug_orchestrator.py +15 -6
  3. pdd/agentic_change_orchestrator.py +18 -7
  4. pdd/agentic_common.py +68 -40
  5. pdd/agentic_crash.py +2 -1
  6. pdd/agentic_e2e_fix_orchestrator.py +165 -9
  7. pdd/agentic_update.py +2 -1
  8. pdd/agentic_verify.py +3 -2
  9. pdd/auto_include.py +51 -0
  10. pdd/commands/analysis.py +32 -25
  11. pdd/commands/connect.py +69 -1
  12. pdd/commands/fix.py +31 -13
  13. pdd/commands/generate.py +5 -0
  14. pdd/commands/modify.py +47 -11
  15. pdd/commands/utility.py +12 -7
  16. pdd/core/cli.py +17 -4
  17. pdd/core/dump.py +68 -20
  18. pdd/fix_main.py +4 -2
  19. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  20. pdd/frontend/dist/index.html +1 -1
  21. pdd/llm_invoke.py +82 -12
  22. pdd/operation_log.py +342 -0
  23. pdd/postprocess.py +122 -100
  24. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +11 -2
  25. pdd/prompts/generate_test_LLM.prompt +0 -1
  26. pdd/prompts/generate_test_from_example_LLM.prompt +251 -0
  27. pdd/prompts/prompt_code_diff_LLM.prompt +29 -25
  28. pdd/server/routes/prompts.py +26 -1
  29. pdd/server/terminal_spawner.py +15 -7
  30. pdd/sync_orchestration.py +164 -147
  31. pdd/sync_order.py +304 -0
  32. pdd/update_main.py +48 -24
  33. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +3 -3
  34. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/RECORD +37 -35
  35. pdd/frontend/dist/assets/index-DQ3wkeQ2.js +0 -449
  36. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  37. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  38. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  39. {pdd_cli-0.0.118.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/core/dump.py CHANGED
@@ -16,6 +16,43 @@ import requests
16
16
  from .. import __version__
17
17
  from .errors import console, get_core_dump_errors
18
18
 
19
+
20
+ def garbage_collect_core_dumps(keep: int = 10) -> int:
21
+ """Delete old core dumps, keeping only the most recent `keep` files.
22
+
23
+ Core dumps are sorted by modification time (mtime), and the oldest
24
+ files beyond the `keep` limit are deleted.
25
+
26
+ Args:
27
+ keep: Number of core dump files to keep. Default is 10.
28
+
29
+ Returns:
30
+ The number of deleted files.
31
+ """
32
+ core_dump_dir = Path.cwd() / ".pdd" / "core_dumps"
33
+ if not core_dump_dir.exists():
34
+ return 0
35
+
36
+ # Find all core dump files and sort by mtime (newest first)
37
+ dumps = sorted(
38
+ core_dump_dir.glob("pdd-core-*.json"),
39
+ key=lambda p: p.stat().st_mtime,
40
+ reverse=True
41
+ )
42
+
43
+ # Delete files beyond the keep limit
44
+ deleted = 0
45
+ for dump_file in dumps[keep:]:
46
+ try:
47
+ dump_file.unlink()
48
+ deleted += 1
49
+ except OSError:
50
+ # If we can't delete a file, just skip it
51
+ pass
52
+
53
+ return deleted
54
+
55
+
19
56
  def _write_core_dump(
20
57
  ctx: click.Context,
21
58
  normalized_results: List[Any],
@@ -70,8 +107,8 @@ def _write_core_dump(
70
107
  file_contents = {}
71
108
  core_dump_files = ctx.obj.get("core_dump_files", set())
72
109
 
73
- if not ctx.obj.get("quiet"):
74
- console.print(f"[info]Core dump: Found {len(core_dump_files)} tracked files[/info]")
110
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
111
+ console.print(f"[info]Debug snapshot: Found {len(core_dump_files)} tracked files[/info]")
75
112
 
76
113
  # Auto-include relevant meta files for the invoked commands
77
114
  meta_dir = Path.cwd() / ".pdd" / "meta"
@@ -100,8 +137,8 @@ def _write_core_dump(
100
137
  for file_path in core_dump_files:
101
138
  try:
102
139
  path = Path(file_path)
103
- if not ctx.obj.get("quiet"):
104
- console.print(f"[info]Core dump: Checking file {file_path}[/info]")
140
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
141
+ console.print(f"[info]Debug snapshot: Checking file {file_path}[/info]")
105
142
 
106
143
  if path.exists() and path.is_file():
107
144
  if path.stat().st_size < 50000: # 50KB limit
@@ -113,23 +150,23 @@ def _write_core_dump(
113
150
  key = str(path)
114
151
 
115
152
  file_contents[key] = path.read_text(encoding='utf-8')
116
- if not ctx.obj.get("quiet"):
117
- console.print(f"[info]Core dump: Added content for {key}[/info]")
153
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
154
+ console.print(f"[info]Debug snapshot: Added content for {key}[/info]")
118
155
  except UnicodeDecodeError:
119
156
  file_contents[str(path)] = "<binary>"
120
- if not ctx.obj.get("quiet"):
121
- console.print(f"[warning]Core dump: Binary file {path}[/warning]")
157
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
158
+ console.print(f"[warning]Debug snapshot: Binary file {path}[/warning]")
122
159
  else:
123
160
  file_contents[str(path)] = "<too large>"
124
- if not ctx.obj.get("quiet"):
125
- console.print(f"[warning]Core dump: File too large {path}[/warning]")
161
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
162
+ console.print(f"[warning]Debug snapshot: File too large {path}[/warning]")
126
163
  else:
127
- if not ctx.obj.get("quiet"):
128
- console.print(f"[warning]Core dump: File not found or not a file: {file_path}[/warning]")
164
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
165
+ console.print(f"[warning]Debug snapshot: File not found or not a file: {file_path}[/warning]")
129
166
  except Exception as e:
130
167
  file_contents[str(file_path)] = f"<error reading file: {e}>"
131
- if not ctx.obj.get("quiet"):
132
- console.print(f"[warning]Core dump: Error reading {file_path}: {e}[/warning]")
168
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
169
+ console.print(f"[warning]Debug snapshot: Error reading {file_path}: {e}[/warning]")
133
170
 
134
171
  payload: Dict[str, Any] = {
135
172
  "schema_version": 1,
@@ -166,15 +203,26 @@ def _write_core_dump(
166
203
 
167
204
  dump_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
168
205
 
206
+ # Garbage collect old core dumps after writing (Issue #231)
207
+ # This ensures we keep at most N dumps, not N+1
208
+ keep_core_dumps = ctx.obj.get("keep_core_dumps", 10)
209
+ garbage_collect_core_dumps(keep=keep_core_dumps)
210
+
169
211
  if not ctx.obj.get("quiet"):
170
- console.print(
171
- f"[info]Core dump written to [path]{dump_path}[/path]. "
172
- "You can attach this file when reporting a bug.[/info]"
173
- )
212
+ # Check if the dump still exists after GC (may be deleted if keep=0)
213
+ if dump_path.exists():
214
+ console.print(
215
+ f"[info]📦 Debug snapshot saved to [path]{dump_path}[/path] "
216
+ "(attach when reporting bugs)[/info]"
217
+ )
218
+ else:
219
+ console.print(
220
+ "[info]📦 Debug snapshot saved and immediately cleaned up (--keep-core-dumps=0)[/info]"
221
+ )
174
222
  except Exception as exc:
175
- # Never let core dumping itself crash the CLI
223
+ # Never let debug snapshot creation crash the CLI
176
224
  if not ctx.obj.get("quiet"):
177
- console.print(f"[warning]Failed to write core dump: {exc}[/warning]", style="warning")
225
+ console.print(f"[warning]Failed to write debug snapshot: {exc}[/warning]", style="warning")
178
226
 
179
227
 
180
228
  def _get_github_token() -> Optional[str]:
pdd/fix_main.py CHANGED
@@ -417,8 +417,10 @@ def fix_main(
417
417
  rprint(f"[bold red]Error printing analysis preview: {e}[/bold red]")
418
418
  if success:
419
419
  rprint("[bold green]Fixed files saved:[/bold green]")
420
- rprint(f" Test file: {output_file_paths['output_test']}")
421
- rprint(f" Code file: {output_file_paths['output_code']}")
420
+ if fixed_unit_test:
421
+ rprint(f" Test file: {output_file_paths['output_test']}")
422
+ if fixed_code:
423
+ rprint(f" Code file: {output_file_paths['output_code']}")
422
424
  if output_file_paths.get("output_results"):
423
425
  rprint(f" Results file: {output_file_paths['output_results']}")
424
426