pdd-cli 0.0.90__py3-none-any.whl → 0.0.121__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 (151) hide show
  1. pdd/__init__.py +38 -6
  2. pdd/agentic_bug.py +323 -0
  3. pdd/agentic_bug_orchestrator.py +506 -0
  4. pdd/agentic_change.py +231 -0
  5. pdd/agentic_change_orchestrator.py +537 -0
  6. pdd/agentic_common.py +533 -770
  7. pdd/agentic_crash.py +2 -1
  8. pdd/agentic_e2e_fix.py +319 -0
  9. pdd/agentic_e2e_fix_orchestrator.py +582 -0
  10. pdd/agentic_fix.py +118 -3
  11. pdd/agentic_update.py +27 -9
  12. pdd/agentic_verify.py +3 -2
  13. pdd/architecture_sync.py +565 -0
  14. pdd/auth_service.py +210 -0
  15. pdd/auto_deps_main.py +63 -53
  16. pdd/auto_include.py +236 -3
  17. pdd/auto_update.py +125 -47
  18. pdd/bug_main.py +195 -23
  19. pdd/cmd_test_main.py +345 -197
  20. pdd/code_generator.py +4 -2
  21. pdd/code_generator_main.py +118 -32
  22. pdd/commands/__init__.py +6 -0
  23. pdd/commands/analysis.py +113 -48
  24. pdd/commands/auth.py +309 -0
  25. pdd/commands/connect.py +358 -0
  26. pdd/commands/fix.py +155 -114
  27. pdd/commands/generate.py +5 -0
  28. pdd/commands/maintenance.py +3 -2
  29. pdd/commands/misc.py +8 -0
  30. pdd/commands/modify.py +225 -163
  31. pdd/commands/sessions.py +284 -0
  32. pdd/commands/utility.py +12 -7
  33. pdd/construct_paths.py +334 -32
  34. pdd/context_generator_main.py +167 -170
  35. pdd/continue_generation.py +6 -3
  36. pdd/core/__init__.py +33 -0
  37. pdd/core/cli.py +44 -7
  38. pdd/core/cloud.py +237 -0
  39. pdd/core/dump.py +68 -20
  40. pdd/core/errors.py +4 -0
  41. pdd/core/remote_session.py +61 -0
  42. pdd/crash_main.py +219 -23
  43. pdd/data/llm_model.csv +4 -4
  44. pdd/docs/prompting_guide.md +864 -0
  45. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  46. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  47. pdd/fix_code_loop.py +208 -34
  48. pdd/fix_code_module_errors.py +6 -2
  49. pdd/fix_error_loop.py +291 -38
  50. pdd/fix_main.py +208 -6
  51. pdd/fix_verification_errors_loop.py +235 -26
  52. pdd/fix_verification_main.py +269 -83
  53. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  54. pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
  55. pdd/frontend/dist/index.html +376 -0
  56. pdd/frontend/dist/logo.svg +33 -0
  57. pdd/generate_output_paths.py +46 -5
  58. pdd/generate_test.py +212 -151
  59. pdd/get_comment.py +19 -44
  60. pdd/get_extension.py +8 -9
  61. pdd/get_jwt_token.py +309 -20
  62. pdd/get_language.py +8 -7
  63. pdd/get_run_command.py +7 -5
  64. pdd/insert_includes.py +2 -1
  65. pdd/llm_invoke.py +531 -97
  66. pdd/load_prompt_template.py +15 -34
  67. pdd/operation_log.py +342 -0
  68. pdd/path_resolution.py +140 -0
  69. pdd/postprocess.py +122 -97
  70. pdd/preprocess.py +68 -12
  71. pdd/preprocess_main.py +33 -1
  72. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  73. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  74. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  75. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  76. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  77. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  78. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  79. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  80. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  81. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  82. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  83. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  84. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
  85. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  86. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  87. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  88. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  89. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  90. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  91. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  92. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  93. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  94. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  95. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  96. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  97. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  98. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  99. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  100. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  101. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  102. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  103. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  104. pdd/prompts/agentic_update_LLM.prompt +192 -338
  105. pdd/prompts/auto_include_LLM.prompt +22 -0
  106. pdd/prompts/change_LLM.prompt +3093 -1
  107. pdd/prompts/detect_change_LLM.prompt +571 -14
  108. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  109. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  110. pdd/prompts/generate_test_LLM.prompt +19 -1
  111. pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
  112. pdd/prompts/insert_includes_LLM.prompt +262 -252
  113. pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
  114. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  115. pdd/remote_session.py +876 -0
  116. pdd/server/__init__.py +52 -0
  117. pdd/server/app.py +335 -0
  118. pdd/server/click_executor.py +587 -0
  119. pdd/server/executor.py +338 -0
  120. pdd/server/jobs.py +661 -0
  121. pdd/server/models.py +241 -0
  122. pdd/server/routes/__init__.py +31 -0
  123. pdd/server/routes/architecture.py +451 -0
  124. pdd/server/routes/auth.py +364 -0
  125. pdd/server/routes/commands.py +929 -0
  126. pdd/server/routes/config.py +42 -0
  127. pdd/server/routes/files.py +603 -0
  128. pdd/server/routes/prompts.py +1347 -0
  129. pdd/server/routes/websocket.py +473 -0
  130. pdd/server/security.py +243 -0
  131. pdd/server/terminal_spawner.py +217 -0
  132. pdd/server/token_counter.py +222 -0
  133. pdd/summarize_directory.py +236 -237
  134. pdd/sync_animation.py +8 -4
  135. pdd/sync_determine_operation.py +329 -47
  136. pdd/sync_main.py +272 -28
  137. pdd/sync_orchestration.py +289 -211
  138. pdd/sync_order.py +304 -0
  139. pdd/template_expander.py +161 -0
  140. pdd/templates/architecture/architecture_json.prompt +41 -46
  141. pdd/trace.py +1 -1
  142. pdd/track_cost.py +0 -13
  143. pdd/unfinished_prompt.py +2 -1
  144. pdd/update_main.py +68 -26
  145. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
  146. pdd_cli-0.0.121.dist-info/RECORD +229 -0
  147. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  148. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
  149. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
  150. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
  151. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/agentic_change.py ADDED
@@ -0,0 +1,231 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import re
5
+ import shutil
6
+ import subprocess
7
+ import tempfile
8
+ from pathlib import Path
9
+ from typing import List, Tuple, Optional, Any
10
+
11
+ from rich.console import Console
12
+
13
+ # Internal imports
14
+ from .agentic_change_orchestrator import run_agentic_change_orchestrator
15
+
16
+ console = Console()
17
+
18
+
19
+ def _check_gh_cli() -> bool:
20
+ """
21
+ Check if the GitHub CLI (gh) is installed and available in the system PATH.
22
+ """
23
+ return shutil.which("gh") is not None
24
+
25
+
26
+ def _parse_issue_url(url: str) -> Optional[Tuple[str, str, int]]:
27
+ """
28
+ Parse a GitHub issue URL to extract the owner, repository name, and issue number.
29
+
30
+ Supported formats:
31
+ - https://github.com/{owner}/{repo}/issues/{number}
32
+ - https://www.github.com/{owner}/{repo}/issues/{number}
33
+ - github.com/{owner}/{repo}/issues/{number}
34
+
35
+ Returns:
36
+ Tuple of (owner, repo, issue_number) if successful, else None.
37
+ """
38
+ pattern = r"(?:https?://)?(?:www\.)?github\.com/([^/]+)/([^/]+)/issues/(\d+)"
39
+ match = re.search(pattern, url)
40
+ if match:
41
+ return match.group(1), match.group(2), int(match.group(3))
42
+ return None
43
+
44
+
45
+ def _run_gh_command(args: List[str]) -> Tuple[bool, str]:
46
+ """
47
+ Execute a gh CLI command.
48
+
49
+ Args:
50
+ args: List of arguments to pass to `gh`.
51
+
52
+ Returns:
53
+ Tuple of (success, output). Output is stdout on success, stderr on failure.
54
+ """
55
+ try:
56
+ result = subprocess.run(
57
+ ["gh"] + args,
58
+ capture_output=True,
59
+ text=True,
60
+ check=False
61
+ )
62
+ if result.returncode != 0:
63
+ return False, result.stderr.strip()
64
+ return True, result.stdout.strip()
65
+ except Exception as e:
66
+ return False, str(e)
67
+
68
+
69
+ def _setup_repository(owner: str, repo: str, quiet: bool) -> Path:
70
+ """
71
+ Prepare the working directory for the agent.
72
+
73
+ Logic:
74
+ 1. If the current directory is the target repository, use it.
75
+ 2. Otherwise, clone the repository into a temporary directory.
76
+
77
+ Returns:
78
+ Path to the working directory.
79
+ """
80
+ # Check if current directory is the repo
81
+ try:
82
+ if (Path.cwd() / ".git").exists():
83
+ # Get remote origin URL
84
+ res = subprocess.run(
85
+ ["git", "remote", "get-url", "origin"],
86
+ capture_output=True,
87
+ text=True
88
+ )
89
+ if res.returncode == 0:
90
+ remote_url = res.stdout.strip()
91
+ # Check if owner/repo is in the remote URL
92
+ # Matches formats like:
93
+ # - https://github.com/owner/repo.git
94
+ # - git@github.com:owner/repo.git
95
+ if f"{owner}/{repo}" in remote_url:
96
+ if not quiet:
97
+ console.print(f"[blue]Using current directory as repository: {Path.cwd()}[/blue]")
98
+ return Path.cwd()
99
+ except Exception:
100
+ # If git check fails, proceed to clone
101
+ pass
102
+
103
+ # Clone to a temporary directory
104
+ temp_dir = Path(tempfile.mkdtemp(prefix=f"pdd_{repo}_"))
105
+ if not quiet:
106
+ console.print(f"[blue]Cloning {owner}/{repo} to temporary directory: {temp_dir}[/blue]")
107
+
108
+ # Use gh repo clone to handle authentication automatically
109
+ clone_cmd = ["repo", "clone", f"{owner}/{repo}", "."]
110
+
111
+ # We run this in the temp_dir
112
+ try:
113
+ result = subprocess.run(
114
+ ["gh"] + clone_cmd,
115
+ cwd=temp_dir,
116
+ capture_output=True,
117
+ text=True,
118
+ check=False
119
+ )
120
+ if result.returncode != 0:
121
+ raise RuntimeError(f"Failed to clone repository: {result.stderr.strip()}")
122
+ except Exception as e:
123
+ raise RuntimeError(f"Failed to execute clone command: {e}")
124
+
125
+ return temp_dir
126
+
127
+
128
+ def run_agentic_change(
129
+ issue_url: str,
130
+ *,
131
+ verbose: bool = False,
132
+ quiet: bool = False,
133
+ timeout_adder: float = 0.0,
134
+ use_github_state: bool = True
135
+ ) -> Tuple[bool, str, float, str, List[str]]:
136
+ """
137
+ CLI entry point for the agentic change workflow.
138
+
139
+ Fetches issue details and comments from GitHub, sets up the repository,
140
+ and invokes the orchestrator to perform the 12-step change process.
141
+
142
+ Args:
143
+ issue_url: The full URL of the GitHub issue.
144
+ verbose: If True, enables detailed logging.
145
+ quiet: If True, suppresses standard output.
146
+ timeout_adder: Additional time to add to step timeouts.
147
+ use_github_state: If True, persists state to GitHub comments.
148
+
149
+ Returns:
150
+ Tuple containing:
151
+ - success (bool)
152
+ - message (str)
153
+ - total_cost (float)
154
+ - model_used (str)
155
+ - changed_files (List[str])
156
+ """
157
+ # 1. Check dependencies
158
+ if not _check_gh_cli():
159
+ return False, "gh CLI not found", 0.0, "", []
160
+
161
+ # 2. Parse URL
162
+ parsed = _parse_issue_url(issue_url)
163
+ if not parsed:
164
+ return False, "Invalid GitHub URL", 0.0, "", []
165
+
166
+ owner, repo, issue_number = parsed
167
+
168
+ if not quiet:
169
+ console.print(f"[bold]Fetching issue #{issue_number} from {owner}/{repo}...[/bold]")
170
+
171
+ # 3. Fetch Issue Content
172
+ success, issue_json = _run_gh_command(["api", f"repos/{owner}/{repo}/issues/{issue_number}"])
173
+ if not success:
174
+ return False, f"Issue not found: {issue_json}", 0.0, "", []
175
+
176
+ try:
177
+ issue_data = json.loads(issue_json)
178
+ except json.JSONDecodeError:
179
+ return False, "Failed to parse issue JSON", 0.0, "", []
180
+
181
+ # Extract metadata
182
+ title = issue_data.get("title", "")
183
+ body = issue_data.get("body", "") or ""
184
+ author = issue_data.get("user", {}).get("login", "unknown")
185
+ comments_url = issue_data.get("comments_url", "")
186
+
187
+ # 4. Fetch Comments
188
+ comments_data = []
189
+ if comments_url:
190
+ success, comments_json = _run_gh_command(["api", comments_url])
191
+ if success:
192
+ try:
193
+ comments_data = json.loads(comments_json)
194
+ except json.JSONDecodeError:
195
+ if verbose:
196
+ console.print("[yellow]Warning: Failed to parse comments JSON[/yellow]")
197
+
198
+ # 5. Construct Full Context
199
+ issue_content = f"Title: {title}\n\nDescription:\n{body}\n"
200
+ if comments_data and isinstance(comments_data, list):
201
+ issue_content += "\nComments:\n"
202
+ for comment in comments_data:
203
+ if isinstance(comment, dict):
204
+ c_user = comment.get("user", {}).get("login", "unknown")
205
+ c_body = comment.get("body", "")
206
+ issue_content += f"\n--- Comment by {c_user} ---\n{c_body}\n"
207
+
208
+ # 6. Setup Repository (Clone or Use Current)
209
+ try:
210
+ work_dir = _setup_repository(owner, repo, quiet)
211
+ except RuntimeError as e:
212
+ return False, str(e), 0.0, "", []
213
+
214
+ # 7. Run Orchestrator
215
+ if not quiet:
216
+ console.print(f"[bold green]Starting Agentic Change Orchestrator...[/bold green]")
217
+
218
+ return run_agentic_change_orchestrator(
219
+ issue_url=issue_url,
220
+ issue_content=issue_content,
221
+ repo_owner=owner,
222
+ repo_name=repo,
223
+ issue_number=issue_number,
224
+ issue_author=author,
225
+ issue_title=title,
226
+ cwd=work_dir,
227
+ verbose=verbose,
228
+ quiet=quiet,
229
+ timeout_adder=timeout_adder,
230
+ use_github_state=use_github_state
231
+ )