pdd-cli 0.0.90__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 (144) hide show
  1. pdd/__init__.py +38 -6
  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 +521 -786
  7. pdd/agentic_e2e_fix.py +319 -0
  8. pdd/agentic_e2e_fix_orchestrator.py +426 -0
  9. pdd/agentic_fix.py +118 -3
  10. pdd/agentic_update.py +25 -8
  11. pdd/architecture_sync.py +565 -0
  12. pdd/auth_service.py +210 -0
  13. pdd/auto_deps_main.py +63 -53
  14. pdd/auto_include.py +185 -3
  15. pdd/auto_update.py +125 -47
  16. pdd/bug_main.py +195 -23
  17. pdd/cmd_test_main.py +345 -197
  18. pdd/code_generator.py +4 -2
  19. pdd/code_generator_main.py +118 -32
  20. pdd/commands/__init__.py +6 -0
  21. pdd/commands/analysis.py +87 -29
  22. pdd/commands/auth.py +309 -0
  23. pdd/commands/connect.py +290 -0
  24. pdd/commands/fix.py +136 -113
  25. pdd/commands/maintenance.py +3 -2
  26. pdd/commands/misc.py +8 -0
  27. pdd/commands/modify.py +190 -164
  28. pdd/commands/sessions.py +284 -0
  29. pdd/construct_paths.py +334 -32
  30. pdd/context_generator_main.py +167 -170
  31. pdd/continue_generation.py +6 -3
  32. pdd/core/__init__.py +33 -0
  33. pdd/core/cli.py +27 -3
  34. pdd/core/cloud.py +237 -0
  35. pdd/core/errors.py +4 -0
  36. pdd/core/remote_session.py +61 -0
  37. pdd/crash_main.py +219 -23
  38. pdd/data/llm_model.csv +4 -4
  39. pdd/docs/prompting_guide.md +864 -0
  40. pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
  41. pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
  42. pdd/fix_code_loop.py +208 -34
  43. pdd/fix_code_module_errors.py +6 -2
  44. pdd/fix_error_loop.py +291 -38
  45. pdd/fix_main.py +204 -4
  46. pdd/fix_verification_errors_loop.py +235 -26
  47. pdd/fix_verification_main.py +269 -83
  48. pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
  49. pdd/frontend/dist/assets/index-DQ3wkeQ2.js +449 -0
  50. pdd/frontend/dist/index.html +376 -0
  51. pdd/frontend/dist/logo.svg +33 -0
  52. pdd/generate_output_paths.py +46 -5
  53. pdd/generate_test.py +212 -151
  54. pdd/get_comment.py +19 -44
  55. pdd/get_extension.py +8 -9
  56. pdd/get_jwt_token.py +309 -20
  57. pdd/get_language.py +8 -7
  58. pdd/get_run_command.py +7 -5
  59. pdd/insert_includes.py +2 -1
  60. pdd/llm_invoke.py +459 -95
  61. pdd/load_prompt_template.py +15 -34
  62. pdd/path_resolution.py +140 -0
  63. pdd/postprocess.py +4 -1
  64. pdd/preprocess.py +68 -12
  65. pdd/preprocess_main.py +33 -1
  66. pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
  67. pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
  68. pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
  69. pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
  70. pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
  71. pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
  72. pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
  73. pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
  74. pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
  75. pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
  76. pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
  77. pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
  78. pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +131 -0
  79. pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
  80. pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
  81. pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
  82. pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
  83. pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
  84. pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
  85. pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
  86. pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
  87. pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
  88. pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
  89. pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
  90. pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
  91. pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
  92. pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
  93. pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
  94. pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
  95. pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
  96. pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
  97. pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
  98. pdd/prompts/agentic_update_LLM.prompt +192 -338
  99. pdd/prompts/auto_include_LLM.prompt +22 -0
  100. pdd/prompts/change_LLM.prompt +3093 -1
  101. pdd/prompts/detect_change_LLM.prompt +571 -14
  102. pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
  103. pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
  104. pdd/prompts/generate_test_LLM.prompt +20 -1
  105. pdd/prompts/generate_test_from_example_LLM.prompt +115 -0
  106. pdd/prompts/insert_includes_LLM.prompt +262 -252
  107. pdd/prompts/prompt_code_diff_LLM.prompt +119 -0
  108. pdd/prompts/prompt_diff_LLM.prompt +82 -0
  109. pdd/remote_session.py +876 -0
  110. pdd/server/__init__.py +52 -0
  111. pdd/server/app.py +335 -0
  112. pdd/server/click_executor.py +587 -0
  113. pdd/server/executor.py +338 -0
  114. pdd/server/jobs.py +661 -0
  115. pdd/server/models.py +241 -0
  116. pdd/server/routes/__init__.py +31 -0
  117. pdd/server/routes/architecture.py +451 -0
  118. pdd/server/routes/auth.py +364 -0
  119. pdd/server/routes/commands.py +929 -0
  120. pdd/server/routes/config.py +42 -0
  121. pdd/server/routes/files.py +603 -0
  122. pdd/server/routes/prompts.py +1322 -0
  123. pdd/server/routes/websocket.py +473 -0
  124. pdd/server/security.py +243 -0
  125. pdd/server/terminal_spawner.py +209 -0
  126. pdd/server/token_counter.py +222 -0
  127. pdd/summarize_directory.py +236 -237
  128. pdd/sync_animation.py +8 -4
  129. pdd/sync_determine_operation.py +329 -47
  130. pdd/sync_main.py +272 -28
  131. pdd/sync_orchestration.py +136 -75
  132. pdd/template_expander.py +161 -0
  133. pdd/templates/architecture/architecture_json.prompt +41 -46
  134. pdd/trace.py +1 -1
  135. pdd/track_cost.py +0 -13
  136. pdd/unfinished_prompt.py +2 -1
  137. pdd/update_main.py +23 -5
  138. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/METADATA +15 -10
  139. pdd_cli-0.0.118.dist-info/RECORD +227 -0
  140. pdd_cli-0.0.90.dist-info/RECORD +0 -153
  141. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/WHEEL +0 -0
  142. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/entry_points.txt +0 -0
  143. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/licenses/LICENSE +0 -0
  144. {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.118.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,284 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import json
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ import click
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+
11
+ from ..core.cloud import CloudConfig
12
+ from ..remote_session import RemoteSessionManager, RemoteSessionError
13
+
14
+ console = Console()
15
+
16
+
17
+ @click.group(name="sessions")
18
+ def sessions() -> None:
19
+ """Manage remote PDD sessions."""
20
+ pass
21
+
22
+
23
+ @sessions.command(name="list")
24
+ @click.option("--json", "json_output", is_flag=True, help="Output as JSON.")
25
+ def list_sessions(json_output: bool) -> None:
26
+ """List active remote sessions.
27
+
28
+ Retrieves a list of active remote sessions associated with the current
29
+ authenticated user and displays them in a table or as JSON.
30
+ """
31
+ jwt_token = CloudConfig.get_jwt_token()
32
+ if not jwt_token:
33
+ console.print("[red]Error: Not authenticated. Please run 'pdd auth login'.[/red]")
34
+ return
35
+
36
+ try:
37
+ sessions_list = asyncio.run(RemoteSessionManager.list_sessions(jwt_token))
38
+ except Exception as e:
39
+ console.print(f"[red]Error listing sessions: {e}[/red]")
40
+ return
41
+
42
+ if json_output:
43
+ output_data = []
44
+ for s in sessions_list:
45
+ # Handle Pydantic v1/v2 or dataclasses
46
+ if hasattr(s, "model_dump"):
47
+ output_data.append(s.model_dump())
48
+ elif hasattr(s, "dict"):
49
+ output_data.append(s.dict())
50
+ else:
51
+ output_data.append(s.__dict__)
52
+ console.print_json(data=output_data)
53
+ return
54
+
55
+ if not sessions_list:
56
+ console.print("[yellow]No active remote sessions found.[/yellow]")
57
+ return
58
+
59
+ table = Table(show_header=True, header_style="bold magenta")
60
+ table.add_column("SESSION ID", style="dim", width=12)
61
+ table.add_column("PROJECT")
62
+ table.add_column("CLOUD URL", style="blue")
63
+ table.add_column("STATUS")
64
+ table.add_column("LAST SEEN")
65
+
66
+ for session in sessions_list:
67
+ # Safely access attributes with defaults
68
+ s_id = getattr(session, "session_id", "unknown")
69
+ project = getattr(session, "project_name", "default")
70
+ url = getattr(session, "cloud_url", "")
71
+ status = getattr(session, "status", "unknown")
72
+ last_seen = getattr(session, "last_heartbeat", "never")
73
+
74
+ # Truncate ID for display
75
+ display_id = s_id[:8] if len(s_id) > 8 else s_id
76
+
77
+ # Colorize status
78
+ status_str = str(status)
79
+ if status_str.lower() == "active":
80
+ status_render = f"[green]{status_str}[/green]"
81
+ elif status_str.lower() == "stale":
82
+ status_render = f"[yellow]{status_str}[/yellow]"
83
+ else:
84
+ status_render = status_str
85
+
86
+ table.add_row(
87
+ display_id,
88
+ str(project),
89
+ str(url),
90
+ status_render,
91
+ str(last_seen)
92
+ )
93
+
94
+ console.print(table)
95
+
96
+
97
+ @sessions.command(name="info")
98
+ @click.argument("session_id")
99
+ def session_info(session_id: str) -> None:
100
+ """Display detailed info about a specific session.
101
+
102
+ Args:
103
+ session_id: The unique identifier of the session to inspect.
104
+ """
105
+ jwt_token = CloudConfig.get_jwt_token()
106
+ if not jwt_token:
107
+ console.print("[red]Error: Not authenticated. Please run 'pdd auth login'.[/red]")
108
+ return
109
+
110
+ try:
111
+ # Attempt to fetch specific session details
112
+ # Note: Assuming get_session exists on RemoteSessionManager
113
+ session = asyncio.run(RemoteSessionManager.get_session(jwt_token, session_id))
114
+ except Exception as e:
115
+ console.print(f"[red]Error fetching session: {e}[/red]")
116
+ return
117
+
118
+ if not session:
119
+ console.print(f"[red]Session '{session_id}' not found.[/red]")
120
+ return
121
+
122
+ console.print(f"[bold blue]Session Information: {session_id}[/bold blue]")
123
+
124
+ # Convert session object to dictionary for iteration
125
+ if hasattr(session, "model_dump"):
126
+ data = session.model_dump()
127
+ elif hasattr(session, "dict"):
128
+ data = session.dict()
129
+ else:
130
+ data = session.__dict__
131
+
132
+ # Display metadata in a clean table
133
+ table = Table(show_header=False, box=None, padding=(0, 2))
134
+ table.add_column("Field", style="bold cyan", justify="right")
135
+ table.add_column("Value", style="white")
136
+
137
+ # Sort keys for consistent display
138
+ for key in sorted(data.keys()):
139
+ value = data[key]
140
+ # Format key for display (snake_case to Title Case)
141
+ display_key = key.replace("_", " ").title()
142
+ table.add_row(display_key, str(value))
143
+
144
+ console.print(table)
145
+
146
+
147
+ @sessions.command(name="cleanup")
148
+ @click.option("--all", "cleanup_all", is_flag=True, help="Cleanup all sessions (including active).")
149
+ @click.option("--stale", "cleanup_stale", is_flag=True, help="Cleanup only stale sessions.")
150
+ @click.option("--force", is_flag=True, help="Skip confirmation prompt.")
151
+ def cleanup_sessions(cleanup_all: bool, cleanup_stale: bool, force: bool) -> None:
152
+ """Cleanup (deregister) remote sessions.
153
+
154
+ By default, lists sessions and prompts for cleanup.
155
+ Use --all to cleanup all sessions, or --stale to cleanup only stale sessions.
156
+ """
157
+ jwt_token = CloudConfig.get_jwt_token()
158
+ if not jwt_token:
159
+ console.print("[red]Error: Not authenticated. Please run 'pdd login'.[/red]")
160
+ return
161
+
162
+ try:
163
+ sessions_list = asyncio.run(RemoteSessionManager.list_sessions(jwt_token))
164
+ except Exception as e:
165
+ console.print(f"[red]Error listing sessions: {e}[/red]")
166
+ return
167
+
168
+ if not sessions_list:
169
+ console.print("[yellow]No active remote sessions found.[/yellow]")
170
+ return
171
+
172
+ # Filter sessions based on flags
173
+ if cleanup_stale:
174
+ sessions_to_cleanup = [s for s in sessions_list if getattr(s, "status", "").lower() == "stale"]
175
+ if not sessions_to_cleanup:
176
+ console.print("[yellow]No stale sessions found.[/yellow]")
177
+ return
178
+ elif cleanup_all:
179
+ sessions_to_cleanup = sessions_list
180
+ else:
181
+ # Interactive mode - show sessions and ask which to cleanup
182
+ console.print("[bold]Current remote sessions:[/bold]")
183
+ table = Table(show_header=True, header_style="bold magenta")
184
+ table.add_column("#", style="dim", width=3)
185
+ table.add_column("SESSION ID", style="dim", width=12)
186
+ table.add_column("PROJECT")
187
+ table.add_column("STATUS")
188
+ table.add_column("LAST SEEN")
189
+
190
+ for idx, session in enumerate(sessions_list, 1):
191
+ s_id = getattr(session, "session_id", "unknown")
192
+ project = getattr(session, "project_name", "default")
193
+ status = getattr(session, "status", "unknown")
194
+ last_seen = getattr(session, "last_heartbeat", "never")
195
+
196
+ display_id = s_id[:8] if len(s_id) > 8 else s_id
197
+
198
+ status_str = str(status)
199
+ if status_str.lower() == "active":
200
+ status_render = f"[green]{status_str}[/green]"
201
+ elif status_str.lower() == "stale":
202
+ status_render = f"[yellow]{status_str}[/yellow]"
203
+ else:
204
+ status_render = status_str
205
+
206
+ table.add_row(
207
+ str(idx),
208
+ display_id,
209
+ str(project),
210
+ status_render,
211
+ str(last_seen)
212
+ )
213
+
214
+ console.print(table)
215
+ console.print("\n[bold]Options:[/bold]")
216
+ console.print(" - Enter session numbers (comma-separated) to cleanup specific sessions")
217
+ console.print(" - Enter 'stale' to cleanup all stale sessions")
218
+ console.print(" - Enter 'all' to cleanup all sessions")
219
+ console.print(" - Press Enter to cancel")
220
+
221
+ choice = click.prompt("\nYour choice", default="", show_default=False)
222
+
223
+ if not choice:
224
+ console.print("[yellow]Cancelled.[/yellow]")
225
+ return
226
+
227
+ if choice.lower() == "all":
228
+ sessions_to_cleanup = sessions_list
229
+ elif choice.lower() == "stale":
230
+ sessions_to_cleanup = [s for s in sessions_list if getattr(s, "status", "").lower() == "stale"]
231
+ if not sessions_to_cleanup:
232
+ console.print("[yellow]No stale sessions found.[/yellow]")
233
+ return
234
+ else:
235
+ # Parse comma-separated numbers
236
+ try:
237
+ indices = [int(x.strip()) - 1 for x in choice.split(",")]
238
+ sessions_to_cleanup = [sessions_list[i] for i in indices if 0 <= i < len(sessions_list)]
239
+ if not sessions_to_cleanup:
240
+ console.print("[red]Invalid selection.[/red]")
241
+ return
242
+ except (ValueError, IndexError):
243
+ console.print("[red]Invalid input. Please enter numbers separated by commas.[/red]")
244
+ return
245
+
246
+ # Confirm cleanup
247
+ if not force:
248
+ console.print(f"\n[bold yellow]About to cleanup {len(sessions_to_cleanup)} session(s):[/bold yellow]")
249
+ for session in sessions_to_cleanup:
250
+ s_id = getattr(session, "session_id", "unknown")
251
+ project = getattr(session, "project_name", "default")
252
+ console.print(f" - {s_id[:8]} ({project})")
253
+
254
+ if not click.confirm("\nProceed with cleanup?", default=False):
255
+ console.print("[yellow]Cancelled.[/yellow]")
256
+ return
257
+
258
+ # Perform cleanup
259
+ success_count = 0
260
+ fail_count = 0
261
+
262
+ async def cleanup_session(session_id: str) -> bool:
263
+ """Helper to deregister a single session."""
264
+ from pathlib import Path
265
+ manager = RemoteSessionManager(jwt_token, project_path=Path.cwd())
266
+ manager.session_id = session_id
267
+ try:
268
+ await manager.deregister()
269
+ return True
270
+ except Exception as e:
271
+ console.print(f"[red]Failed to cleanup {session_id[:8]}: {e}[/red]")
272
+ return False
273
+
274
+ with console.status("[bold green]Cleaning up sessions..."):
275
+ for session in sessions_to_cleanup:
276
+ s_id = getattr(session, "session_id", "unknown")
277
+ if asyncio.run(cleanup_session(s_id)):
278
+ success_count += 1
279
+ else:
280
+ fail_count += 1
281
+
282
+ console.print(f"\n[bold green]✓[/bold green] Successfully cleaned up {success_count} session(s)")
283
+ if fail_count > 0:
284
+ console.print(f"[bold red]✗[/bold red] Failed to cleanup {fail_count} session(s)")