pdd-cli 0.0.45__py3-none-any.whl → 0.0.90__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 (114) hide show
  1. pdd/__init__.py +4 -4
  2. pdd/agentic_common.py +863 -0
  3. pdd/agentic_crash.py +534 -0
  4. pdd/agentic_fix.py +1179 -0
  5. pdd/agentic_langtest.py +162 -0
  6. pdd/agentic_update.py +370 -0
  7. pdd/agentic_verify.py +183 -0
  8. pdd/auto_deps_main.py +15 -5
  9. pdd/auto_include.py +63 -5
  10. pdd/bug_main.py +3 -2
  11. pdd/bug_to_unit_test.py +2 -0
  12. pdd/change_main.py +11 -4
  13. pdd/cli.py +22 -1181
  14. pdd/cmd_test_main.py +73 -21
  15. pdd/code_generator.py +58 -18
  16. pdd/code_generator_main.py +672 -25
  17. pdd/commands/__init__.py +42 -0
  18. pdd/commands/analysis.py +248 -0
  19. pdd/commands/fix.py +140 -0
  20. pdd/commands/generate.py +257 -0
  21. pdd/commands/maintenance.py +174 -0
  22. pdd/commands/misc.py +79 -0
  23. pdd/commands/modify.py +230 -0
  24. pdd/commands/report.py +144 -0
  25. pdd/commands/templates.py +215 -0
  26. pdd/commands/utility.py +110 -0
  27. pdd/config_resolution.py +58 -0
  28. pdd/conflicts_main.py +8 -3
  29. pdd/construct_paths.py +258 -82
  30. pdd/context_generator.py +10 -2
  31. pdd/context_generator_main.py +113 -11
  32. pdd/continue_generation.py +47 -7
  33. pdd/core/__init__.py +0 -0
  34. pdd/core/cli.py +503 -0
  35. pdd/core/dump.py +554 -0
  36. pdd/core/errors.py +63 -0
  37. pdd/core/utils.py +90 -0
  38. pdd/crash_main.py +44 -11
  39. pdd/data/language_format.csv +71 -63
  40. pdd/data/llm_model.csv +20 -18
  41. pdd/detect_change_main.py +5 -4
  42. pdd/fix_code_loop.py +330 -76
  43. pdd/fix_error_loop.py +207 -61
  44. pdd/fix_errors_from_unit_tests.py +4 -3
  45. pdd/fix_main.py +75 -18
  46. pdd/fix_verification_errors.py +12 -100
  47. pdd/fix_verification_errors_loop.py +306 -272
  48. pdd/fix_verification_main.py +28 -9
  49. pdd/generate_output_paths.py +93 -10
  50. pdd/generate_test.py +16 -5
  51. pdd/get_jwt_token.py +9 -2
  52. pdd/get_run_command.py +73 -0
  53. pdd/get_test_command.py +68 -0
  54. pdd/git_update.py +70 -19
  55. pdd/incremental_code_generator.py +2 -2
  56. pdd/insert_includes.py +11 -3
  57. pdd/llm_invoke.py +1269 -103
  58. pdd/load_prompt_template.py +36 -10
  59. pdd/pdd_completion.fish +25 -2
  60. pdd/pdd_completion.sh +30 -4
  61. pdd/pdd_completion.zsh +79 -4
  62. pdd/postprocess.py +10 -3
  63. pdd/preprocess.py +228 -15
  64. pdd/preprocess_main.py +8 -5
  65. pdd/prompts/agentic_crash_explore_LLM.prompt +49 -0
  66. pdd/prompts/agentic_fix_explore_LLM.prompt +45 -0
  67. pdd/prompts/agentic_fix_harvest_only_LLM.prompt +48 -0
  68. pdd/prompts/agentic_fix_primary_LLM.prompt +85 -0
  69. pdd/prompts/agentic_update_LLM.prompt +1071 -0
  70. pdd/prompts/agentic_verify_explore_LLM.prompt +45 -0
  71. pdd/prompts/auto_include_LLM.prompt +100 -905
  72. pdd/prompts/detect_change_LLM.prompt +122 -20
  73. pdd/prompts/example_generator_LLM.prompt +22 -1
  74. pdd/prompts/extract_code_LLM.prompt +5 -1
  75. pdd/prompts/extract_program_code_fix_LLM.prompt +7 -1
  76. pdd/prompts/extract_prompt_update_LLM.prompt +7 -8
  77. pdd/prompts/extract_promptline_LLM.prompt +17 -11
  78. pdd/prompts/find_verification_errors_LLM.prompt +6 -0
  79. pdd/prompts/fix_code_module_errors_LLM.prompt +4 -2
  80. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +8 -0
  81. pdd/prompts/fix_verification_errors_LLM.prompt +22 -0
  82. pdd/prompts/generate_test_LLM.prompt +21 -6
  83. pdd/prompts/increase_tests_LLM.prompt +1 -5
  84. pdd/prompts/insert_includes_LLM.prompt +228 -108
  85. pdd/prompts/trace_LLM.prompt +25 -22
  86. pdd/prompts/unfinished_prompt_LLM.prompt +85 -1
  87. pdd/prompts/update_prompt_LLM.prompt +22 -1
  88. pdd/pytest_output.py +127 -12
  89. pdd/render_mermaid.py +236 -0
  90. pdd/setup_tool.py +648 -0
  91. pdd/simple_math.py +2 -0
  92. pdd/split_main.py +3 -2
  93. pdd/summarize_directory.py +49 -6
  94. pdd/sync_determine_operation.py +543 -98
  95. pdd/sync_main.py +81 -31
  96. pdd/sync_orchestration.py +1334 -751
  97. pdd/sync_tui.py +848 -0
  98. pdd/template_registry.py +264 -0
  99. pdd/templates/architecture/architecture_json.prompt +242 -0
  100. pdd/templates/generic/generate_prompt.prompt +174 -0
  101. pdd/trace.py +168 -12
  102. pdd/trace_main.py +4 -3
  103. pdd/track_cost.py +151 -61
  104. pdd/unfinished_prompt.py +49 -3
  105. pdd/update_main.py +549 -67
  106. pdd/update_model_costs.py +2 -2
  107. pdd/update_prompt.py +19 -4
  108. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.90.dist-info}/METADATA +19 -6
  109. pdd_cli-0.0.90.dist-info/RECORD +153 -0
  110. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.90.dist-info}/licenses/LICENSE +1 -1
  111. pdd_cli-0.0.45.dist-info/RECORD +0 -116
  112. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.90.dist-info}/WHEEL +0 -0
  113. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.90.dist-info}/entry_points.txt +0 -0
  114. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.90.dist-info}/top_level.txt +0 -0
pdd/sync_main.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  import time
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List, Tuple
4
+ from typing import Any, Dict, List, Optional, Tuple
5
5
 
6
6
  import click
7
7
  from rich.console import Console
@@ -79,12 +79,12 @@ def _detect_languages(basename: str, prompts_dir: Path) -> List[str]:
79
79
  def sync_main(
80
80
  ctx: click.Context,
81
81
  basename: str,
82
- max_attempts: int,
83
- budget: float,
82
+ max_attempts: Optional[int],
83
+ budget: Optional[float],
84
84
  skip_verify: bool,
85
85
  skip_tests: bool,
86
86
  target_coverage: float,
87
- log: bool,
87
+ dry_run: bool,
88
88
  ) -> Tuple[Dict[str, Any], float, str]:
89
89
  """
90
90
  CLI wrapper for the sync command. Handles parameter validation, path construction,
@@ -93,12 +93,12 @@ def sync_main(
93
93
  Args:
94
94
  ctx: The Click context object.
95
95
  basename: The base name for the prompt file.
96
- max_attempts: Maximum number of fix attempts.
97
- budget: Maximum total cost for the sync process.
96
+ max_attempts: Maximum number of fix attempts. If None, uses .pddrc value or default (3).
97
+ budget: Maximum total cost for the sync process. If None, uses .pddrc value or default (20.0).
98
98
  skip_verify: Skip the functional verification step.
99
99
  skip_tests: Skip unit test generation and fixing.
100
100
  target_coverage: Desired code coverage percentage.
101
- log: If True, display sync logs instead of running the sync.
101
+ dry_run: If True, analyze sync state without executing operations.
102
102
 
103
103
  Returns:
104
104
  A tuple containing the results dictionary, total cost, and primary model name.
@@ -118,15 +118,19 @@ def sync_main(
118
118
  local = ctx.obj.get("local", False)
119
119
  context_override = ctx.obj.get("context", None)
120
120
 
121
- # 2. Validate inputs
121
+ # Default values for max_attempts and budget when not specified via CLI or .pddrc
122
+ DEFAULT_MAX_ATTEMPTS = 3
123
+ DEFAULT_BUDGET = 20.0
124
+
125
+ # 2. Validate inputs (basename only - budget/max_attempts validated after config resolution)
122
126
  _validate_basename(basename)
123
- if budget <= 0:
124
- raise click.BadParameter("Budget must be a positive number.", param_hint="--budget")
125
- if max_attempts <= 0:
126
- raise click.BadParameter("Max attempts must be a positive integer.", param_hint="--max-attempts")
127
127
 
128
- if not quiet and budget < 1.0:
129
- console.log(f"[yellow]Warning:[/] Budget of ${budget:.2f} is low. Complex operations may exceed this limit.")
128
+ # Validate CLI-specified values if provided (not None)
129
+ # Note: max_attempts=0 is valid (skips LLM loop, goes straight to agentic mode)
130
+ if budget is not None and budget <= 0:
131
+ raise click.BadParameter("Budget must be a positive number.", param_hint="--budget")
132
+ if max_attempts is not None and max_attempts < 0:
133
+ raise click.BadParameter("Max attempts must be a non-negative integer.", param_hint="--max-attempts")
130
134
 
131
135
  # 3. Use construct_paths in 'discovery' mode to find the prompts directory.
132
136
  try:
@@ -151,10 +155,10 @@ def sync_main(
151
155
  f"Expected files with format: '{basename}_<language>.prompt'"
152
156
  )
153
157
 
154
- # 5. Handle --log mode separately
155
- if log:
158
+ # 5. Handle --dry-run mode separately
159
+ if dry_run:
156
160
  if not quiet:
157
- rprint(Panel(f"Displaying sync logs for [bold cyan]{basename}[/bold cyan]", title="PDD Sync Log", expand=False))
161
+ rprint(Panel(f"Displaying sync analysis for [bold cyan]{basename}[/bold cyan]", title="PDD Sync Dry Run", expand=False))
158
162
 
159
163
  for lang in languages:
160
164
  if not quiet:
@@ -189,19 +193,27 @@ def sync_main(
189
193
  code_dir=str(code_dir),
190
194
  examples_dir=str(examples_dir),
191
195
  tests_dir=str(tests_dir),
192
- log=True,
196
+ dry_run=True,
193
197
  verbose=verbose,
194
198
  quiet=quiet,
199
+ context_override=context_override,
195
200
  )
196
201
  return {}, 0.0, ""
197
202
 
198
203
  # 6. Main Sync Workflow
204
+ # Determine display values for summary panel (use CLI values or defaults for display)
205
+ display_budget = budget if budget is not None else DEFAULT_BUDGET
206
+ display_max_attempts = max_attempts if max_attempts is not None else DEFAULT_MAX_ATTEMPTS
207
+
208
+ if not quiet and display_budget < 1.0:
209
+ console.log(f"[yellow]Warning:[/] Budget of ${display_budget:.2f} is low. Complex operations may exceed this limit.")
210
+
199
211
  if not quiet:
200
212
  summary_panel = Panel(
201
213
  f"Basename: [bold cyan]{basename}[/bold cyan]\n"
202
214
  f"Languages: [bold green]{', '.join(languages)}[/bold green]\n"
203
- f"Budget: [bold yellow]${budget:.2f}[/bold yellow]\n"
204
- f"Max Attempts: [bold blue]{max_attempts}[/bold blue]",
215
+ f"Budget: [bold yellow]${display_budget:.2f}[/bold yellow]\n"
216
+ f"Max Attempts: [bold blue]{display_max_attempts}[/bold blue]",
205
217
  title="PDD Sync Starting",
206
218
  expand=False,
207
219
  )
@@ -211,13 +223,15 @@ def sync_main(
211
223
  total_cost = 0.0
212
224
  primary_model = ""
213
225
  overall_success = True
214
- remaining_budget = budget
226
+ # remaining_budget will be set from resolved config on first language iteration
227
+ remaining_budget: Optional[float] = None
215
228
 
216
229
  for lang in languages:
217
230
  if not quiet:
218
231
  rprint(f"\n[bold]🚀 Syncing for language: [green]{lang}[/green]...[/bold]")
219
232
 
220
- if remaining_budget <= 0:
233
+ # Check budget exhaustion (after first iteration when remaining_budget is set)
234
+ if remaining_budget is not None and remaining_budget <= 0:
221
235
  if not quiet:
222
236
  rprint(f"[yellow]Budget exhausted. Skipping sync for '{lang}'.[/yellow]")
223
237
  overall_success = False
@@ -231,17 +245,25 @@ def sync_main(
231
245
  command_options = {
232
246
  "basename": basename,
233
247
  "language": lang,
234
- "max_attempts": max_attempts,
235
- "budget": budget,
236
248
  "target_coverage": target_coverage,
237
- "strength": strength,
238
- "temperature": temperature,
239
249
  "time": time_param,
240
250
  }
241
-
251
+ # Only pass values if explicitly set by user (not CLI defaults)
252
+ # This allows .pddrc values to take precedence when user doesn't pass CLI flags
253
+ if max_attempts is not None:
254
+ command_options["max_attempts"] = max_attempts
255
+ if budget is not None:
256
+ command_options["budget"] = budget
257
+ if strength != DEFAULT_STRENGTH:
258
+ command_options["strength"] = strength
259
+ if temperature != 0.0: # 0.0 is the CLI default for temperature
260
+ command_options["temperature"] = temperature
261
+
262
+ # Use force=True for path discovery - actual file writes happen in sync_orchestration
263
+ # which will handle confirmations via the TUI's confirm_callback
242
264
  resolved_config, _, _, resolved_language = construct_paths(
243
265
  input_file_paths={"prompt_file": str(prompt_file_path)},
244
- force=force,
266
+ force=True, # Always force during path discovery
245
267
  quiet=True,
246
268
  command="sync",
247
269
  command_options=command_options,
@@ -249,11 +271,38 @@ def sync_main(
249
271
  )
250
272
 
251
273
  # Extract all parameters directly from the resolved configuration
274
+ # Priority: CLI value > .pddrc value > hardcoded default
252
275
  final_strength = resolved_config.get("strength", strength)
253
276
  final_temp = resolved_config.get("temperature", temperature)
254
- final_max_attempts = resolved_config.get("max_attempts", max_attempts)
255
277
  final_target_coverage = resolved_config.get("target_coverage", target_coverage)
256
-
278
+
279
+ # For max_attempts and budget: CLI > .pddrc > hardcoded default
280
+ # If CLI value is provided (not None), use it. Otherwise, use .pddrc or default.
281
+ if max_attempts is not None:
282
+ final_max_attempts = max_attempts
283
+ else:
284
+ final_max_attempts = resolved_config.get("max_attempts") or DEFAULT_MAX_ATTEMPTS
285
+
286
+ if budget is not None:
287
+ final_budget = budget
288
+ else:
289
+ final_budget = resolved_config.get("budget") or DEFAULT_BUDGET
290
+
291
+ # Validate the resolved values
292
+ # Note: max_attempts=0 is valid (skips LLM loop, goes straight to agentic mode)
293
+ if final_budget <= 0:
294
+ raise click.BadParameter("Budget must be a positive number.", param_hint="--budget")
295
+ if final_max_attempts < 0:
296
+ raise click.BadParameter("Max attempts must be a non-negative integer.", param_hint="--max-attempts")
297
+
298
+ # Initialize remaining_budget from first resolved config if not set yet
299
+ if remaining_budget is None:
300
+ remaining_budget = final_budget
301
+
302
+ # Update ctx.obj with resolved values so sub-commands inherit them
303
+ ctx.obj["strength"] = final_strength
304
+ ctx.obj["temperature"] = final_temp
305
+
257
306
  code_dir = resolved_config.get("code_dir", "src")
258
307
  tests_dir = resolved_config.get("tests_dir", "tests")
259
308
  examples_dir = resolved_config.get("examples_dir", "examples")
@@ -280,6 +329,7 @@ def sync_main(
280
329
  review_examples=review_examples,
281
330
  local=local,
282
331
  context_config=resolved_config,
332
+ context_override=context_override,
283
333
  )
284
334
 
285
335
  lang_cost = sync_result.get("total_cost", 0.0)
@@ -330,4 +380,4 @@ def sync_main(
330
380
  aggregated_results["total_cost"] = total_cost
331
381
  aggregated_results["primary_model"] = primary_model
332
382
 
333
- return aggregated_results, total_cost, primary_model
383
+ return aggregated_results, total_cost, primary_model