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 +1 -1
- pdd/auto_deps_main.py +3 -2
- pdd/bug_main.py +3 -2
- pdd/change_main.py +1 -0
- pdd/cli.py +78 -8
- pdd/cmd_test_main.py +1 -0
- pdd/code_generator_main.py +48 -7
- pdd/conflicts_main.py +3 -2
- pdd/construct_paths.py +51 -0
- pdd/context_generator_main.py +2 -1
- pdd/crash_main.py +3 -2
- pdd/data/language_format.csv +2 -1
- pdd/detect_change_main.py +3 -2
- pdd/fix_main.py +3 -2
- pdd/fix_verification_main.py +1 -0
- pdd/pdd_completion.fish +1 -0
- pdd/pdd_completion.sh +12 -2
- pdd/pdd_completion.zsh +9 -0
- pdd/preprocess.py +15 -1
- pdd/preprocess_main.py +2 -1
- pdd/split_main.py +3 -2
- pdd/sync_determine_operation.py +28 -15
- pdd/sync_main.py +3 -1
- pdd/sync_orchestration.py +3 -2
- pdd/trace_main.py +2 -1
- pdd/update_main.py +2 -1
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/METADATA +3 -3
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/RECORD +32 -32
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.54.dist-info → pdd_cli-0.0.56.dist-info}/top_level.txt +0 -0
pdd/__init__.py
CHANGED
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
|
-
"--
|
|
285
|
-
"
|
|
326
|
+
"--incremental",
|
|
327
|
+
"incremental_flag",
|
|
286
328
|
is_flag=True,
|
|
287
329
|
default=False,
|
|
288
|
-
help="Force incremental
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
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
pdd/code_generator_main.py
CHANGED
|
@@ -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=
|
|
342
|
-
new_prompt=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
|
pdd/context_generator_main.py
CHANGED
|
@@ -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)
|
pdd/data/language_format.csv
CHANGED
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)
|
pdd/fix_verification_main.py
CHANGED
|
@@ -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)
|
pdd/sync_determine_operation.py
CHANGED
|
@@ -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(
|
|
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.
|
|
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
|
+
.. 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.
|
|
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=
|
|
2
|
-
pdd/auto_deps_main.py,sha256=
|
|
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=
|
|
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=
|
|
9
|
-
pdd/cli.py,sha256=
|
|
10
|
-
pdd/cmd_test_main.py,sha256=
|
|
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=
|
|
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=
|
|
16
|
-
pdd/construct_paths.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
49
|
-
pdd/pdd_completion.sh,sha256=
|
|
50
|
-
pdd/pdd_completion.zsh,sha256=
|
|
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=
|
|
54
|
-
pdd/preprocess_main.py,sha256=
|
|
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=
|
|
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=
|
|
63
|
-
pdd/sync_main.py,sha256=
|
|
64
|
-
pdd/sync_orchestration.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
112
|
-
pdd_cli-0.0.
|
|
113
|
-
pdd_cli-0.0.
|
|
114
|
-
pdd_cli-0.0.
|
|
115
|
-
pdd_cli-0.0.
|
|
116
|
-
pdd_cli-0.0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|