pdd-cli 0.0.54__py3-none-any.whl → 0.0.56__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 pdd-cli might be problematic. Click here for more details.

pdd/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """PDD - Prompt Driven Development"""
2
2
 
3
- __version__ = "0.0.54"
3
+ __version__ = "0.0.56"
4
4
 
5
5
  # Strength parameter used for LLM extraction across the codebase
6
6
  # Used in postprocessing, XML tagging, code generation, and other extraction
pdd/auto_deps_main.py CHANGED
@@ -49,7 +49,8 @@ def auto_deps_main( # pylint: disable=too-many-arguments, too-many-locals
49
49
  force=ctx.obj.get('force', False),
50
50
  quiet=ctx.obj.get('quiet', False),
51
51
  command="auto-deps",
52
- command_options=command_options
52
+ command_options=command_options,
53
+ context_override=ctx.obj.get('context')
53
54
  )
54
55
 
55
56
  # Get the CSV file path
@@ -101,4 +102,4 @@ def auto_deps_main( # pylint: disable=too-many-arguments, too-many-locals
101
102
  except Exception as exc:
102
103
  if not ctx.obj.get('quiet', False):
103
104
  rprint(f"[bold red]Error:[/bold red] {str(exc)}")
104
- sys.exit(1)
105
+ sys.exit(1)
pdd/bug_main.py CHANGED
@@ -50,7 +50,8 @@ def bug_main(
50
50
  force=ctx.obj.get('force', False),
51
51
  quiet=ctx.obj.get('quiet', False),
52
52
  command="bug",
53
- command_options=command_options
53
+ command_options=command_options,
54
+ context_override=ctx.obj.get('context')
54
55
  )
55
56
 
56
57
  # Use the language detected by construct_paths if none was explicitly provided
@@ -117,4 +118,4 @@ def bug_main(
117
118
  except Exception as e:
118
119
  if not ctx.obj.get('quiet', False):
119
120
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
120
- sys.exit(1)
121
+ sys.exit(1)
pdd/change_main.py CHANGED
@@ -203,6 +203,7 @@ def change_main(
203
203
  quiet=quiet,
204
204
  command="change",
205
205
  command_options=command_options,
206
+ context_override=ctx.obj.get('context')
206
207
  )
207
208
  logger.debug("construct_paths returned:")
208
209
  logger.debug(" input_strings keys: %s", list(input_strings.keys()))
pdd/cli.py CHANGED
@@ -26,7 +26,7 @@ from .cmd_test_main import cmd_test_main
26
26
  from .code_generator_main import code_generator_main
27
27
  from .conflicts_main import conflicts_main
28
28
  # Need to import construct_paths for tests patching pdd.cli.construct_paths
29
- from .construct_paths import construct_paths
29
+ from .construct_paths import construct_paths, list_available_contexts
30
30
  from .context_generator_main import context_generator_main
31
31
  from .crash_main import crash_main
32
32
  from .detect_change_main import detect_change_main
@@ -76,7 +76,7 @@ def handle_error(exception: Exception, command_name: str, quiet: bool):
76
76
 
77
77
 
78
78
  # --- Main CLI Group ---
79
- @click.group(chain=True, help="PDD (Prompt-Driven Development) Command Line Interface.")
79
+ @click.group(chain=True, invoke_without_command=True, help="PDD (Prompt-Driven Development) Command Line Interface.")
80
80
  @click.option(
81
81
  "--force",
82
82
  is_flag=True,
@@ -134,6 +134,20 @@ def handle_error(exception: Exception, command_name: str, quiet: bool):
134
134
  default=False,
135
135
  help="Run commands locally instead of in the cloud.",
136
136
  )
137
+ @click.option(
138
+ "--context",
139
+ "context_override",
140
+ type=str,
141
+ default=None,
142
+ help="Override automatic context detection and use the specified .pddrc context.",
143
+ )
144
+ @click.option(
145
+ "--list-contexts",
146
+ "list_contexts",
147
+ is_flag=True,
148
+ default=False,
149
+ help="List available contexts from .pddrc and exit.",
150
+ )
137
151
  @click.version_option(version=__version__, package_name="pdd-cli")
138
152
  @click.pass_context
139
153
  def cli(
@@ -147,6 +161,8 @@ def cli(
147
161
  review_examples: bool,
148
162
  local: bool,
149
163
  time: Optional[float], # Type hint is Optional[float]
164
+ context_override: Optional[str],
165
+ list_contexts: bool,
150
166
  ):
151
167
  """
152
168
  Main entry point for the PDD CLI. Handles global options and initializes context.
@@ -166,11 +182,37 @@ def cli(
166
182
  ctx.obj["local"] = local
167
183
  # Use DEFAULT_TIME if time is not provided
168
184
  ctx.obj["time"] = time if time is not None else DEFAULT_TIME
185
+ # Persist context override for downstream calls
186
+ ctx.obj["context"] = context_override
169
187
 
170
188
  # Suppress verbose if quiet is enabled
171
189
  if quiet:
172
190
  ctx.obj["verbose"] = False
173
191
 
192
+ # If --list-contexts is provided, print and exit before any other actions
193
+ if list_contexts:
194
+ try:
195
+ names = list_available_contexts()
196
+ except Exception as exc:
197
+ # Surface config errors as usage errors
198
+ raise click.UsageError(f"Failed to load .pddrc: {exc}")
199
+ # Print one per line; avoid Rich formatting for portability
200
+ for name in names:
201
+ click.echo(name)
202
+ ctx.exit(0)
203
+
204
+ # Optional early validation for --context
205
+ if context_override:
206
+ try:
207
+ names = list_available_contexts()
208
+ except Exception as exc:
209
+ # If .pddrc is malformed, propagate as usage error
210
+ raise click.UsageError(f"Failed to load .pddrc: {exc}")
211
+ if context_override not in names:
212
+ raise click.UsageError(
213
+ f"Unknown context '{context_override}'. Available contexts: {', '.join(names)}"
214
+ )
215
+
174
216
  # Perform auto-update check unless disabled
175
217
  if os.getenv("PDD_AUTO_UPDATE", "true").lower() != "false":
176
218
  try:
@@ -281,11 +323,18 @@ def process_commands(ctx: click.Context, results: List[Optional[Tuple[Any, float
281
323
  help="Path to the original prompt file for incremental generation.",
282
324
  )
283
325
  @click.option(
284
- "--force-incremental",
285
- "force_incremental_flag",
326
+ "--incremental",
327
+ "incremental_flag",
286
328
  is_flag=True,
287
329
  default=False,
288
- help="Force incremental generation even if full regeneration is suggested.",
330
+ help="Force incremental patching even if changes are significant (requires existing output).",
331
+ )
332
+ @click.option(
333
+ "-e",
334
+ "--env",
335
+ "env_kv",
336
+ multiple=True,
337
+ help="Set template variable (KEY=VALUE) or read KEY from env",
289
338
  )
290
339
  @click.pass_context
291
340
  @track_cost
@@ -294,16 +343,36 @@ def generate(
294
343
  prompt_file: str,
295
344
  output: Optional[str],
296
345
  original_prompt_file_path: Optional[str],
297
- force_incremental_flag: bool,
346
+ incremental_flag: bool,
347
+ env_kv: Tuple[str, ...],
298
348
  ) -> Optional[Tuple[str, float, str]]:
299
349
  """Generate code from a prompt file."""
300
350
  try:
351
+ # Parse -e/--env arguments into a dict
352
+ env_vars: Dict[str, str] = {}
353
+ import os as _os
354
+ for item in env_kv or ():
355
+ if "=" in item:
356
+ key, value = item.split("=", 1)
357
+ key = key.strip()
358
+ if key:
359
+ env_vars[key] = value
360
+ else:
361
+ key = item.strip()
362
+ if key:
363
+ val = _os.environ.get(key)
364
+ if val is not None:
365
+ env_vars[key] = val
366
+ else:
367
+ if ctx.obj.get("verbose") and not ctx.obj.get("quiet"):
368
+ console.print(f"[warning]-e {key} not found in environment; skipping[/warning]")
301
369
  generated_code, incremental, total_cost, model_name = code_generator_main(
302
370
  ctx=ctx,
303
371
  prompt_file=prompt_file,
304
372
  output=output,
305
373
  original_prompt_file_path=original_prompt_file_path,
306
- force_incremental_flag=force_incremental_flag,
374
+ force_incremental_flag=incremental_flag,
375
+ env_vars=env_vars or None,
307
376
  )
308
377
  return generated_code, total_cost, model_name
309
378
  except Exception as exception:
@@ -1022,7 +1091,8 @@ def auto_deps(
1022
1091
  quiet = ctx.obj.get("quiet", False)
1023
1092
  command_name = "auto-deps"
1024
1093
  try:
1025
- clean_directory_path = directory_path.strip('\"')
1094
+ # Strip both single and double quotes from the provided path
1095
+ clean_directory_path = directory_path.strip("'\"")
1026
1096
 
1027
1097
  modified_prompt, total_cost, model_name = auto_deps_main(
1028
1098
  ctx=ctx,
pdd/cmd_test_main.py CHANGED
@@ -88,6 +88,7 @@ def cmd_test_main(
88
88
  quiet=ctx.obj["quiet"],
89
89
  command="test",
90
90
  command_options=command_options,
91
+ context_override=ctx.obj.get('context')
91
92
  )
92
93
  except Exception as exception:
93
94
  # Catching a general exception is necessary here to handle a wide range of
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import re
2
3
  import asyncio
3
4
  import json
4
5
  import pathlib
@@ -59,6 +60,25 @@ def is_git_repository(path: Optional[str] = None) -> bool:
59
60
  return False
60
61
 
61
62
 
63
+ def _expand_vars(text: str, vars_map: Optional[Dict[str, str]]) -> str:
64
+ """Replace $KEY and ${KEY} in text when KEY exists in vars_map. Leave others unchanged."""
65
+ if not text or not vars_map:
66
+ return text
67
+
68
+ def repl_braced(m: re.Match) -> str:
69
+ key = m.group(1)
70
+ return vars_map.get(key, m.group(0))
71
+
72
+ def repl_simple(m: re.Match) -> str:
73
+ key = m.group(1)
74
+ return vars_map.get(key, m.group(0))
75
+
76
+ # Replace ${KEY} first, then $KEY
77
+ text = re.sub(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}", repl_braced, text)
78
+ text = re.sub(r"\$([A-Za-z_][A-Za-z0-9_]*)", repl_simple, text)
79
+ return text
80
+
81
+
62
82
  def get_git_content_at_ref(file_path: str, git_ref: str = "HEAD") -> Optional[str]:
63
83
  """Gets the content of the file as it was at the specified git_ref."""
64
84
  abs_file_path = pathlib.Path(file_path).resolve()
@@ -131,6 +151,7 @@ def code_generator_main(
131
151
  output: Optional[str],
132
152
  original_prompt_file_path: Optional[str],
133
153
  force_incremental_flag: bool,
154
+ env_vars: Optional[Dict[str, str]] = None,
134
155
  ) -> Tuple[str, bool, float, str]:
135
156
  """
136
157
  CLI wrapper for generating code from prompts. Handles full and incremental generation,
@@ -163,6 +184,7 @@ def code_generator_main(
163
184
  quiet=quiet,
164
185
  command="generate",
165
186
  command_options=command_options,
187
+ context_override=ctx.obj.get('context')
166
188
  )
167
189
  prompt_content = input_strings["prompt_file"]
168
190
  # Determine final output path: if user passed a directory, use resolved file path
@@ -190,6 +212,10 @@ def code_generator_main(
190
212
  existing_code_content: Optional[str] = None
191
213
  original_prompt_content_for_incremental: Optional[str] = None
192
214
 
215
+ # Expand variables in output path if provided
216
+ if output_path:
217
+ output_path = _expand_vars(output_path, env_vars)
218
+
193
219
  if output_path and pathlib.Path(output_path).exists():
194
220
  try:
195
221
  existing_code_content = pathlib.Path(output_path).read_text(encoding="utf-8")
@@ -337,9 +363,18 @@ def code_generator_main(
337
363
  if files_to_stage_for_rollback:
338
364
  git_add_files(files_to_stage_for_rollback, verbose=verbose)
339
365
 
366
+ # Preprocess both prompts: expand includes, substitute vars, then double
367
+ orig_proc = pdd_preprocess(original_prompt_content_for_incremental, recursive=True, double_curly_brackets=False)
368
+ orig_proc = _expand_vars(orig_proc, env_vars)
369
+ orig_proc = pdd_preprocess(orig_proc, recursive=False, double_curly_brackets=True)
370
+
371
+ new_proc = pdd_preprocess(prompt_content, recursive=True, double_curly_brackets=False)
372
+ new_proc = _expand_vars(new_proc, env_vars)
373
+ new_proc = pdd_preprocess(new_proc, recursive=False, double_curly_brackets=True)
374
+
340
375
  generated_code_content, was_incremental_operation, total_cost, model_name = incremental_code_generator_func(
341
- original_prompt=original_prompt_content_for_incremental,
342
- new_prompt=prompt_content,
376
+ original_prompt=orig_proc,
377
+ new_prompt=new_proc,
343
378
  existing_code=existing_code_content,
344
379
  language=language,
345
380
  strength=strength,
@@ -347,7 +382,7 @@ def code_generator_main(
347
382
  time=time_budget,
348
383
  force_incremental=force_incremental_flag,
349
384
  verbose=verbose,
350
- preprocess_prompt=True
385
+ preprocess_prompt=False
351
386
  )
352
387
 
353
388
  if not was_incremental_operation:
@@ -364,8 +399,10 @@ def code_generator_main(
364
399
 
365
400
  if not current_execution_is_local:
366
401
  if verbose: console.print("Attempting cloud code generation...")
367
-
368
- processed_prompt_for_cloud = pdd_preprocess(prompt_content, recursive=True, double_curly_brackets=True, exclude_keys=[])
402
+ # Expand includes, substitute vars, then double
403
+ processed_prompt_for_cloud = pdd_preprocess(prompt_content, recursive=True, double_curly_brackets=False, exclude_keys=[])
404
+ processed_prompt_for_cloud = _expand_vars(processed_prompt_for_cloud, env_vars)
405
+ processed_prompt_for_cloud = pdd_preprocess(processed_prompt_for_cloud, recursive=False, double_curly_brackets=True, exclude_keys=[])
369
406
  if verbose: console.print(Panel(Text(processed_prompt_for_cloud, overflow="fold"), title="[cyan]Preprocessed Prompt for Cloud[/cyan]", expand=False))
370
407
 
371
408
  jwt_token: Optional[str] = None
@@ -421,14 +458,18 @@ def code_generator_main(
421
458
 
422
459
  if current_execution_is_local:
423
460
  if verbose: console.print("Executing code generator locally...")
461
+ # Expand includes, substitute vars, then double; pass to local generator with preprocess_prompt=False
462
+ local_prompt = pdd_preprocess(prompt_content, recursive=True, double_curly_brackets=False, exclude_keys=[])
463
+ local_prompt = _expand_vars(local_prompt, env_vars)
464
+ local_prompt = pdd_preprocess(local_prompt, recursive=False, double_curly_brackets=True, exclude_keys=[])
424
465
  generated_code_content, total_cost, model_name = local_code_generator_func(
425
- prompt=prompt_content,
466
+ prompt=local_prompt,
426
467
  language=language,
427
468
  strength=strength,
428
469
  temperature=temperature,
429
470
  time=time_budget,
430
471
  verbose=verbose,
431
- preprocess_prompt=True
472
+ preprocess_prompt=False
432
473
  )
433
474
  was_incremental_operation = False
434
475
  if verbose:
pdd/conflicts_main.py CHANGED
@@ -33,7 +33,8 @@ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optio
33
33
  force=ctx.obj.get('force', False),
34
34
  quiet=ctx.obj.get('quiet', False),
35
35
  command="conflicts",
36
- command_options=command_options
36
+ command_options=command_options,
37
+ context_override=ctx.obj.get('context')
37
38
  )
38
39
 
39
40
  # Load input files
@@ -91,4 +92,4 @@ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optio
91
92
  except Exception as e:
92
93
  if not ctx.obj.get('quiet', False):
93
94
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
94
- sys.exit(1)
95
+ sys.exit(1)
pdd/construct_paths.py CHANGED
@@ -56,6 +56,23 @@ def _load_pddrc_config(pddrc_path: Path) -> Dict[str, Any]:
56
56
  except Exception as e:
57
57
  raise ValueError(f"Error loading .pddrc: {e}")
58
58
 
59
+ def list_available_contexts(start_path: Optional[Path] = None) -> list[str]:
60
+ """Return sorted context names from the nearest .pddrc.
61
+
62
+ - Searches upward from `start_path` (or CWD) for a `.pddrc` file.
63
+ - If found, loads and validates it, then returns sorted context names.
64
+ - If no `.pddrc` exists, returns ["default"].
65
+ - Propagates ValueError for malformed `.pddrc` to allow callers to render
66
+ helpful errors.
67
+ """
68
+ pddrc = _find_pddrc_file(start_path)
69
+ if not pddrc:
70
+ return ["default"]
71
+ config = _load_pddrc_config(pddrc)
72
+ contexts = config.get("contexts", {})
73
+ names = sorted(contexts.keys()) if isinstance(contexts, dict) else []
74
+ return names or ["default"]
75
+
59
76
  def _detect_context(current_dir: Path, config: Dict[str, Any], context_override: Optional[str] = None) -> Optional[str]:
60
77
  """Detect the appropriate context based on current directory path."""
61
78
  if context_override:
@@ -433,6 +450,23 @@ def construct_paths(
433
450
  file_extension=".py", # Dummy extension
434
451
  context_config=context_config,
435
452
  )
453
+
454
+ # Honor .pddrc generate_output_path explicitly for sync discovery (robust to logger source)
455
+ try:
456
+ cfg_gen_dir = context_config.get("generate_output_path")
457
+ current_gen = output_paths_str.get("generate_output_path")
458
+ # Only override when generator placed code at CWD root (the problematic case)
459
+ if cfg_gen_dir and current_gen and Path(current_gen).parent.resolve() == Path.cwd().resolve():
460
+ # Preserve the filename selected by generate_output_paths (e.g., basename + ext)
461
+ gen_filename = Path(current_gen).name
462
+ base_dir = Path.cwd()
463
+ # Compose absolute path under configured directory
464
+ abs_cfg_gen_dir = (base_dir / cfg_gen_dir).resolve() if not Path(cfg_gen_dir).is_absolute() else Path(cfg_gen_dir)
465
+ output_paths_str["generate_output_path"] = str((abs_cfg_gen_dir / gen_filename).resolve())
466
+ except Exception:
467
+ # Best-effort override; fall back silently if anything goes wrong
468
+ pass
469
+
436
470
  # Infer base directories from a sample output path
437
471
  gen_path = Path(output_paths_str.get("generate_output_path", "src"))
438
472
 
@@ -627,6 +661,23 @@ def construct_paths(
627
661
  file_extension=file_extension,
628
662
  context_config=context_config,
629
663
  )
664
+
665
+ # For sync, explicitly honor .pddrc generate_output_path even if generator logged as 'default'
666
+ if command == "sync":
667
+ try:
668
+ cfg_gen_dir = context_config.get("generate_output_path")
669
+ current_gen = output_paths_str.get("generate_output_path")
670
+ # Only override when generator placed code at CWD root (the problematic case)
671
+ if cfg_gen_dir and current_gen and Path(current_gen).parent.resolve() == Path.cwd().resolve():
672
+ # Keep the filename chosen by generate_output_paths
673
+ gen_filename = Path(current_gen).name
674
+ # Resolve configured directory relative to CWD (or prompt file directory if available)
675
+ base_dir = Path.cwd()
676
+ abs_cfg_gen_dir = (base_dir / cfg_gen_dir).resolve() if not Path(cfg_gen_dir).is_absolute() else Path(cfg_gen_dir)
677
+ output_paths_str["generate_output_path"] = str((abs_cfg_gen_dir / gen_filename).resolve())
678
+ except Exception:
679
+ # Non-fatal; fall back to whatever generate_output_paths returned
680
+ pass
630
681
  # Convert to Path objects for internal use
631
682
  output_paths_resolved: Dict[str, Path] = {k: Path(v) for k, v in output_paths_str.items()}
632
683
 
@@ -31,7 +31,8 @@ def context_generator_main(ctx: click.Context, prompt_file: str, code_file: str,
31
31
  force=ctx.obj.get('force', False),
32
32
  quiet=ctx.obj.get('quiet', False),
33
33
  command="example",
34
- command_options=command_options
34
+ command_options=command_options,
35
+ context_override=ctx.obj.get('context')
35
36
  )
36
37
 
37
38
  # Load input files
pdd/crash_main.py CHANGED
@@ -77,7 +77,8 @@ def crash_main(
77
77
  force=force,
78
78
  quiet=quiet,
79
79
  command="crash",
80
- command_options=command_options
80
+ command_options=command_options,
81
+ context_override=ctx.obj.get('context')
81
82
  )
82
83
 
83
84
  prompt_content = input_strings["prompt_file"]
@@ -177,4 +178,4 @@ def crash_main(
177
178
  except Exception as e:
178
179
  if not quiet:
179
180
  rprint(f"[bold red]An unexpected error occurred:[/bold red] {str(e)}")
180
- sys.exit(1)
181
+ sys.exit(1)
@@ -60,4 +60,5 @@ LLM,del,.prompt
60
60
  prompt,del,.prompt
61
61
  TOML,#,.toml
62
62
  Log,del,.log
63
- reStructuredText,del,.rst
63
+ reStructuredText,del,.rst
64
+ Text,del,.txt
pdd/detect_change_main.py CHANGED
@@ -44,7 +44,8 @@ def detect_change_main(
44
44
  force=ctx.obj.get('force', False),
45
45
  quiet=ctx.obj.get('quiet', False),
46
46
  command="detect",
47
- command_options=command_options
47
+ command_options=command_options,
48
+ context_override=ctx.obj.get('context')
48
49
  )
49
50
 
50
51
  # Get change description content
@@ -100,4 +101,4 @@ def detect_change_main(
100
101
  except Exception as e:
101
102
  if not ctx.obj.get('quiet', False):
102
103
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
103
- sys.exit(1)
104
+ sys.exit(1)
pdd/fix_main.py CHANGED
@@ -97,7 +97,8 @@ def fix_main(
97
97
  quiet=ctx.obj.get('quiet', False),
98
98
  command="fix",
99
99
  command_options=command_options,
100
- create_error_file=loop # Only create error file if in loop mode
100
+ create_error_file=loop, # Only create error file if in loop mode
101
+ context_override=ctx.obj.get('context')
101
102
  )
102
103
 
103
104
  # Get parameters from context
@@ -296,4 +297,4 @@ def fix_main(
296
297
  # Print other errors normally, escaping the error string
297
298
  from rich.markup import escape # Ensure escape is imported
298
299
  rprint(f"[bold red]Error:[/bold red] {escape(str(e))}")
299
- sys.exit(1)
300
+ sys.exit(1)
@@ -204,6 +204,7 @@ def fix_verification_main(
204
204
  quiet=quiet,
205
205
  command="verify",
206
206
  command_options=command_options,
207
+ context_override=ctx.obj.get('context')
207
208
  )
208
209
  output_code_path = output_file_paths.get("output_code")
209
210
  output_results_path = output_file_paths.get("output_results")
pdd/pdd_completion.fish CHANGED
@@ -34,6 +34,7 @@ complete -c pdd -n "__fish_use_subcommand" -a verify -d "Verify functional corre
34
34
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l output -r -d "Output location for generated code"
35
35
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l original-prompt -r -d "Original prompt file for incremental generation"
36
36
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l incremental -d "Force incremental patching"
37
+ complete -c pdd -n "__fish_seen_subcommand_from generate" -s e -l env -xa "(env | cut -d= -f1 | sed 's/$/=/' | sort -u)" -d "Set template variable (KEY=VALUE) or read KEY from env"
37
38
  complete -c pdd -n "__fish_seen_subcommand_from generate" -a "(__fish_complete_suffix .prompt)"
38
39
 
39
40
  complete -c pdd -n "__fish_seen_subcommand_from example" -l output -r -d "Output location for example code"
pdd/pdd_completion.sh CHANGED
@@ -21,7 +21,7 @@ _pdd() {
21
21
  local commands="generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify"
22
22
 
23
23
  # Command-specific options
24
- local generate_opts="--output --original-prompt --incremental"
24
+ local generate_opts="--output --original-prompt --incremental --env -e"
25
25
  local example_opts="--output"
26
26
  local test_opts="--output --language --coverage-report --existing-tests --target-coverage --merge"
27
27
  local preprocess_opts="--output --xml --recursive --double --exclude"
@@ -46,6 +46,16 @@ _pdd() {
46
46
  # Complete command-specific options
47
47
  case ${words[1]} in
48
48
  generate)
49
+ # If completing the value for -e/--env, suggest environment variable names (with and without '=')
50
+ if [[ $prev == "-e" || $prev == "--env" ]]; then
51
+ local vars
52
+ vars=$(env | cut -d= -f1 | sort -u)
53
+ # Offer both KEY and KEY=
54
+ local vars_with_eq
55
+ vars_with_eq=$(printf '%s=\n' $vars)
56
+ COMPREPLY=($(compgen -W "$vars $vars_with_eq" -- "$cur"))
57
+ return
58
+ fi
49
59
  _complete_files ".prompt"
50
60
  COMPREPLY+=($(compgen -W "$generate_opts" -- "$cur"))
51
61
  ;;
@@ -157,4 +167,4 @@ _complete_files() {
157
167
  fi
158
168
  }
159
169
 
160
- complete -F _pdd pdd
170
+ complete -F _pdd pdd
pdd/pdd_completion.zsh CHANGED
@@ -65,6 +65,14 @@ _pdd_global_opts=(
65
65
  # Per-subcommand completion functions
66
66
  ##
67
67
 
68
+ # Helper: suggest environment variables (KEY and KEY=)
69
+ _pdd_env_vars() {
70
+ local -a envs envs_eq
71
+ envs=(${(f)"$(env | cut -d= -f1 | sort -u)"})
72
+ envs_eq=(${envs/%/=})
73
+ _describe -t envvars 'environment variables' envs_eq envs
74
+ }
75
+
68
76
  # generate
69
77
  # Usage: pdd [GLOBAL OPTIONS] generate [OPTIONS] PROMPT_FILE
70
78
  # Options:
@@ -77,6 +85,7 @@ _pdd_generate() {
77
85
  '--output=[Specify where to save the generated code.]:filename:_files' \
78
86
  '--original-prompt=[The original prompt file used to generate existing code.]:filename:_files' \
79
87
  '--incremental[Force incremental patching even if changes are significant.]' \
88
+ '(-e --env)'{-e,--env}'[Set template variable (KEY=VALUE) or read KEY from env]:template variable:_pdd_env_vars' \
80
89
  '1:prompt-file:_files' \
81
90
  '*:filename:_files'
82
91
  }
pdd/preprocess.py CHANGED
@@ -172,6 +172,14 @@ def double_curly(text: str, exclude_keys: Optional[List[str]] = None) -> str:
172
172
  "2": {{"id": "2", "name": "Resource Two"}}
173
173
  }}"""
174
174
 
175
+ # Protect ${IDENT} placeholders so they remain unchanged
176
+ # Use placeholders that won't collide with typical content
177
+ protected_vars: List[str] = []
178
+ def _protect_var(m):
179
+ protected_vars.append(m.group(0))
180
+ return f"__PDD_VAR_{len(protected_vars)-1}__"
181
+ text = re.sub(r"\$\{[A-Za-z_][A-Za-z0-9_]*\}", _protect_var, text)
182
+
175
183
  # First, protect any existing double curly braces
176
184
  text = re.sub(r'\{\{([^{}]*)\}\}', r'__ALREADY_DOUBLED__\1__END_ALREADY__', text)
177
185
 
@@ -188,6 +196,12 @@ def double_curly(text: str, exclude_keys: Optional[List[str]] = None) -> str:
188
196
 
189
197
  # Restore already doubled brackets
190
198
  text = re.sub(r'__ALREADY_DOUBLED__(.*?)__END_ALREADY__', r'{{\1}}', text)
199
+
200
+ # Restore protected ${IDENT} placeholders
201
+ def _restore_var(m):
202
+ idx = int(m.group(1))
203
+ return protected_vars[idx] if 0 <= idx < len(protected_vars) else m.group(0)
204
+ text = re.sub(r"__PDD_VAR_(\d+)__", _restore_var, text)
191
205
 
192
206
  # Special handling for code blocks
193
207
  code_block_pattern = r'```([\w\s]*)\n([\s\S]*?)```'
@@ -213,4 +227,4 @@ def double_curly(text: str, exclude_keys: Optional[List[str]] = None) -> str:
213
227
  # Process code blocks
214
228
  text = re.sub(code_block_pattern, process_code_block, text, flags=re.DOTALL)
215
229
 
216
- return text
230
+ return text
pdd/preprocess_main.py CHANGED
@@ -33,6 +33,7 @@ def preprocess_main(
33
33
  quiet=ctx.obj.get("quiet", False),
34
34
  command="preprocess",
35
35
  command_options=command_options,
36
+ context_override=ctx.obj.get('context')
36
37
  )
37
38
 
38
39
  # Load prompt file
@@ -76,4 +77,4 @@ def preprocess_main(
76
77
  except Exception as e:
77
78
  if not ctx.obj.get("quiet", False):
78
79
  rprint(f"[bold red]Error during preprocessing:[/bold red] {e}")
79
- sys.exit(1)
80
+ sys.exit(1)
pdd/split_main.py CHANGED
@@ -53,7 +53,8 @@ def split_main(
53
53
  force=ctx.obj.get('force', False),
54
54
  quiet=ctx.obj.get('quiet', False),
55
55
  command="split",
56
- command_options=command_options
56
+ command_options=command_options,
57
+ context_override=ctx.obj.get('context')
57
58
  )
58
59
 
59
60
  # Get parameters from context
@@ -113,4 +114,4 @@ def split_main(
113
114
  elif isinstance(e, ValueError):
114
115
  rprint("[yellow]Hint: Check if input files have valid content.[/yellow]")
115
116
 
116
- sys.exit(1)
117
+ sys.exit(1)
@@ -209,7 +209,7 @@ def get_extension(language: str) -> str:
209
209
  return extensions.get(language.lower(), language.lower())
210
210
 
211
211
 
212
- def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts") -> Dict[str, Path]:
212
+ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts", context_override: Optional[str] = None) -> Dict[str, Path]:
213
213
  """Returns a dictionary mapping file types to their expected Path objects."""
214
214
  import logging
215
215
  logger = logging.getLogger(__name__)
@@ -233,7 +233,8 @@ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts
233
233
  force=True,
234
234
  quiet=True,
235
235
  command="sync",
236
- command_options={"basename": basename, "language": language}
236
+ command_options={"basename": basename, "language": language},
237
+ context_override=context_override
237
238
  )
238
239
 
239
240
  import logging
@@ -299,7 +300,8 @@ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts
299
300
  force=True, # Use force=True to avoid interactive prompts during sync
300
301
  quiet=True,
301
302
  command="sync", # Use sync command to get more tolerant path handling
302
- command_options={"basename": basename, "language": language}
303
+ command_options={"basename": basename, "language": language},
304
+ context_override=context_override
303
305
  )
304
306
 
305
307
  # For sync command, output_file_paths contains the configured paths
@@ -332,7 +334,8 @@ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts
332
334
  # Get example path using example command
333
335
  _, _, example_output_paths, _ = construct_paths(
334
336
  input_file_paths={"prompt_file": prompt_path, "code_file": code_path},
335
- force=True, quiet=True, command="example", command_options={}
337
+ force=True, quiet=True, command="example", command_options={},
338
+ context_override=context_override
336
339
  )
337
340
  example_path = Path(example_output_paths.get('output', f"{basename}_example.{get_extension(language)}"))
338
341
 
@@ -340,7 +343,8 @@ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts
340
343
  try:
341
344
  _, _, test_output_paths, _ = construct_paths(
342
345
  input_file_paths={"prompt_file": prompt_path, "code_file": code_path},
343
- force=True, quiet=True, command="test", command_options={}
346
+ force=True, quiet=True, command="test", command_options={},
347
+ context_override=context_override
344
348
  )
345
349
  test_path = Path(test_output_paths.get('output', f"test_{basename}.{get_extension(language)}"))
346
350
  except FileNotFoundError:
@@ -365,14 +369,16 @@ def get_pdd_file_paths(basename: str, language: str, prompts_dir: str = "prompts
365
369
  # Get configured directories by using construct_paths with just the prompt file
366
370
  _, _, example_output_paths, _ = construct_paths(
367
371
  input_file_paths={"prompt_file": prompt_path},
368
- force=True, quiet=True, command="example", command_options={}
372
+ force=True, quiet=True, command="example", command_options={},
373
+ context_override=context_override
369
374
  )
370
375
  example_path = Path(example_output_paths.get('output', f"{basename}_example.{get_extension(language)}"))
371
376
 
372
377
  try:
373
378
  _, _, test_output_paths, _ = construct_paths(
374
379
  input_file_paths={"prompt_file": prompt_path},
375
- force=True, quiet=True, command="test", command_options={}
380
+ force=True, quiet=True, command="test", command_options={},
381
+ context_override=context_override
376
382
  )
377
383
  test_path = Path(test_output_paths.get('output', f"test_{basename}.{get_extension(language)}"))
378
384
  except Exception:
@@ -768,7 +774,7 @@ def _check_example_success_history(basename: str, language: str) -> bool:
768
774
  return False
769
775
 
770
776
 
771
- def sync_determine_operation(basename: str, language: str, target_coverage: float, budget: float = 10.0, log_mode: bool = False, prompts_dir: str = "prompts", skip_tests: bool = False, skip_verify: bool = False) -> SyncDecision:
777
+ def sync_determine_operation(basename: str, language: str, target_coverage: float, budget: float = 10.0, log_mode: bool = False, prompts_dir: str = "prompts", skip_tests: bool = False, skip_verify: bool = False, context_override: Optional[str] = None) -> SyncDecision:
772
778
  """
773
779
  Core decision-making function for sync operations with skip flag awareness.
774
780
 
@@ -788,14 +794,14 @@ def sync_determine_operation(basename: str, language: str, target_coverage: floa
788
794
 
789
795
  if log_mode:
790
796
  # Skip locking for read-only analysis
791
- return _perform_sync_analysis(basename, language, target_coverage, budget, prompts_dir, skip_tests, skip_verify)
797
+ return _perform_sync_analysis(basename, language, target_coverage, budget, prompts_dir, skip_tests, skip_verify, context_override)
792
798
  else:
793
799
  # Normal exclusive locking for actual operations
794
800
  with SyncLock(basename, language) as lock:
795
- return _perform_sync_analysis(basename, language, target_coverage, budget, prompts_dir, skip_tests, skip_verify)
801
+ return _perform_sync_analysis(basename, language, target_coverage, budget, prompts_dir, skip_tests, skip_verify, context_override)
796
802
 
797
803
 
798
- def _perform_sync_analysis(basename: str, language: str, target_coverage: float, budget: float, prompts_dir: str = "prompts", skip_tests: bool = False, skip_verify: bool = False) -> SyncDecision:
804
+ def _perform_sync_analysis(basename: str, language: str, target_coverage: float, budget: float, prompts_dir: str = "prompts", skip_tests: bool = False, skip_verify: bool = False, context_override: Optional[str] = None) -> SyncDecision:
799
805
  """
800
806
  Perform the sync state analysis without locking concerns.
801
807
 
@@ -846,7 +852,7 @@ def _perform_sync_analysis(basename: str, language: str, target_coverage: float,
846
852
  # Check test failures (after crash verification check)
847
853
  if run_report.tests_failed > 0:
848
854
  # First check if the test file actually exists
849
- pdd_files = get_pdd_file_paths(basename, language, prompts_dir)
855
+ pdd_files = get_pdd_file_paths(basename, language, prompts_dir, context_override=context_override)
850
856
  test_file = pdd_files.get('test')
851
857
 
852
858
  # Only suggest 'fix' if test file exists
@@ -945,7 +951,7 @@ def _perform_sync_analysis(basename: str, language: str, target_coverage: float,
945
951
  )
946
952
 
947
953
  # 2. Analyze File State
948
- paths = get_pdd_file_paths(basename, language, prompts_dir)
954
+ paths = get_pdd_file_paths(basename, language, prompts_dir, context_override=context_override)
949
955
  current_hashes = calculate_current_hashes(paths)
950
956
 
951
957
  # 3. Implement the Decision Tree
@@ -1264,7 +1270,14 @@ def _perform_sync_analysis(basename: str, language: str, target_coverage: float,
1264
1270
  )
1265
1271
 
1266
1272
 
1267
- def analyze_conflict_with_llm(basename: str, language: str, fingerprint: Fingerprint, changed_files: List[str], prompts_dir: str = "prompts") -> SyncDecision:
1273
+ def analyze_conflict_with_llm(
1274
+ basename: str,
1275
+ language: str,
1276
+ fingerprint: Fingerprint,
1277
+ changed_files: List[str],
1278
+ prompts_dir: str = "prompts",
1279
+ context_override: Optional[str] = None,
1280
+ ) -> SyncDecision:
1268
1281
  """
1269
1282
  Resolve complex sync conflicts using an LLM.
1270
1283
 
@@ -1297,7 +1310,7 @@ def analyze_conflict_with_llm(basename: str, language: str, fingerprint: Fingerp
1297
1310
  )
1298
1311
 
1299
1312
  # 2. Gather file paths and diffs
1300
- paths = get_pdd_file_paths(basename, language, prompts_dir)
1313
+ paths = get_pdd_file_paths(basename, language, prompts_dir, context_override=context_override)
1301
1314
 
1302
1315
  # Generate diffs for changed files
1303
1316
  diffs = {}
pdd/sync_main.py CHANGED
@@ -192,6 +192,7 @@ def sync_main(
192
192
  log=True,
193
193
  verbose=verbose,
194
194
  quiet=quiet,
195
+ context_override=context_override,
195
196
  )
196
197
  return {}, 0.0, ""
197
198
 
@@ -280,6 +281,7 @@ def sync_main(
280
281
  review_examples=review_examples,
281
282
  local=local,
282
283
  context_config=resolved_config,
284
+ context_override=context_override,
283
285
  )
284
286
 
285
287
  lang_cost = sync_result.get("total_cost", 0.0)
@@ -330,4 +332,4 @@ def sync_main(
330
332
  aggregated_results["total_cost"] = total_cost
331
333
  aggregated_results["primary_model"] = primary_model
332
334
 
333
- return aggregated_results, total_cost, primary_model
335
+ return aggregated_results, total_cost, primary_model
pdd/sync_orchestration.py CHANGED
@@ -341,6 +341,7 @@ def sync_orchestration(
341
341
  review_examples: bool = False,
342
342
  local: bool = False,
343
343
  context_config: Optional[Dict[str, str]] = None,
344
+ context_override: Optional[str] = None,
344
345
  ) -> Dict[str, Any]:
345
346
  """
346
347
  Orchestrates the complete PDD sync workflow with parallel animation.
@@ -358,7 +359,7 @@ def sync_orchestration(
358
359
 
359
360
  # --- Initialize State and Paths ---
360
361
  try:
361
- pdd_files = get_pdd_file_paths(basename, language, prompts_dir)
362
+ pdd_files = get_pdd_file_paths(basename, language, prompts_dir, context_override=context_override)
362
363
  # Debug: Print the paths we got
363
364
  print(f"DEBUG: get_pdd_file_paths returned:")
364
365
  print(f" test: {pdd_files.get('test', 'N/A')}")
@@ -457,7 +458,7 @@ def sync_orchestration(
457
458
  "percentage": (budget_remaining / budget) * 100
458
459
  })
459
460
 
460
- decision = sync_determine_operation(basename, language, target_coverage, budget_remaining, False, prompts_dir, skip_tests, skip_verify)
461
+ decision = sync_determine_operation(basename, language, target_coverage, budget_remaining, False, prompts_dir, skip_tests, skip_verify, context_override)
461
462
  operation = decision.operation
462
463
 
463
464
  # Create log entry with decision info
pdd/trace_main.py CHANGED
@@ -39,7 +39,8 @@ def trace_main(ctx: click.Context, prompt_file: str, code_file: str, code_line:
39
39
  force=ctx.obj.get('force', False),
40
40
  quiet=quiet,
41
41
  command="trace",
42
- command_options=command_options
42
+ command_options=command_options,
43
+ context_override=ctx.obj.get('context')
43
44
  )
44
45
  logger.debug("File paths constructed successfully")
45
46
 
pdd/update_main.py CHANGED
@@ -48,6 +48,7 @@ def update_main(
48
48
  quiet=ctx.obj.get("quiet", False),
49
49
  command="update",
50
50
  command_options=command_options,
51
+ context_override=ctx.obj.get('context')
51
52
  )
52
53
 
53
54
  # Extract input strings
@@ -101,4 +102,4 @@ def update_main(
101
102
  except Exception as e:
102
103
  if not ctx.obj.get("quiet", False):
103
104
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
104
- sys.exit(1)
105
+ sys.exit(1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pdd-cli
3
- Version: 0.0.54
3
+ Version: 0.0.56
4
4
  Summary: PDD (Prompt-Driven Development) Command Line Interface
5
5
  Author: Greg Tanaka
6
6
  Author-email: glt@alumni.caltech.edu
@@ -51,7 +51,7 @@ Requires-Dist: build; extra == "dev"
51
51
  Requires-Dist: twine; extra == "dev"
52
52
  Dynamic: license-file
53
53
 
54
- .. image:: https://img.shields.io/badge/pdd--cli-v0.0.54-blue
54
+ .. image:: https://img.shields.io/badge/pdd--cli-v0.0.56-blue
55
55
  :alt: PDD-CLI Version
56
56
 
57
57
  .. image:: https://img.shields.io/badge/Discord-join%20chat-7289DA.svg?logo=discord&logoColor=white&link=https://discord.gg/Yp4RTh8bG7
@@ -128,7 +128,7 @@ After installation, verify:
128
128
 
129
129
  pdd --version
130
130
 
131
- You'll see the current PDD version (e.g., 0.0.54).
131
+ You'll see the current PDD version (e.g., 0.0.56).
132
132
 
133
133
  Getting Started with Examples
134
134
  -----------------------------
@@ -1,35 +1,35 @@
1
- pdd/__init__.py,sha256=73_l5GSekP6w1F-hNrcnRsENsm3NCUb3G8OhNq8BqZQ,633
2
- pdd/auto_deps_main.py,sha256=iV2khcgSejiXjh5hiQqeu_BJQOLfTKXhMx14j6vRlf8,3916
1
+ pdd/__init__.py,sha256=ca8MfVovv1zfjBoDhLPWEWu1bOMwbT7XY3CcRF635Sg,633
2
+ pdd/auto_deps_main.py,sha256=cpP3bbzVL3jomrGinpzTxzIDIC8tmDDYOwUAC1TKRaw,3970
3
3
  pdd/auto_include.py,sha256=OJcdcwTwJNqHPHKG9P4m9Ij-PiLex0EbuwJP0uiQi_Y,7484
4
4
  pdd/auto_update.py,sha256=w6jzTnMiYRNpwQHQxWNiIAwQ0d6xh1iOB3xgDsabWtc,5236
5
- pdd/bug_main.py,sha256=INFWwD3TU00cBmTqFcScAoK25LsZE1zkinmnSM7ElS0,4787
5
+ pdd/bug_main.py,sha256=EtaGTuucQ7VgqOhyg4o6GFG7_QtTsDPTrRdGJWT648M,4841
6
6
  pdd/bug_to_unit_test.py,sha256=BoQqNyKQpBQDW8-JwBH_RX4RHRSiU8Kk3EplFrkECt0,6665
7
7
  pdd/change.py,sha256=Hg_x0pa370-e6oDiczaTgFAy3Am9ReCPkqFrvqv4U38,6114
8
- pdd/change_main.py,sha256=oTQz9DUy6pIqq5CJzHIk01NrC88Xrm4FNEu0e-1Hx5Y,27748
9
- pdd/cli.py,sha256=2eB0CS4E4dcu9gStWnVDUjZxO2V_8prxVrwcbkXJ9o8,44284
10
- pdd/cmd_test_main.py,sha256=5ftxDNNklDlHodkW8Rluvo3NKMHyMNhumG7G8mSoM9g,7716
8
+ pdd/change_main.py,sha256=04VHiO_D-jlfeRn6rrVH7ZTA5agXPoJGm1StGI8--XY,27804
9
+ pdd/cli.py,sha256=njGVezstZGTeinhrUdOQ2K21Wpt1jiA5jU6BT7OZpaY,46782
10
+ pdd/cmd_test_main.py,sha256=M-i5x26ORXurt_pu8x1sgLAyVIItbuRThiux4wBg3Ls,7768
11
11
  pdd/code_generator.py,sha256=AxMRZKGIlLh9xWdn2FA6b3zSoZ-5TIZNIAzqjFboAQs,4718
12
- pdd/code_generator_main.py,sha256=whj_IaqoU-OQR9CW9rFRGzdua7cr9YnIuDsnmscE2jY,25815
12
+ pdd/code_generator_main.py,sha256=RwyjzrC1NHOfJJkfxdwWwJf_UNAy2lCWjSyasucG2KU,27997
13
13
  pdd/comment_line.py,sha256=sX2hf4bG1fILi_rvI9MkkwCZ2IitgKkW7nOiw8aQKPY,1845
14
14
  pdd/conflicts_in_prompts.py,sha256=9N3rZWdJUGayOTOgnHW9G_Jm1C9G4Y8hSLhnURc1BkY,4890
15
- pdd/conflicts_main.py,sha256=U23aJqJ6pgLDDCz-AaejWnG-qsTGAhZ9024KsHR9pYU,3650
16
- pdd/construct_paths.py,sha256=aG1v0NR-FIAQPLzhnD5mY7Rd_D_21fmzmM4x8Mj9hw8,30962
15
+ pdd/conflicts_main.py,sha256=SK8eljbAY_wWT4dieRSsQwBrU1Dm90MImry3AGL-Dj4,3704
16
+ pdd/construct_paths.py,sha256=BDXDazbMOwgSLbYhCmgUwYIg0QPrjj8AVRbmFGJQIm4,33966
17
17
  pdd/context_generator.py,sha256=HAptXNSFTGu4rHw2SceiCcoCunIemESNCMVNqY1w6hU,6107
18
- pdd/context_generator_main.py,sha256=3riIDV2QskAMP-eq-uFicML79zQ5X-KMdw91g1RbP4E,4094
18
+ pdd/context_generator_main.py,sha256=ORpokRM9ZToBdMhFpM76iXkCGySyv2wsbQiyl4Ir5lQ,4147
19
19
  pdd/continue_generation.py,sha256=nEkAKZd9lM7OelQkffP99Y-JLxTFZeY3AH9VLRQCjHg,7854
20
- pdd/crash_main.py,sha256=BmTbFSrEAICS-Ji7sTFI9SHpTTUZot16918wiypNnhc,7611
20
+ pdd/crash_main.py,sha256=ICiMbpCxdu6bjtLssKgTlVQK3PXXXjFRAX9CL0VJuH8,7665
21
21
  pdd/detect_change.py,sha256=mA6k62xqeU1UG__CjzveJK0JDiRAO7AAC-JUfS0i2HQ,5510
22
- pdd/detect_change_main.py,sha256=J8YBJiWZNrE1ceCkS8CFtShTgaScJE9oYO0m_OXbXV0,3809
22
+ pdd/detect_change_main.py,sha256=aEehiFlKAfqegW6e18gVMKxbcSQpV9gqLpyX7Bl7Chw,3863
23
23
  pdd/edit_file.py,sha256=-FhZ-KGKYkPbnt0zFiDnnosPLh3bbKmften0Ios4-90,35017
24
24
  pdd/find_section.py,sha256=lz_FPY4KDCRAGlL1pWVZiutUNv7E4KsDFK-ymDWA_Ec,962
25
25
  pdd/fix_code_loop.py,sha256=LQXYQuFMjMM4yo6oJaFKyCg9OHpFwATp6QeHm8TsGR4,24468
26
26
  pdd/fix_code_module_errors.py,sha256=jKH88KunVhof1MYRI_F42_YnLt5k4lif4YztQgzB9g8,5446
27
27
  pdd/fix_error_loop.py,sha256=lhJrfJuFi_dB7hWTbpJfLEEZaltaRXYDl0XScDLvbdk,26464
28
28
  pdd/fix_errors_from_unit_tests.py,sha256=fIqEfVIEx8PPSAzWu5nhin_remKu4c0_o51AN3g_x6s,9398
29
- pdd/fix_main.py,sha256=7TbHVUM2HuzCVMY-B2iHzvy5YEnKaaSbU1ZzXN7YG3U,14004
29
+ pdd/fix_main.py,sha256=F0A9nv0ayYAqTMthluAzw2gvuFwEcZpec_0v8Krfd0s,14058
30
30
  pdd/fix_verification_errors.py,sha256=HvqGGdQqHq7OERmzcYP8Ft5nX_xthwVPJPG-YLv6VNM,17444
31
31
  pdd/fix_verification_errors_loop.py,sha256=80899aKg27wM9-I5wOoiHbbla5Bfm0bOibx7WNmiWPE,55715
32
- pdd/fix_verification_main.py,sha256=nZP0uYx9WLULSl17Du9mgENNO7UWgXFXJWKxIJy0e3Q,24237
32
+ pdd/fix_verification_main.py,sha256=lnhe4Sr6eV8EZEOTsSrzgxB_Ili5OJ0ZeFij_4Coc-E,24289
33
33
  pdd/generate_output_paths.py,sha256=YYxeEKiHf39s-giDu4ak_xufyfRqkuCcvowVuOytXaI,24224
34
34
  pdd/generate_test.py,sha256=Kd-iZnbHhGsgY6aD7Nu3i10ZNDpu3CVQROwAOYcjBok,6993
35
35
  pdd/get_comment.py,sha256=yuRtk68-SDkMaGzOSyIFdldRoymJBRSKjOYkr0narVc,2627
@@ -45,32 +45,32 @@ pdd/llm_invoke.py,sha256=US8J9oZZdVgLIjcmOI-YyHu6z2un43ZN811IcQBmAIA,96728
45
45
  pdd/load_prompt_template.py,sha256=stt42Og0PzXy0N_bsaivk5e2l5z_BnHiXIJZM14oVWw,2673
46
46
  pdd/logo_animation.py,sha256=n6HJWzuFze2csAAW2-zbxfjvWFYRI4hIdwVBtHBOkj4,20782
47
47
  pdd/mcp_config.json,sha256=D3ctWHlShvltbtH37zbYb6smVE0V80_lGjDKDIqsSBE,124
48
- pdd/pdd_completion.fish,sha256=_qxDWL-BrgEVLqFB-uPFGOCRwp-EPDBg2F0NJ5kq03o,11908
49
- pdd/pdd_completion.sh,sha256=e0K4Ai1yJRj6Fd6Qpk-brAEicW3ciPSyIuNMQkSG5mI,5342
50
- pdd/pdd_completion.zsh,sha256=dglBWoAehcnoSdiomY-VkmGPVrs0dgy0FMsPe8KlvGI,15002
48
+ pdd/pdd_completion.fish,sha256=K4qT52-9LG6apKVH0Bgfqk1Kqfketb47ge_6mcacVFE,12089
49
+ pdd/pdd_completion.sh,sha256=DI2gJc7IxqjgKyEgjmoi8ev3f9Gs3LJrRgSQ-u2T6ms,5844
50
+ pdd/pdd_completion.zsh,sha256=f4BxXrLZUEcFeIUmXWdnFOTnTpJBFZ8KGpnIdjDdKFo,15353
51
51
  pdd/postprocess.py,sha256=mNw3iSDxE-eTYo3QwJCj_EmdEnnB5ysUN62YPapC_IM,4433
52
52
  pdd/postprocess_0.py,sha256=OW17GyCFLYErCyWh2tL4syuho3q2yFf2wyekQ4BLdPM,2168
53
- pdd/preprocess.py,sha256=UB1rqSNscUC-JHujvGb11LJ5OadZ3GVD1Qeq1dI6HMc,9508
54
- pdd/preprocess_main.py,sha256=CwmU3WcXdsZ3vJN0Z0eCv_CbiybXu4O9BxFkCmZz6v4,3209
53
+ pdd/preprocess.py,sha256=XdMSJDqkMQk_wO7ysOYELIpIXEIig-9pBqiEhm9QdsU,10108
54
+ pdd/preprocess_main.py,sha256=WGhOB9qEu7MmFoyXNml_AmqGii73LJWngx4kTlZ526k,3262
55
55
  pdd/process_csv_change.py,sha256=ckNqVPRooWVyIvmqjdEgo2PDLnpoQ6Taa2dUaWGRlzU,27926
56
56
  pdd/pytest_output.py,sha256=IrRKYneW_F6zv9WaJwKFGnOBLFBFjk1CnhO_EVAjb9E,6612
57
57
  pdd/python_env_detector.py,sha256=y-QESoPNiKaD821uz8okX-9qA-oqvH9cQHY2_MwFHzU,5194
58
58
  pdd/split.py,sha256=9lWrh-JOjOpxRp4-s1VL7bqJMVWlsmY5LxONT7sYM8A,5288
59
- pdd/split_main.py,sha256=GJzkWvDB6AA_duT2nZTRIvPmrX-ePhDRpZuog5xFCT0,4791
59
+ pdd/split_main.py,sha256=52rcZoeS_wpYRiqbqMUgr_hUY7GS62otwzDfuAGi6YA,4845
60
60
  pdd/summarize_directory.py,sha256=BR3-yGcmUdDT26vWLGokBo6mAZzaT7PzoY_qZriH3cc,10061
61
61
  pdd/sync_animation.py,sha256=e7Qb4m70BHYpl37CuuF-95j-APctPL4Zm_o1PSTTRFQ,28070
62
- pdd/sync_determine_operation.py,sha256=16Co4_IE0AZBLPdICi2MqW3730hiyLdqOf2kZcQA2cc,59590
63
- pdd/sync_main.py,sha256=2XUZZL9oIiNVsVohdsMpvrNoV8XkXhEKyt5bb2HlNHI,13641
64
- pdd/sync_orchestration.py,sha256=W23k6-acwkWvd6UNtsrpFbgx_SW5HwlDbFhIHd1AL30,69869
62
+ pdd/sync_determine_operation.py,sha256=o4wQSaOP3rGzkPuB5aVySdzhYdHiMWQvXFZPHZl99pc,60248
63
+ pdd/sync_main.py,sha256=MrQldtBDeClNPtBECRR4Sy5LM_-TkPSNSnuNXdTxb10,13744
64
+ pdd/sync_orchestration.py,sha256=tQydUxozSikHbXWBb8GtZMEDR3aRwePKdEGZukuH6uM,69966
65
65
  pdd/trace.py,sha256=Ty-r3ZQC5k3PUCUacgfzg4sO8RgcH8kquSDau-jHkec,10874
66
- pdd/trace_main.py,sha256=5gVmk60NY67eCynWGM_qG81iLZj4G9epneNgqUGw_GU,4939
66
+ pdd/trace_main.py,sha256=SOnMEHta9WaMAxulMrk6aFojma4ti61ILvJp3NVhpbg,4992
67
67
  pdd/track_cost.py,sha256=vsLrQmipjG1BW4P9Wl33qoS8XFJDYBVY4ruQqKjPEx0,4238
68
68
  pdd/unfinished_prompt.py,sha256=8En5NZqg6eCL6b451T_ndJx5ln1XDtFJyieW4pKRakE,5955
69
- pdd/update_main.py,sha256=okTsl-oamzCOqjpircs58urBt4Cu7PXxOztvc57088Q,4332
69
+ pdd/update_main.py,sha256=SWCd7Us3YatrDR7B66dQCpRCIgQoMHysPzxa4dedVmk,4385
70
70
  pdd/update_model_costs.py,sha256=RfeOlAHtc1FCx47A7CjrH2t5WXQclQ_9uYtNjtQh75I,22998
71
71
  pdd/update_prompt.py,sha256=zc-HiI1cwGBkJHVmNDyoSZa13lZH90VdB9l8ajdj6Kk,4543
72
72
  pdd/xml_tagger.py,sha256=5Bc3HRm7iz_XjBdzQIcMb8KocUQ8PELI2NN5Gw4amd4,4825
73
- pdd/data/language_format.csv,sha256=yQW5PuqIpihhDF9r_4o5x1CNUU5yyx-mdhbS6GQaEEI,918
73
+ pdd/data/language_format.csv,sha256=pWF3yCh6xOFNmrr2TxTnye7EPKT3c-LPjmYV3Qc0a-Y,933
74
74
  pdd/data/llm_model.csv,sha256=UxllgS0btSpCKpPgPnaTFAtZsAynBhLeZyoIVo0Tpwc,1698
75
75
  pdd/prompts/auto_include_LLM.prompt,sha256=sNF2rdJu9wJ8c0lwjCfZ9ZReX8zGXRUNehRs1ZiyDoc,12108
76
76
  pdd/prompts/bug_to_unit_test_LLM.prompt,sha256=KdMkvRVnjVSf0NTYIaDXIMT93xPttXEwkMpjWx5leLs,1588
@@ -108,9 +108,9 @@ pdd/prompts/trim_results_start_LLM.prompt,sha256=OKz8fAf1cYWKWgslFOHEkUpfaUDARh3
108
108
  pdd/prompts/unfinished_prompt_LLM.prompt,sha256=vud_G9PlVv9Ig64uBC-hPEVFRk5lwpc8pW6tOIxJM4I,5082
109
109
  pdd/prompts/update_prompt_LLM.prompt,sha256=prIc8uLp2jqnLTHt6JvWDZGanPZipivhhYeXe0lVaYw,1328
110
110
  pdd/prompts/xml_convertor_LLM.prompt,sha256=YGRGXJeg6EhM9690f-SKqQrKqSJjLFD51UrPOlO0Frg,2786
111
- pdd_cli-0.0.54.dist-info/licenses/LICENSE,sha256=kvTJnnxPVTYlGKSY4ZN1kzdmJ0lxRdNWxgupaB27zsU,1066
112
- pdd_cli-0.0.54.dist-info/METADATA,sha256=eXZXSfmCMSZQ1Wb6Mqvz4z6jTj3Oq1DnEpBj9_NTZ7E,12597
113
- pdd_cli-0.0.54.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
114
- pdd_cli-0.0.54.dist-info/entry_points.txt,sha256=Kr8HtNVb8uHZtQJNH4DnF8j7WNgWQbb7_Pw5hECSR-I,36
115
- pdd_cli-0.0.54.dist-info/top_level.txt,sha256=xjnhIACeMcMeDfVNREgQZl4EbTni2T11QkL5r7E-sbE,4
116
- pdd_cli-0.0.54.dist-info/RECORD,,
111
+ pdd_cli-0.0.56.dist-info/licenses/LICENSE,sha256=kvTJnnxPVTYlGKSY4ZN1kzdmJ0lxRdNWxgupaB27zsU,1066
112
+ pdd_cli-0.0.56.dist-info/METADATA,sha256=xVQb-QcyhglRDa-RDyTZ4EuSvQccTUcdlfaL5L8PR9s,12597
113
+ pdd_cli-0.0.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
114
+ pdd_cli-0.0.56.dist-info/entry_points.txt,sha256=Kr8HtNVb8uHZtQJNH4DnF8j7WNgWQbb7_Pw5hECSR-I,36
115
+ pdd_cli-0.0.56.dist-info/top_level.txt,sha256=xjnhIACeMcMeDfVNREgQZl4EbTni2T11QkL5r7E-sbE,4
116
+ pdd_cli-0.0.56.dist-info/RECORD,,