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.
- pdd/__init__.py +38 -6
- pdd/agentic_bug.py +323 -0
- pdd/agentic_bug_orchestrator.py +506 -0
- pdd/agentic_change.py +231 -0
- pdd/agentic_change_orchestrator.py +537 -0
- pdd/agentic_common.py +533 -770
- pdd/agentic_crash.py +2 -1
- pdd/agentic_e2e_fix.py +319 -0
- pdd/agentic_e2e_fix_orchestrator.py +582 -0
- pdd/agentic_fix.py +118 -3
- pdd/agentic_update.py +27 -9
- pdd/agentic_verify.py +3 -2
- pdd/architecture_sync.py +565 -0
- pdd/auth_service.py +210 -0
- pdd/auto_deps_main.py +63 -53
- pdd/auto_include.py +236 -3
- pdd/auto_update.py +125 -47
- pdd/bug_main.py +195 -23
- pdd/cmd_test_main.py +345 -197
- pdd/code_generator.py +4 -2
- pdd/code_generator_main.py +118 -32
- pdd/commands/__init__.py +6 -0
- pdd/commands/analysis.py +113 -48
- pdd/commands/auth.py +309 -0
- pdd/commands/connect.py +358 -0
- pdd/commands/fix.py +155 -114
- pdd/commands/generate.py +5 -0
- pdd/commands/maintenance.py +3 -2
- pdd/commands/misc.py +8 -0
- pdd/commands/modify.py +225 -163
- pdd/commands/sessions.py +284 -0
- pdd/commands/utility.py +12 -7
- pdd/construct_paths.py +334 -32
- pdd/context_generator_main.py +167 -170
- pdd/continue_generation.py +6 -3
- pdd/core/__init__.py +33 -0
- pdd/core/cli.py +44 -7
- pdd/core/cloud.py +237 -0
- pdd/core/dump.py +68 -20
- pdd/core/errors.py +4 -0
- pdd/core/remote_session.py +61 -0
- pdd/crash_main.py +219 -23
- pdd/data/llm_model.csv +4 -4
- pdd/docs/prompting_guide.md +864 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/benchmark_analysis.py +495 -0
- pdd/docs/whitepaper_with_benchmarks/data_and_functions/creation_compare.py +528 -0
- pdd/fix_code_loop.py +208 -34
- pdd/fix_code_module_errors.py +6 -2
- pdd/fix_error_loop.py +291 -38
- pdd/fix_main.py +208 -6
- pdd/fix_verification_errors_loop.py +235 -26
- pdd/fix_verification_main.py +269 -83
- pdd/frontend/dist/assets/index-B5DZHykP.css +1 -0
- pdd/frontend/dist/assets/index-CUWd8al1.js +450 -0
- pdd/frontend/dist/index.html +376 -0
- pdd/frontend/dist/logo.svg +33 -0
- pdd/generate_output_paths.py +46 -5
- pdd/generate_test.py +212 -151
- pdd/get_comment.py +19 -44
- pdd/get_extension.py +8 -9
- pdd/get_jwt_token.py +309 -20
- pdd/get_language.py +8 -7
- pdd/get_run_command.py +7 -5
- pdd/insert_includes.py +2 -1
- pdd/llm_invoke.py +531 -97
- pdd/load_prompt_template.py +15 -34
- pdd/operation_log.py +342 -0
- pdd/path_resolution.py +140 -0
- pdd/postprocess.py +122 -97
- pdd/preprocess.py +68 -12
- pdd/preprocess_main.py +33 -1
- pdd/prompts/agentic_bug_step10_pr_LLM.prompt +182 -0
- pdd/prompts/agentic_bug_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_bug_step2_docs_LLM.prompt +129 -0
- pdd/prompts/agentic_bug_step3_triage_LLM.prompt +95 -0
- pdd/prompts/agentic_bug_step4_reproduce_LLM.prompt +97 -0
- pdd/prompts/agentic_bug_step5_root_cause_LLM.prompt +123 -0
- pdd/prompts/agentic_bug_step6_test_plan_LLM.prompt +107 -0
- pdd/prompts/agentic_bug_step7_generate_LLM.prompt +172 -0
- pdd/prompts/agentic_bug_step8_verify_LLM.prompt +119 -0
- pdd/prompts/agentic_bug_step9_e2e_test_LLM.prompt +289 -0
- pdd/prompts/agentic_change_step10_identify_issues_LLM.prompt +1006 -0
- pdd/prompts/agentic_change_step11_fix_issues_LLM.prompt +984 -0
- pdd/prompts/agentic_change_step12_create_pr_LLM.prompt +140 -0
- pdd/prompts/agentic_change_step1_duplicate_LLM.prompt +73 -0
- pdd/prompts/agentic_change_step2_docs_LLM.prompt +101 -0
- pdd/prompts/agentic_change_step3_research_LLM.prompt +126 -0
- pdd/prompts/agentic_change_step4_clarify_LLM.prompt +164 -0
- pdd/prompts/agentic_change_step5_docs_change_LLM.prompt +981 -0
- pdd/prompts/agentic_change_step6_devunits_LLM.prompt +1005 -0
- pdd/prompts/agentic_change_step7_architecture_LLM.prompt +1044 -0
- pdd/prompts/agentic_change_step8_analyze_LLM.prompt +1027 -0
- pdd/prompts/agentic_change_step9_implement_LLM.prompt +1077 -0
- pdd/prompts/agentic_e2e_fix_step1_unit_tests_LLM.prompt +90 -0
- pdd/prompts/agentic_e2e_fix_step2_e2e_tests_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step3_root_cause_LLM.prompt +89 -0
- pdd/prompts/agentic_e2e_fix_step4_fix_e2e_tests_LLM.prompt +96 -0
- pdd/prompts/agentic_e2e_fix_step5_identify_devunits_LLM.prompt +91 -0
- pdd/prompts/agentic_e2e_fix_step6_create_unit_tests_LLM.prompt +106 -0
- pdd/prompts/agentic_e2e_fix_step7_verify_tests_LLM.prompt +116 -0
- pdd/prompts/agentic_e2e_fix_step8_run_pdd_fix_LLM.prompt +120 -0
- pdd/prompts/agentic_e2e_fix_step9_verify_all_LLM.prompt +146 -0
- pdd/prompts/agentic_fix_primary_LLM.prompt +2 -2
- pdd/prompts/agentic_update_LLM.prompt +192 -338
- pdd/prompts/auto_include_LLM.prompt +22 -0
- pdd/prompts/change_LLM.prompt +3093 -1
- pdd/prompts/detect_change_LLM.prompt +571 -14
- pdd/prompts/fix_code_module_errors_LLM.prompt +8 -0
- pdd/prompts/fix_errors_from_unit_tests_LLM.prompt +1 -0
- pdd/prompts/generate_test_LLM.prompt +19 -1
- pdd/prompts/generate_test_from_example_LLM.prompt +366 -0
- pdd/prompts/insert_includes_LLM.prompt +262 -252
- pdd/prompts/prompt_code_diff_LLM.prompt +123 -0
- pdd/prompts/prompt_diff_LLM.prompt +82 -0
- pdd/remote_session.py +876 -0
- pdd/server/__init__.py +52 -0
- pdd/server/app.py +335 -0
- pdd/server/click_executor.py +587 -0
- pdd/server/executor.py +338 -0
- pdd/server/jobs.py +661 -0
- pdd/server/models.py +241 -0
- pdd/server/routes/__init__.py +31 -0
- pdd/server/routes/architecture.py +451 -0
- pdd/server/routes/auth.py +364 -0
- pdd/server/routes/commands.py +929 -0
- pdd/server/routes/config.py +42 -0
- pdd/server/routes/files.py +603 -0
- pdd/server/routes/prompts.py +1347 -0
- pdd/server/routes/websocket.py +473 -0
- pdd/server/security.py +243 -0
- pdd/server/terminal_spawner.py +217 -0
- pdd/server/token_counter.py +222 -0
- pdd/summarize_directory.py +236 -237
- pdd/sync_animation.py +8 -4
- pdd/sync_determine_operation.py +329 -47
- pdd/sync_main.py +272 -28
- pdd/sync_orchestration.py +289 -211
- pdd/sync_order.py +304 -0
- pdd/template_expander.py +161 -0
- pdd/templates/architecture/architecture_json.prompt +41 -46
- pdd/trace.py +1 -1
- pdd/track_cost.py +0 -13
- pdd/unfinished_prompt.py +2 -1
- pdd/update_main.py +68 -26
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/METADATA +15 -10
- pdd_cli-0.0.121.dist-info/RECORD +229 -0
- pdd_cli-0.0.90.dist-info/RECORD +0 -153
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/licenses/LICENSE +0 -0
- {pdd_cli-0.0.90.dist-info → pdd_cli-0.0.121.dist-info}/top_level.txt +0 -0
pdd/commands/sessions.py
ADDED
|
@@ -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)")
|
pdd/commands/utility.py
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Utility commands (install_completion, verify/fix-verification).
|
|
3
3
|
"""
|
|
4
|
+
from __future__ import annotations
|
|
4
5
|
import click
|
|
5
|
-
from typing import Optional, Tuple
|
|
6
|
+
from typing import Optional, Tuple, Dict, Any
|
|
6
7
|
|
|
7
8
|
from ..fix_verification_main import fix_verification_main
|
|
8
9
|
from ..track_cost import track_cost
|
|
9
10
|
from ..core.errors import handle_error
|
|
11
|
+
from ..operation_log import log_operation
|
|
10
12
|
|
|
11
13
|
@click.command("install_completion")
|
|
12
14
|
@click.pass_context
|
|
13
|
-
def install_completion_cmd(ctx: click.Context):
|
|
15
|
+
def install_completion_cmd(ctx: click.Context) -> None:
|
|
14
16
|
"""Install shell completion for the PDD CLI."""
|
|
17
|
+
# Safely retrieve quiet flag, defaulting to False if ctx.obj is None
|
|
18
|
+
quiet = (ctx.obj or {}).get("quiet", False)
|
|
15
19
|
try:
|
|
16
20
|
from .. import cli as cli_module # Import parent module for proper patching
|
|
17
|
-
quiet = ctx.obj.get("quiet", False)
|
|
18
21
|
# Call through cli_module so patches to pdd.cli.install_completion work
|
|
19
22
|
cli_module.install_completion(quiet=quiet)
|
|
20
23
|
except Exception as e:
|
|
21
|
-
handle_error(e, "install_completion",
|
|
24
|
+
handle_error(e, "install_completion", quiet)
|
|
22
25
|
|
|
23
26
|
|
|
24
27
|
@click.command("verify")
|
|
@@ -64,6 +67,7 @@ def install_completion_cmd(ctx: click.Context):
|
|
|
64
67
|
help="Enable agentic fallback if the primary fix mechanism fails.",
|
|
65
68
|
)
|
|
66
69
|
@click.pass_context
|
|
70
|
+
@log_operation(operation="verify", clears_run_report=True, updates_run_report=True)
|
|
67
71
|
@track_cost
|
|
68
72
|
def verify(
|
|
69
73
|
ctx: click.Context,
|
|
@@ -76,7 +80,7 @@ def verify(
|
|
|
76
80
|
max_attempts: int,
|
|
77
81
|
budget: float,
|
|
78
82
|
agentic_fallback: bool,
|
|
79
|
-
) -> Optional[Tuple]:
|
|
83
|
+
) -> Optional[Tuple[Dict[str, Any], float, str]]:
|
|
80
84
|
"""Verify code using a verification program."""
|
|
81
85
|
try:
|
|
82
86
|
# verify command implies a loop if max_attempts > 1, but let's enable loop by default
|
|
@@ -106,5 +110,6 @@ def verify(
|
|
|
106
110
|
except click.Abort:
|
|
107
111
|
raise
|
|
108
112
|
except Exception as exception:
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
quiet = (ctx.obj or {}).get("quiet", False)
|
|
114
|
+
handle_error(exception, "verify", quiet)
|
|
115
|
+
return None
|