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
@@ -1,8 +1,7 @@
1
1
  from pathlib import Path
2
- import os
3
2
  from typing import Optional
4
- import sys
5
3
  from rich import print
4
+ from pdd.path_resolution import get_default_resolver
6
5
 
7
6
  def print_formatted(message: str) -> None:
8
7
  """Print message with raw formatting tags for testing compatibility."""
@@ -23,18 +22,26 @@ def load_prompt_template(prompt_name: str) -> Optional[str]:
23
22
  print_formatted("[red]Unexpected error loading prompt template[/red]")
24
23
  return None
25
24
 
26
- # Step 1: Get project path from environment variable
27
- project_path = os.getenv('PDD_PATH')
28
- if not project_path:
29
- print_formatted("[red]PDD_PATH environment variable is not set[/red]")
30
- return None
25
+ resolver = get_default_resolver()
26
+ prompt_path = resolver.resolve_prompt_template(prompt_name)
27
+
28
+ if prompt_path is None:
29
+ candidate_roots = []
30
+ if resolver.pdd_path_env is not None:
31
+ candidate_roots.append(resolver.pdd_path_env)
32
+ if resolver.repo_root is not None:
33
+ candidate_roots.append(resolver.repo_root)
34
+ candidate_roots.append(resolver.cwd)
31
35
 
32
- # Construct the full path to the prompt file
33
- prompt_path = Path(project_path) / 'prompts' / f"{prompt_name}.prompt"
36
+ prompt_candidates = []
37
+ for root in candidate_roots:
38
+ prompt_candidates.append(root / 'prompts' / f"{prompt_name}.prompt")
39
+ prompt_candidates.append(root / 'pdd' / 'prompts' / f"{prompt_name}.prompt")
34
40
 
35
- # Step 2: Load and return the prompt template
36
- if not prompt_path.exists():
37
- print_formatted(f"[red]Prompt file not found: {prompt_path}[/red]")
41
+ tried = "\n".join(str(c) for c in prompt_candidates)
42
+ print_formatted(
43
+ f"[red]Prompt file not found in any candidate locations for '{prompt_name}'. Tried:\n{tried}[/red]"
44
+ )
38
45
  return None
39
46
 
40
47
  try:
pdd/path_resolution.py ADDED
@@ -0,0 +1,140 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Literal, Optional
7
+
8
+ IncludeProfile = Literal["cwd_then_package_then_repo"]
9
+ PromptProfile = Literal["pdd_path_then_repo_then_cwd"]
10
+ DataProfile = Literal["pdd_path_only"]
11
+ ProjectRootProfile = Literal["pdd_path_then_marker_then_cwd"]
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class PathResolver:
16
+ cwd: Path
17
+ pdd_path_env: Optional[Path]
18
+ package_root: Path
19
+ repo_root: Optional[Path]
20
+
21
+ def resolve_include(self, rel: str, profile: IncludeProfile = "cwd_then_package_then_repo") -> Path:
22
+ if profile != "cwd_then_package_then_repo":
23
+ raise ValueError(f"Unsupported include profile: {profile}")
24
+
25
+ cwd_path = self.cwd / rel
26
+ if cwd_path.exists():
27
+ return cwd_path
28
+
29
+ pkg_path = self.package_root / rel
30
+ if pkg_path.exists():
31
+ return pkg_path
32
+
33
+ if self.repo_root is not None:
34
+ repo_path = self.repo_root / rel
35
+ if repo_path.exists():
36
+ return repo_path
37
+
38
+ return cwd_path
39
+
40
+ def resolve_prompt_template(
41
+ self,
42
+ name: str,
43
+ profile: PromptProfile = "pdd_path_then_repo_then_cwd",
44
+ ) -> Optional[Path]:
45
+ if profile != "pdd_path_then_repo_then_cwd":
46
+ raise ValueError(f"Unsupported prompt profile: {profile}")
47
+
48
+ roots = []
49
+ if self.pdd_path_env is not None:
50
+ roots.append(self.pdd_path_env)
51
+ if self.repo_root is not None:
52
+ roots.append(self.repo_root)
53
+ roots.append(self.cwd)
54
+
55
+ prompt_file = f"{name}.prompt"
56
+ for root in roots:
57
+ candidate = root / "prompts" / prompt_file
58
+ if candidate.exists():
59
+ return candidate
60
+ candidate = root / "pdd" / "prompts" / prompt_file
61
+ if candidate.exists():
62
+ return candidate
63
+
64
+ return None
65
+
66
+ def resolve_data_file(self, rel: str, profile: DataProfile = "pdd_path_only") -> Path:
67
+ if profile != "pdd_path_only":
68
+ raise ValueError(f"Unsupported data profile: {profile}")
69
+ if self.pdd_path_env is None:
70
+ raise ValueError("PDD_PATH environment variable is not set.")
71
+ return self.pdd_path_env / rel
72
+
73
+ def resolve_project_root(
74
+ self,
75
+ profile: ProjectRootProfile = "pdd_path_then_marker_then_cwd",
76
+ max_levels: int = 5,
77
+ ) -> Path:
78
+ if profile != "pdd_path_then_marker_then_cwd":
79
+ raise ValueError(f"Unsupported project root profile: {profile}")
80
+
81
+ if (
82
+ self.pdd_path_env is not None
83
+ and self.pdd_path_env.is_dir()
84
+ and not _is_within(self.pdd_path_env, self.package_root)
85
+ ):
86
+ return self.pdd_path_env
87
+
88
+ current = self.cwd
89
+ for _ in range(max_levels):
90
+ if _has_project_marker(current):
91
+ return current
92
+ parent = current.parent
93
+ if parent == current:
94
+ break
95
+ current = parent
96
+
97
+ return self.cwd
98
+
99
+
100
+ def get_default_resolver() -> PathResolver:
101
+ cwd = Path.cwd().resolve()
102
+
103
+ pdd_path_env = None
104
+ env_value = os.getenv("PDD_PATH")
105
+ if env_value:
106
+ pdd_path_env = Path(env_value).expanduser().resolve()
107
+
108
+ package_root = Path(__file__).resolve().parent
109
+ repo_root = package_root.parent
110
+
111
+ return PathResolver(
112
+ cwd=cwd,
113
+ pdd_path_env=pdd_path_env,
114
+ package_root=package_root,
115
+ repo_root=repo_root,
116
+ )
117
+
118
+
119
+ def _has_project_marker(path: Path) -> bool:
120
+ return (
121
+ (path / ".git").exists()
122
+ or (path / "pyproject.toml").exists()
123
+ or (path / "data").is_dir()
124
+ or (path / ".env").exists()
125
+ )
126
+
127
+
128
+ def _is_within(path: Path, parent: Path) -> bool:
129
+ try:
130
+ resolved_path = path.resolve()
131
+ resolved_parent = parent.resolve()
132
+ except Exception:
133
+ return False
134
+
135
+ if resolved_path == resolved_parent:
136
+ return True
137
+ parent_str = str(resolved_parent)
138
+ if not parent_str.endswith(os.sep):
139
+ parent_str = parent_str + os.sep
140
+ return str(resolved_path).startswith(parent_str)
pdd/pdd_completion.fish CHANGED
@@ -10,6 +10,8 @@ complete -c pdd -n "__fish_use_subcommand" -l quiet -d "Decrease output verbosit
10
10
  complete -c pdd -n "__fish_use_subcommand" -l output-cost -r -d "Enable cost tracking and output CSV file"
11
11
  complete -c pdd -n "__fish_use_subcommand" -l review-examples -d "Review few-shot examples before execution"
12
12
  complete -c pdd -n "__fish_use_subcommand" -l local -d "Run commands locally"
13
+ complete -c pdd -n "__fish_use_subcommand" -l context -r -d "Override .pddrc context"
14
+ complete -c pdd -n "__fish_use_subcommand" -l list-contexts -d ".pddrc contexts and exit"
13
15
  complete -c pdd -n "__fish_use_subcommand" -l help -d "Show help message"
14
16
  complete -c pdd -n "__fish_use_subcommand" -l version -d "Show version information"
15
17
 
@@ -27,13 +29,18 @@ complete -c pdd -n "__fish_use_subcommand" -a conflicts -d "Analyze conflicts be
27
29
  complete -c pdd -n "__fish_use_subcommand" -a crash -d "Fix code causing program crash"
28
30
  complete -c pdd -n "__fish_use_subcommand" -a trace -d "Trace code line to prompt"
29
31
  complete -c pdd -n "__fish_use_subcommand" -a bug -d "Generate unit test from bug report"
30
- complete -c pdd -n "__fish_use_subcommand" -a auto-deps -d "Analyze and insert dependencies"
32
+ complete -c pdd -n "__fish_use_subcommand" -a auto-deps -d "Analyze and insert dependencies from directory or glob"
31
33
  complete -c pdd -n "__fish_use_subcommand" -a verify -d "Verify functional correctness using LLM judgment"
34
+ complete -c pdd -n "__fish_use_subcommand" -a sync -d "Synchronize prompt, code, examples, tests"
35
+ complete -c pdd -n "__fish_use_subcommand" -a setup -d "Interactive setup and completion install"
36
+ complete -c pdd -n "__fish_use_subcommand" -a install_completion -d "Install shell completion"
37
+ complete -c pdd -n "__fish_use_subcommand" -a pytest-output -d "Run pytest and capture structured output"
32
38
 
33
39
  # Command-specific completions
34
40
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l output -r -d "Output location for generated code"
35
41
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l original-prompt -r -d "Original prompt file for incremental generation"
36
42
  complete -c pdd -n "__fish_seen_subcommand_from generate" -l incremental -d "Force incremental patching"
43
+ 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
44
  complete -c pdd -n "__fish_seen_subcommand_from generate" -a "(__fish_complete_suffix .prompt)"
38
45
 
39
46
  complete -c pdd -n "__fish_seen_subcommand_from example" -l output -r -d "Output location for example code"
@@ -125,10 +132,26 @@ complete -c pdd -n "__fish_seen_subcommand_from verify" -l budget -x -d "Max bud
125
132
  complete -c pdd -n "__fish_seen_subcommand_from verify" -a "(__fish_complete_suffix .prompt)"
126
133
  complete -c pdd -n "__fish_seen_subcommand_from verify" -a "(__fish_complete_suffix .py .js .java .cpp .rb .go)" # For CODE_FILE and PROGRAM_FILE
127
134
 
135
+ # sync command
136
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l max-attempts -x -d "Max attempts for loops"
137
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l budget -x -d "Total budget for sync"
138
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l skip-verify -d "Skip functional verification"
139
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l skip-tests -d "Skip unit test generation"
140
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l target-coverage -x -d "Desired coverage percentage"
141
+ complete -c pdd -n "__fish_seen_subcommand_from sync" -l log -d "Show analysis instead of running"
142
+
143
+ # setup and install_completion have no options
144
+ complete -c pdd -n "__fish_seen_subcommand_from setup" -d "Run interactive setup"
145
+ complete -c pdd -n "__fish_seen_subcommand_from install_completion" -d "Install shell completion"
146
+
147
+ # pytest-output command
148
+ complete -c pdd -n "__fish_seen_subcommand_from pytest-output" -l json-only -d "Print only JSON"
149
+ complete -c pdd -n "__fish_seen_subcommand_from pytest-output" -a "(__fish_complete_suffix .py)"
150
+
128
151
  # File completion for all commands
129
152
  complete -c pdd -n "__fish_seen_subcommand_from generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify" -a "(__fish_complete_suffix .prompt)"
130
153
  complete -c pdd -n "__fish_seen_subcommand_from generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify" -a "(__fish_complete_suffix .py .js .java .cpp .rb .go)"
131
154
  complete -c pdd -n "__fish_seen_subcommand_from generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify" -a "(__fish_complete_suffix .log .txt .csv)"
132
155
 
133
156
  # Help completion
134
- complete -c pdd -n "__fish_seen_subcommand_from help" -a "generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify" -d "Show help for specific command"
157
+ complete -c pdd -n "__fish_seen_subcommand_from help" -a "generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify sync setup install_completion pytest-output" -d "Show help for specific command"
pdd/pdd_completion.sh CHANGED
@@ -15,13 +15,13 @@ _pdd() {
15
15
  cword=$COMP_CWORD
16
16
 
17
17
  # Global options
18
- local global_opts="--force --strength --time --temperature --verbose --quiet --output-cost --review-examples --local --help --version"
18
+ local global_opts="--force --strength --time --temperature --verbose --quiet --output-cost --review-examples --local --context --list-contexts --help --version"
19
19
 
20
20
  # Commands
21
- local commands="generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify"
21
+ local commands="generate example test preprocess fix split change update detect conflicts crash trace bug auto-deps verify sync setup install_completion pytest-output"
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"
@@ -36,6 +36,8 @@ _pdd() {
36
36
  local bug_opts="--output --language"
37
37
  local auto_deps_opts="--output --csv --force-scan"
38
38
  local verify_opts="--output-results --output-code --output-program --max-attempts --budget"
39
+ local sync_opts="--max-attempts --budget --skip-verify --skip-tests --target-coverage --log"
40
+ local pytest_output_opts="--json-only"
39
41
 
40
42
  # Complete global options before command
41
43
  if [[ $cword -eq 1 ]]; then
@@ -46,6 +48,16 @@ _pdd() {
46
48
  # Complete command-specific options
47
49
  case ${words[1]} in
48
50
  generate)
51
+ # If completing the value for -e/--env, suggest environment variable names (with and without '=')
52
+ if [[ $prev == "-e" || $prev == "--env" ]]; then
53
+ local vars
54
+ vars=$(env | cut -d= -f1 | sort -u)
55
+ # Offer both KEY and KEY=
56
+ local vars_with_eq
57
+ vars_with_eq=$(printf '%s=\n' $vars)
58
+ COMPREPLY=($(compgen -W "$vars $vars_with_eq" -- "$cur"))
59
+ return
60
+ fi
49
61
  _complete_files ".prompt"
50
62
  COMPREPLY+=($(compgen -W "$generate_opts" -- "$cur"))
51
63
  ;;
@@ -131,6 +143,20 @@ _pdd() {
131
143
  _complete_files
132
144
  COMPREPLY+=($(compgen -W "$verify_opts" -- "$cur"))
133
145
  ;;
146
+ sync)
147
+ # BASENAME (not a file), offer options
148
+ COMPREPLY+=($(compgen -W "$sync_opts" -- "$cur"))
149
+ ;;
150
+ setup)
151
+ # no command-specific options
152
+ ;;
153
+ install_completion)
154
+ # no command-specific options
155
+ ;;
156
+ pytest-output)
157
+ _complete_files
158
+ COMPREPLY+=($(compgen -W "$pytest_output_opts" -- "$cur"))
159
+ ;;
134
160
  *)
135
161
  COMPREPLY=($(compgen -W "$global_opts" -- "$cur"))
136
162
  ;;
@@ -157,4 +183,4 @@ _complete_files() {
157
183
  fi
158
184
  }
159
185
 
160
- complete -F _pdd pdd
186
+ complete -F _pdd pdd
pdd/pdd_completion.zsh CHANGED
@@ -57,6 +57,8 @@ _pdd_global_opts=(
57
57
  '--output-cost[Enable cost tracking and output a CSV file with usage details.]:filename:_files'
58
58
  '--review-examples[Review and optionally exclude few-shot examples before command execution.]'
59
59
  '--local[Run commands locally instead of in the cloud.]'
60
+ '--context[Override automatic .pddrc context]:context-name:_guard'
61
+ '--list-contexts[List available .pddrc contexts and exit]'
60
62
  '--help[Show help message and exit.]'
61
63
  '--version[Show version and exit.]'
62
64
  )
@@ -65,6 +67,14 @@ _pdd_global_opts=(
65
67
  # Per-subcommand completion functions
66
68
  ##
67
69
 
70
+ # Helper: suggest environment variables (KEY and KEY=)
71
+ _pdd_env_vars() {
72
+ local -a envs envs_eq
73
+ envs=(${(f)"$(env | cut -d= -f1 | sort -u)"})
74
+ envs_eq=(${envs/%/=})
75
+ _describe -t envvars 'environment variables' envs_eq envs
76
+ }
77
+
68
78
  # generate
69
79
  # Usage: pdd [GLOBAL OPTIONS] generate [OPTIONS] PROMPT_FILE
70
80
  # Options:
@@ -77,6 +87,7 @@ _pdd_generate() {
77
87
  '--output=[Specify where to save the generated code.]:filename:_files' \
78
88
  '--original-prompt=[The original prompt file used to generate existing code.]:filename:_files' \
79
89
  '--incremental[Force incremental patching even if changes are significant.]' \
90
+ '(-e --env)'{-e,--env}'[Set template variable (KEY=VALUE) or read KEY from env]:template variable:_pdd_env_vars' \
80
91
  '1:prompt-file:_files' \
81
92
  '*:filename:_files'
82
93
  }
@@ -349,7 +360,7 @@ _pdd_bug() {
349
360
  # --force-scan
350
361
  # Args:
351
362
  # 1: PROMPT_FILE
352
- # 2: DIRECTORY_PATH
363
+ # 2: DIRECTORY_PATH (directory or glob pattern)
353
364
  _pdd_auto_deps() {
354
365
  _arguments -s \
355
366
  $_pdd_global_opts \
@@ -357,7 +368,7 @@ _pdd_auto_deps() {
357
368
  '--csv=[CSV file for dependency info (default: project_dependencies.csv).]:filename:_files' \
358
369
  '--force-scan[Force rescanning of all potential dependency files.]' \
359
370
  '1:prompt-file:_files' \
360
- '2:directory:_files -/' \
371
+ '2:directory-or-glob:_files -/' \
361
372
  '*:filename:_files'
362
373
  }
363
374
 
@@ -387,6 +398,54 @@ _pdd_verify() {
387
398
  '*:filename:_files'
388
399
  }
389
400
 
401
+ # sync
402
+ # Usage: pdd [GLOBAL OPTIONS] sync [OPTIONS] BASENAME
403
+ # Options:
404
+ # --max-attempts [INT]
405
+ # --budget [FLOAT]
406
+ # --skip-verify
407
+ # --skip-tests
408
+ # --target-coverage [FLOAT]
409
+ # --log
410
+ # Arg:
411
+ # 1: BASENAME
412
+ _pdd_sync() {
413
+ _arguments -s \
414
+ $_pdd_global_opts \
415
+ '--max-attempts=[Maximum attempts for iterative loops (default 3)]:int' \
416
+ '--budget=[Maximum total cost for sync (default 10.0)]:float' \
417
+ '--skip-verify[Skip the functional verification step]' \
418
+ '--skip-tests[Skip unit test generation and fixing]' \
419
+ '--target-coverage=[Desired code coverage percentage]:float' \
420
+ '--log[Show analysis instead of executing operations]' \
421
+ '1:basename: ' \
422
+ '*: :'
423
+ }
424
+
425
+ # setup (no options)
426
+ _pdd_setup() {
427
+ _arguments -s $_pdd_global_opts
428
+ }
429
+
430
+ # install_completion (no options)
431
+ _pdd_install_completion() {
432
+ _arguments -s $_pdd_global_opts
433
+ }
434
+
435
+ # pytest-output
436
+ # Usage: pdd [GLOBAL OPTIONS] pytest-output [OPTIONS] TEST_FILE
437
+ # Options:
438
+ # --json-only
439
+ # Arg:
440
+ # 1: TEST_FILE
441
+ _pdd_pytest_output() {
442
+ _arguments -s \
443
+ $_pdd_global_opts \
444
+ '--json-only[Print only JSON to stdout]' \
445
+ '1:test-file:_files' \
446
+ '*:filename:_files'
447
+ }
448
+
390
449
  ##
391
450
  # Main PDD completion dispatcher
392
451
  ##
@@ -410,8 +469,12 @@ _pdd() {
410
469
  'crash:Fix errors in a code module and its calling program'
411
470
  'trace:Find the prompt file line number associated with a code line'
412
471
  'bug:Generate a unit test based on incorrect vs desired outputs'
413
- 'auto-deps:Analyze a prompt file and directory for needed dependencies'
472
+ 'auto-deps:Analyze a prompt and include deps from a directory or glob'
414
473
  'verify:Verify functional correctness using LLM judgment and iteratively fix'
474
+ 'sync:Synchronize prompt, code, examples, tests with analysis'
475
+ 'setup:Interactive setup and completion install'
476
+ 'install_completion:Install shell completion for current shell'
477
+ 'pytest-output:Run pytest and capture structured output'
415
478
  )
416
479
 
417
480
  # If there's no subcommand yet (i.e., user typed only "pdd " or "pdd -<Tab>"), offer global opts or subcommands.
@@ -471,6 +534,18 @@ _pdd() {
471
534
  verify)
472
535
  _pdd_verify
473
536
  ;;
537
+ sync)
538
+ _pdd_sync
539
+ ;;
540
+ setup)
541
+ _pdd_setup
542
+ ;;
543
+ install_completion)
544
+ _pdd_install_completion
545
+ ;;
546
+ pytest-output)
547
+ _pdd_pytest_output
548
+ ;;
474
549
  # If the subcommand is unknown or not typed yet, fall back to showing the list of subcommands.
475
550
  *)
476
551
  _describe -t subcommands 'pdd subcommand' _pdd_subcommands
@@ -487,4 +562,4 @@ else
487
562
  echo >&2 "Warning: Could not register pdd completion. Make sure ZSH completion system is working."
488
563
  fi
489
564
 
490
- # End of pdd_completion.zsh
565
+ # End of pdd_completion.zsh
pdd/postprocess.py CHANGED
@@ -3,10 +3,12 @@ from rich import print
3
3
  from pydantic import BaseModel, Field
4
4
  from .load_prompt_template import load_prompt_template
5
5
  from .llm_invoke import llm_invoke
6
- from . import DEFAULT_TIME
6
+ from . import DEFAULT_TIME, DEFAULT_STRENGTH
7
7
 
8
8
  class ExtractedCode(BaseModel):
9
9
  """Pydantic model for the extracted code."""
10
+ focus: str = Field(default="", description="The focus of the generation")
11
+ explanation: str = Field(default="", description="Explanation of the extraction")
10
12
  extracted_code: str = Field(description="The extracted code from the LLM output")
11
13
 
12
14
  def postprocess_0(text: str) -> str:
@@ -36,7 +38,7 @@ def postprocess_0(text: str) -> str:
36
38
  def postprocess(
37
39
  llm_output: str,
38
40
  language: str,
39
- strength: float = 0.9,
41
+ strength: float = DEFAULT_STRENGTH,
40
42
  temperature: float = 0,
41
43
  time: float = DEFAULT_TIME,
42
44
  verbose: bool = False
@@ -93,13 +95,21 @@ def postprocess(
93
95
  temperature=temperature,
94
96
  time=time,
95
97
  verbose=verbose,
96
- output_pydantic=ExtractedCode
98
+ output_pydantic=ExtractedCode,
99
+ language=language,
97
100
  )
98
101
 
99
102
  if not response or 'result' not in response:
100
103
  raise ValueError("Failed to get valid response from LLM")
101
104
 
102
- extracted_code_obj: ExtractedCode = response['result'] # Renamed for clarity
105
+ result_obj = response['result']
106
+ if not isinstance(result_obj, ExtractedCode):
107
+ # If we got a string (likely an error message from llm_invoke), fallback to simple extraction
108
+ if verbose:
109
+ print(f"[yellow]Structured extraction failed ({result_obj}). Falling back to simple extraction.[/yellow]")
110
+ return (postprocess_0(llm_output), response.get('cost', 0.0), response.get('model_name', 'fallback'))
111
+
112
+ extracted_code_obj: ExtractedCode = result_obj
103
113
  code_text = extracted_code_obj.extracted_code
104
114
 
105
115
  # Step 3c: Remove triple backticks and language identifier if present