pdd-cli 0.0.45__py3-none-any.whl → 0.0.118__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. pdd/__init__.py +40 -8
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +497 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +526 -0
  6. pdd/agentic_common.py +598 -0
  7. pdd/agentic_crash.py +534 -0
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +426 -0
  10. pdd/agentic_fix.py +1294 -0
  11. pdd/agentic_langtest.py +162 -0
  12. pdd/agentic_update.py +387 -0
  13. pdd/agentic_verify.py +183 -0
  14. pdd/architecture_sync.py +565 -0
  15. pdd/auth_service.py +210 -0
  16. pdd/auto_deps_main.py +71 -51
  17. pdd/auto_include.py +245 -5
  18. pdd/auto_update.py +125 -47
  19. pdd/bug_main.py +196 -23
  20. pdd/bug_to_unit_test.py +2 -0
  21. pdd/change_main.py +11 -4
  22. pdd/cli.py +22 -1181
  23. pdd/cmd_test_main.py +350 -150
  24. pdd/code_generator.py +60 -18
  25. pdd/code_generator_main.py +790 -57
  26. pdd/commands/__init__.py +48 -0
  27. pdd/commands/analysis.py +306 -0
  28. pdd/commands/auth.py +309 -0
  29. pdd/commands/connect.py +290 -0
  30. pdd/commands/fix.py +163 -0
  31. pdd/commands/generate.py +257 -0
  32. pdd/commands/maintenance.py +175 -0
  33. pdd/commands/misc.py +87 -0
  34. pdd/commands/modify.py +256 -0
  35. pdd/commands/report.py +144 -0
  36. pdd/commands/sessions.py +284 -0
  37. pdd/commands/templates.py +215 -0
  38. pdd/commands/utility.py +110 -0
  39. pdd/config_resolution.py +58 -0
  40. pdd/conflicts_main.py +8 -3
  41. pdd/construct_paths.py +589 -111
  42. pdd/context_generator.py +10 -2
  43. pdd/context_generator_main.py +175 -76
  44. pdd/continue_generation.py +53 -10
  45. pdd/core/__init__.py +33 -0
  46. pdd/core/cli.py +527 -0
  47. pdd/core/cloud.py +237 -0
  48. pdd/core/dump.py +554 -0
  49. pdd/core/errors.py +67 -0
  50. pdd/core/remote_session.py +61 -0
  51. pdd/core/utils.py +90 -0
  52. pdd/crash_main.py +262 -33
  53. pdd/data/language_format.csv +71 -63
  54. pdd/data/llm_model.csv +20 -18
  55. pdd/detect_change_main.py +5 -4
  56. pdd/docs/prompting_guide.md +864 -0
  57. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  58. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  59. pdd/fix_code_loop.py +523 -95
  60. pdd/fix_code_module_errors.py +6 -2
  61. pdd/fix_error_loop.py +491 -92
  62. pdd/fix_errors_from_unit_tests.py +4 -3
  63. pdd/fix_main.py +278 -21
  64. pdd/fix_verification_errors.py +12 -100
  65. pdd/fix_verification_errors_loop.py +529 -286
  66. pdd/fix_verification_main.py +294 -89
  67. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  68. pdd/frontend/dist/assets/index-DQ3wkeQ2.js +449 -0
  69. pdd/frontend/dist/index.html +376 -0
  70. pdd/frontend/dist/logo.svg +33 -0
  71. pdd/generate_output_paths.py +139 -15
  72. pdd/generate_test.py +218 -146
  73. pdd/get_comment.py +19 -44
  74. pdd/get_extension.py +8 -9
  75. pdd/get_jwt_token.py +318 -22
  76. pdd/get_language.py +8 -7
  77. pdd/get_run_command.py +75 -0
  78. pdd/get_test_command.py +68 -0
  79. pdd/git_update.py +70 -19
  80. pdd/incremental_code_generator.py +2 -2
  81. pdd/insert_includes.py +13 -4
  82. pdd/llm_invoke.py +1711 -181
  83. pdd/load_prompt_template.py +19 -12
  84. pdd/path_resolution.py +140 -0
  85. pdd/pdd_completion.fish +25 -2
  86. pdd/pdd_completion.sh +30 -4
  87. pdd/pdd_completion.zsh +79 -4
  88. pdd/postprocess.py +14 -4
  89. pdd/preprocess.py +293 -24
  90. pdd/preprocess_main.py +41 -6
  91. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  92. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  93. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  94. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  95. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  96. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  97. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  98. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  99. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  100. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  101. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  102. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  103. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +131 -0
  104. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  105. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  106. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  107. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  108. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  109. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  110. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  111. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  112. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  113. pdd/prompts/agentic_crash_explore_LLM.prompt +49 -0
  114. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  115. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  116. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  117. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  118. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  119. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  120. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  121. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  122. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  123. pdd/prompts/agentic_fix_explore_LLM.prompt +45 -0
  124. pdd/prompts/agentic_fix_harvest_only_LLM.prompt +48 -0
  125. pdd/prompts/agentic_fix_primary_LLM.prompt +85 -0
  126. pdd/prompts/agentic_update_LLM.prompt +925 -0
  127. pdd/prompts/agentic_verify_explore_LLM.prompt +45 -0
  128. pdd/prompts/auto_include_LLM.prompt +122 -905
  129. pdd/prompts/change_LLM.prompt +3093 -1
  130. pdd/prompts/detect_change_LLM.prompt +686 -27
  131. pdd/prompts/example_generator_LLM.prompt +22 -1
  132. pdd/prompts/extract_code_LLM.prompt +5 -1
  133. pdd/prompts/extract_program_code_fix_LLM.prompt +7 -1
  134. pdd/prompts/extract_prompt_update_LLM.prompt +7 -8
  135. pdd/prompts/extract_promptline_LLM.prompt +17 -11
  136. pdd/prompts/find_verification_errors_LLM.prompt +6 -0
  137. pdd/prompts/fix_code_module_errors_LLM.prompt +12 -2
  138. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +9 -0
  139. pdd/prompts/fix_verification_errors_LLM.prompt +22 -0
  140. pdd/prompts/generate_test_LLM.prompt +41 -7
  141. pdd/prompts/generate_test_from_example_LLM.prompt +115 -0
  142. pdd/prompts/increase_tests_LLM.prompt +1 -5
  143. pdd/prompts/insert_includes_LLM.prompt +316 -186
  144. pdd/prompts/prompt_code_diff_LLM.prompt +119 -0
  145. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  146. pdd/prompts/trace_LLM.prompt +25 -22
  147. pdd/prompts/unfinished_prompt_LLM.prompt +85 -1
  148. pdd/prompts/update_prompt_LLM.prompt +22 -1
  149. pdd/pytest_output.py +127 -12
  150. pdd/remote_session.py +876 -0
  151. pdd/render_mermaid.py +236 -0
  152. pdd/server/__init__.py +52 -0
  153. pdd/server/app.py +335 -0
  154. pdd/server/click_executor.py +587 -0
  155. pdd/server/executor.py +338 -0
  156. pdd/server/jobs.py +661 -0
  157. pdd/server/models.py +241 -0
  158. pdd/server/routes/__init__.py +31 -0
  159. pdd/server/routes/architecture.py +451 -0
  160. pdd/server/routes/auth.py +364 -0
  161. pdd/server/routes/commands.py +929 -0
  162. pdd/server/routes/config.py +42 -0
  163. pdd/server/routes/files.py +603 -0
  164. pdd/server/routes/prompts.py +1322 -0
  165. pdd/server/routes/websocket.py +473 -0
  166. pdd/server/security.py +243 -0
  167. pdd/server/terminal_spawner.py +209 -0
  168. pdd/server/token_counter.py +222 -0
  169. pdd/setup_tool.py +648 -0
  170. pdd/simple_math.py +2 -0
  171. pdd/split_main.py +3 -2
  172. pdd/summarize_directory.py +237 -195
  173. pdd/sync_animation.py +8 -4
  174. pdd/sync_determine_operation.py +839 -112
  175. pdd/sync_main.py +351 -57
  176. pdd/sync_orchestration.py +1400 -756
  177. pdd/sync_tui.py +848 -0
  178. pdd/template_expander.py +161 -0
  179. pdd/template_registry.py +264 -0
  180. pdd/templates/architecture/architecture_json.prompt +237 -0
  181. pdd/templates/generic/generate_prompt.prompt +174 -0
  182. pdd/trace.py +168 -12
  183. pdd/trace_main.py +4 -3
  184. pdd/track_cost.py +140 -63
  185. pdd/unfinished_prompt.py +51 -4
  186. pdd/update_main.py +567 -67
  187. pdd/update_model_costs.py +2 -2
  188. pdd/update_prompt.py +19 -4
  189. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.118.dist-info}/METADATA +29 -11
  190. pdd_cli-0.0.118.dist-info/RECORD +227 -0
  191. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.118.dist-info}/licenses/LICENSE +1 -1
  192. pdd_cli-0.0.45.dist-info/RECORD +0 -116
  193. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.118.dist-info}/WHEEL +0 -0
  194. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.118.dist-info}/entry_points.txt +0 -0
  195. {pdd_cli-0.0.45.dist-info → pdd_cli-0.0.118.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,215 @@
1
+ """
2
+ Templates command group.
3
+ """
4
+ import click
5
+ from rich import box
6
+ from rich.table import Table
7
+ from rich.text import Text
8
+ from rich.markup import escape
9
+ from typing import Optional, List, Tuple, Any
10
+
11
+ from .. import template_registry
12
+ from ..core.errors import handle_error, console
13
+
14
+ @click.group(name="templates")
15
+ def templates_group():
16
+ """Manage packaged and project templates."""
17
+ pass
18
+
19
+
20
+ @templates_group.command("list")
21
+ @click.option("--json", "as_json", is_flag=True, help="Output as JSON")
22
+ @click.option("--filter", "filter_tag", type=str, default=None, help="Filter by tag")
23
+ def templates_list(as_json: bool, filter_tag: Optional[str]):
24
+ try:
25
+ items = template_registry.list_templates(filter_tag)
26
+ if as_json:
27
+ import json as _json
28
+ click.echo(_json.dumps(items, indent=2))
29
+ else:
30
+ if not items:
31
+ console.print("[info]No templates found.[/info]")
32
+ return
33
+ console.print("[info]Available Templates:[/info]")
34
+ for it in items:
35
+ # Print the template name on its own line to avoid Rich wrapping
36
+ name_line = Text(f"- {it['name']}", style="bold", no_wrap=True)
37
+ console.print(name_line)
38
+ # Print details on the next line(s) with a small indent; wrapping is fine here
39
+ version = it.get("version", "")
40
+ description = it.get("description", "")
41
+ tags = ", ".join(it.get("tags", []))
42
+ details_parts = []
43
+ if version:
44
+ details_parts.append(f"({version})")
45
+ if description:
46
+ details_parts.append(description)
47
+ if tags:
48
+ details_parts.append(f"[{tags}]")
49
+ if details_parts:
50
+ console.print(" " + " — ".join(details_parts))
51
+ except Exception as e:
52
+ handle_error(e, "templates list", False)
53
+
54
+
55
+ @templates_group.command("show")
56
+ @click.argument("name", type=str)
57
+ def templates_show(name: str):
58
+ try:
59
+ data = template_registry.show_template(name)
60
+ summary = data.get("summary", {})
61
+
62
+ def _render_key_value_table(title: Optional[str], items: List[Tuple[str, Any]], *, highlight_path: bool = False):
63
+ """Render a 2-column Rich table for key/value pairs."""
64
+
65
+ table = Table(show_header=False, box=box.SIMPLE, expand=True)
66
+ table.add_column("Field", style="info", no_wrap=True)
67
+ table.add_column("Value", overflow="fold")
68
+
69
+ added_rows = False
70
+ for label, value in items:
71
+ if value in (None, "", [], {}):
72
+ continue
73
+ if isinstance(value, (list, tuple)):
74
+ value_str = ", ".join(str(v) for v in value)
75
+ else:
76
+ value_str = str(value)
77
+
78
+ if highlight_path and label.lower() == "path":
79
+ value_markup = f"[path]{escape(value_str)}[/path]"
80
+ else:
81
+ value_markup = escape(value_str)
82
+
83
+ table.add_row(label, value_markup)
84
+ added_rows = True
85
+
86
+ if added_rows:
87
+ if title:
88
+ console.print(f"[info]{title}[/info]")
89
+ console.print(table)
90
+
91
+ summary_items = [
92
+ ("Name", summary.get("name")),
93
+ ("Description", summary.get("description")),
94
+ ("Version", summary.get("version")),
95
+ ("Tags", summary.get("tags", [])),
96
+ ("Language", summary.get("language")),
97
+ ("Output", summary.get("output")),
98
+ ("Path", summary.get("path")),
99
+ ]
100
+ _render_key_value_table("Template Summary:", summary_items, highlight_path=True)
101
+
102
+ if data.get("variables"):
103
+ console.print("\n[info]Variables:[/info]")
104
+ variables_table = Table(box=box.SIMPLE_HEAD, show_lines=False, expand=True)
105
+ variables_table.add_column("Name", style="bold", no_wrap=True)
106
+ variables_table.add_column("Required", style="info", no_wrap=True)
107
+ variables_table.add_column("Type", no_wrap=True)
108
+ variables_table.add_column("Description", overflow="fold")
109
+ variables_table.add_column("Default/Examples", overflow="fold")
110
+
111
+ for var_name, var_meta in data["variables"].items():
112
+ required = var_meta.get("required")
113
+ if required is True:
114
+ required_str = "Yes"
115
+ elif required is False:
116
+ required_str = "No"
117
+ else:
118
+ required_str = "-"
119
+
120
+ var_type = escape(str(var_meta.get("type", "-")))
121
+ description = escape(str(var_meta.get("description", "")))
122
+
123
+ default_parts: List[str] = []
124
+ default_value = var_meta.get("default")
125
+ if default_value not in (None, ""):
126
+ default_parts.append(f"default: {default_value}")
127
+
128
+ examples_value = var_meta.get("examples")
129
+ if examples_value:
130
+ if isinstance(examples_value, (list, tuple)):
131
+ examples_str = ", ".join(str(example) for example in examples_value)
132
+ else:
133
+ examples_str = str(examples_value)
134
+ default_parts.append(f"examples: {examples_str}")
135
+
136
+ example_paths_value = var_meta.get("example_paths")
137
+ if example_paths_value:
138
+ if isinstance(example_paths_value, (list, tuple)):
139
+ example_paths_str = ", ".join(str(example) for example in example_paths_value)
140
+ else:
141
+ example_paths_str = str(example_paths_value)
142
+ default_parts.append(f"paths: {example_paths_str}")
143
+
144
+ default_examples = "\n".join(default_parts) if default_parts else "-"
145
+
146
+ variables_table.add_row(
147
+ escape(str(var_name)),
148
+ required_str,
149
+ var_type,
150
+ description,
151
+ escape(default_examples),
152
+ )
153
+
154
+ console.print(variables_table)
155
+
156
+ if data.get("usage"):
157
+ console.print("\n[info]Usage:[/info]")
158
+ usage = data["usage"]
159
+ if isinstance(usage, dict):
160
+ for group_name, entries in usage.items():
161
+ console.print(f"[bold]{escape(str(group_name))}[/bold]")
162
+ usage_table = Table(box=box.SIMPLE, show_lines=False, expand=True)
163
+ usage_table.add_column("Name", style="bold", no_wrap=True)
164
+ usage_table.add_column("Command", overflow="fold")
165
+
166
+ if isinstance(entries, (list, tuple)):
167
+ iterable_entries = entries
168
+ else:
169
+ iterable_entries = [entries]
170
+
171
+ for entry in iterable_entries:
172
+ if isinstance(entry, dict):
173
+ name_value = escape(str(entry.get("name", "")))
174
+ command_value = escape(str(entry.get("command", "")))
175
+ else:
176
+ name_value = "-"
177
+ command_value = escape(str(entry))
178
+ usage_table.add_row(name_value, f"[command]{command_value}[/command]")
179
+
180
+ if usage_table.row_count:
181
+ console.print(usage_table)
182
+ else:
183
+ console.print(usage)
184
+
185
+ if data.get("discover"):
186
+ console.print("\n[info]Discover:[/info]")
187
+ discover = data["discover"]
188
+ if isinstance(discover, dict):
189
+ discover_items = [(str(key), value) for key, value in discover.items()]
190
+ _render_key_value_table(None, discover_items)
191
+ else:
192
+ console.print(discover)
193
+ if data.get("output_schema"):
194
+ console.print("\n[info]Output Schema:[/info]")
195
+ try:
196
+ import json as _json
197
+ console.print(_json.dumps(data["output_schema"], indent=2))
198
+ except Exception:
199
+ console.print(str(data["output_schema"]))
200
+ if data.get("notes"):
201
+ console.print("\n[info]Notes:[/info]")
202
+ console.print(data["notes"]) # plain text
203
+ except Exception as e:
204
+ handle_error(e, "templates show", False)
205
+
206
+
207
+ @templates_group.command("copy")
208
+ @click.argument("name", type=str)
209
+ @click.option("--to", "dest_dir", type=click.Path(file_okay=False), required=True)
210
+ def templates_copy(name: str, dest_dir: str):
211
+ try:
212
+ dest = template_registry.copy_template(name, dest_dir)
213
+ console.print(f"[success]Copied to:[/success] {dest}")
214
+ except Exception as e:
215
+ handle_error(e, "templates copy", False)
@@ -0,0 +1,110 @@
1
+ """
2
+ Utility commands (install_completion, verify/fix-verification).
3
+ """
4
+ import click
5
+ from typing import Optional, Tuple
6
+
7
+ from ..fix_verification_main import fix_verification_main
8
+ from ..track_cost import track_cost
9
+ from ..core.errors import handle_error
10
+
11
+ @click.command("install_completion")
12
+ @click.pass_context
13
+ def install_completion_cmd(ctx: click.Context):
14
+ """Install shell completion for the PDD CLI."""
15
+ try:
16
+ from .. import cli as cli_module # Import parent module for proper patching
17
+ quiet = ctx.obj.get("quiet", False)
18
+ # Call through cli_module so patches to pdd.cli.install_completion work
19
+ cli_module.install_completion(quiet=quiet)
20
+ except Exception as e:
21
+ handle_error(e, "install_completion", ctx.obj.get("quiet", False))
22
+
23
+
24
+ @click.command("verify")
25
+ @click.argument("prompt_file", type=click.Path(exists=True, dir_okay=False))
26
+ @click.argument("code_file", type=click.Path(exists=True, dir_okay=False))
27
+ @click.argument("verification_program", type=click.Path(exists=True, dir_okay=False))
28
+ @click.option(
29
+ "--output-code",
30
+ type=click.Path(writable=True),
31
+ default=None,
32
+ help="Specify where to save the verified code file (file or directory).",
33
+ )
34
+ @click.option(
35
+ "--output-program",
36
+ type=click.Path(writable=True),
37
+ default=None,
38
+ help="Specify where to save the verified program file (file or directory).",
39
+ )
40
+ @click.option(
41
+ "--output-results",
42
+ type=click.Path(writable=True),
43
+ default=None,
44
+ help="Specify where to save the results log (file or directory).",
45
+ )
46
+ @click.option(
47
+ "--max-attempts",
48
+ type=int,
49
+ default=3,
50
+ show_default=True,
51
+ help="Maximum number of verification attempts.",
52
+ )
53
+ @click.option(
54
+ "--budget",
55
+ type=float,
56
+ default=5.0,
57
+ show_default=True,
58
+ help="Maximum cost allowed for the verification process.",
59
+ )
60
+ @click.option(
61
+ "--agentic-fallback/--no-agentic-fallback",
62
+ is_flag=True,
63
+ default=True,
64
+ help="Enable agentic fallback if the primary fix mechanism fails.",
65
+ )
66
+ @click.pass_context
67
+ @track_cost
68
+ def verify(
69
+ ctx: click.Context,
70
+ prompt_file: str,
71
+ code_file: str,
72
+ verification_program: str,
73
+ output_code: Optional[str],
74
+ output_program: Optional[str],
75
+ output_results: Optional[str],
76
+ max_attempts: int,
77
+ budget: float,
78
+ agentic_fallback: bool,
79
+ ) -> Optional[Tuple]:
80
+ """Verify code using a verification program."""
81
+ try:
82
+ # verify command implies a loop if max_attempts > 1, but let's enable loop by default
83
+ # as it's the more powerful mode and matches the CLI args provided (max_attempts).
84
+ # verification_program positional arg acts as both program_file (to run) and verification_program (reference)
85
+ success, prog_code, code_content, attempts, total_cost, model_name = fix_verification_main(
86
+ ctx=ctx,
87
+ prompt_file=prompt_file,
88
+ code_file=code_file,
89
+ program_file=verification_program,
90
+ output_code=output_code,
91
+ output_program=output_program,
92
+ output_results=output_results,
93
+ loop=True,
94
+ verification_program=verification_program,
95
+ max_attempts=max_attempts,
96
+ budget=budget,
97
+ agentic_fallback=agentic_fallback,
98
+ )
99
+ result = {
100
+ "success": success,
101
+ "program_code": prog_code,
102
+ "code_content": code_content,
103
+ "attempts": attempts,
104
+ }
105
+ return result, total_cost, model_name
106
+ except click.Abort:
107
+ raise
108
+ except Exception as exception:
109
+ handle_error(exception, "verify", ctx.obj.get("quiet", False))
110
+ return None
@@ -0,0 +1,58 @@
1
+ """
2
+ Centralized config resolution for all commands.
3
+
4
+ Single source of truth for resolving strength, temperature, and other config values.
5
+ This module ensures consistent priority ordering across all commands:
6
+ 1. CLI global options (--strength, --temperature) - highest priority
7
+ 2. pddrc context defaults - medium priority
8
+ 3. Hardcoded defaults - lowest priority
9
+ """
10
+ from typing import Dict, Any, Optional
11
+ import click
12
+
13
+ from . import DEFAULT_STRENGTH, DEFAULT_TEMPERATURE, DEFAULT_TIME
14
+
15
+
16
+ def resolve_effective_config(
17
+ ctx: click.Context,
18
+ resolved_config: Dict[str, Any],
19
+ param_overrides: Optional[Dict[str, Any]] = None
20
+ ) -> Dict[str, Any]:
21
+ """
22
+ Resolve effective config values with proper priority.
23
+
24
+ Priority (highest to lowest):
25
+ 1. Command parameter overrides (e.g., strength kwarg)
26
+ 2. CLI global options (--strength stored in ctx.obj)
27
+ 3. pddrc context defaults (from resolved_config)
28
+ 4. Hardcoded defaults
29
+
30
+ Args:
31
+ ctx: Click context with CLI options in ctx.obj
32
+ resolved_config: Config returned by construct_paths (contains pddrc values)
33
+ param_overrides: Optional command-specific parameter overrides
34
+
35
+ Returns:
36
+ Dict with resolved values for strength, temperature, time
37
+ """
38
+ ctx_obj = ctx.obj if ctx.obj else {}
39
+ param_overrides = param_overrides or {}
40
+
41
+ def resolve_value(key: str, default: Any) -> Any:
42
+ # Priority 1: Command parameter override
43
+ if key in param_overrides and param_overrides[key] is not None:
44
+ return param_overrides[key]
45
+ # Priority 2: CLI global option (only if key IS in ctx.obj - meaning CLI passed it)
46
+ if key in ctx_obj:
47
+ return ctx_obj[key]
48
+ # Priority 3: pddrc context default
49
+ if key in resolved_config and resolved_config[key] is not None:
50
+ return resolved_config[key]
51
+ # Priority 4: Hardcoded default
52
+ return default
53
+
54
+ return {
55
+ "strength": resolve_value("strength", DEFAULT_STRENGTH),
56
+ "temperature": resolve_value("temperature", DEFAULT_TEMPERATURE),
57
+ "time": resolve_value("time", DEFAULT_TIME),
58
+ }
pdd/conflicts_main.py CHANGED
@@ -8,7 +8,7 @@ from .construct_paths import construct_paths
8
8
  from .conflicts_in_prompts import conflicts_in_prompts
9
9
  from . import DEFAULT_TIME, DEFAULT_STRENGTH
10
10
 
11
- def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optional[str], verbose: bool = False) -> Tuple[List[Dict], float, str]:
11
+ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optional[str], verbose: bool = False, language: Optional[str] = None) -> Tuple[List[Dict], float, str]:
12
12
  """
13
13
  Main function to analyze conflicts between two prompts.
14
14
 
@@ -17,6 +17,7 @@ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optio
17
17
  :param prompt2: Path to the second prompt file.
18
18
  :param output: Optional path to save the output CSV file.
19
19
  :param verbose: Optional parameter to control verbosity (default: False).
20
+ :param language: Optional language hint for file processing.
20
21
  :return: A tuple containing the list of conflicts, total cost, and model name used.
21
22
  """
22
23
  try:
@@ -28,12 +29,16 @@ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optio
28
29
  command_options = {
29
30
  "output": output
30
31
  }
32
+ if language:
33
+ command_options["language"] = language
34
+
31
35
  resolved_config, input_strings, output_file_paths, _ = construct_paths(
32
36
  input_file_paths=input_file_paths,
33
37
  force=ctx.obj.get('force', False),
34
38
  quiet=ctx.obj.get('quiet', False),
35
39
  command="conflicts",
36
- command_options=command_options
40
+ command_options=command_options,
41
+ context_override=ctx.obj.get('context')
37
42
  )
38
43
 
39
44
  # Load input files
@@ -91,4 +96,4 @@ def conflicts_main(ctx: click.Context, prompt1: str, prompt2: str, output: Optio
91
96
  except Exception as e:
92
97
  if not ctx.obj.get('quiet', False):
93
98
  rprint(f"[bold red]Error:[/bold red] {str(e)}")
94
- sys.exit(1)
99
+ sys.exit(1)