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/__init__.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"""PDD - Prompt Driven Development"""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
__version__ = "0.0.121"
|
|
4
6
|
|
|
5
7
|
# Strength parameter used for LLM extraction across the codebase
|
|
6
8
|
# Used in postprocessing, XML tagging, code generation, and other extraction
|
|
7
9
|
# operations. The module should have a large context window and be affordable.
|
|
8
|
-
EXTRACTION_STRENGTH = 0.
|
|
10
|
+
EXTRACTION_STRENGTH = 0.5
|
|
9
11
|
|
|
10
12
|
DEFAULT_STRENGTH = 1.0
|
|
11
13
|
|
|
@@ -15,9 +17,39 @@ DEFAULT_TIME = 0.25
|
|
|
15
17
|
|
|
16
18
|
# Define constants used across the package
|
|
17
19
|
DEFAULT_LLM_MODEL = "gpt-5.1-codex-mini"
|
|
18
|
-
# When going to production, set the following constants:
|
|
19
|
-
# REACT_APP_FIREBASE_API_KEY
|
|
20
|
-
# GITHUB_CLIENT_ID
|
|
21
20
|
|
|
22
|
-
#
|
|
21
|
+
# Public OAuth credentials for cloud mode
|
|
22
|
+
# These are safe to embed as they are public client identifiers:
|
|
23
|
+
# - Firebase API keys are designed to be public (client-side)
|
|
24
|
+
# - GitHub OAuth Client IDs are public (the secret stays server-side)
|
|
25
|
+
# Users still need to authenticate via GitHub OAuth to use cloud features.
|
|
26
|
+
_DEFAULT_FIREBASE_API_KEY = "AIzaSyC0w2jwRR82ZFgQs_YXJoEBqnnTH71X6BE"
|
|
27
|
+
_DEFAULT_GITHUB_CLIENT_ID = "Ov23liJ4eSm0y5W1L20u"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _setup_cloud_defaults() -> None:
|
|
31
|
+
"""Set up default cloud credentials if not already set.
|
|
32
|
+
|
|
33
|
+
Only sets defaults when:
|
|
34
|
+
1. Not running inside cloud environment (K_SERVICE or FUNCTIONS_EMULATOR)
|
|
35
|
+
2. Environment variables are not already set
|
|
36
|
+
|
|
37
|
+
This prevents infinite loops when cloud endpoints call CLI internally,
|
|
38
|
+
while providing a seamless experience for local users.
|
|
39
|
+
"""
|
|
40
|
+
# Skip if running in cloud environment to prevent infinite loops
|
|
41
|
+
if os.environ.get("K_SERVICE") or os.environ.get("FUNCTIONS_EMULATOR"):
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
# Set Firebase API key if not already set
|
|
45
|
+
if not os.environ.get("NEXT_PUBLIC_FIREBASE_API_KEY"):
|
|
46
|
+
os.environ["NEXT_PUBLIC_FIREBASE_API_KEY"] = _DEFAULT_FIREBASE_API_KEY
|
|
47
|
+
|
|
48
|
+
# Set GitHub Client ID if not already set
|
|
49
|
+
if not os.environ.get("GITHUB_CLIENT_ID"):
|
|
50
|
+
os.environ["GITHUB_CLIENT_ID"] = _DEFAULT_GITHUB_CLIENT_ID
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# Initialize cloud defaults on package import
|
|
54
|
+
_setup_cloud_defaults()
|
|
23
55
|
|
pdd/agentic_bug.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Agentic bug investigation entry point.
|
|
5
|
+
|
|
6
|
+
This module serves as the CLI entry point for the agentic bug investigation workflow.
|
|
7
|
+
It parses a GitHub issue URL, fetches the issue content and comments using the `gh` CLI,
|
|
8
|
+
sets up the environment, and invokes the orchestrator to run the investigation process.
|
|
9
|
+
It also supports a legacy manual mode via argument inspection.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
import tempfile
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
19
|
+
|
|
20
|
+
from rich.console import Console
|
|
21
|
+
|
|
22
|
+
# Internal imports
|
|
23
|
+
from .agentic_bug_orchestrator import run_agentic_bug_orchestrator
|
|
24
|
+
from .bug_main import bug_main
|
|
25
|
+
|
|
26
|
+
# Optional globals from package root
|
|
27
|
+
try: # pragma: no cover
|
|
28
|
+
from . import DEFAULT_STRENGTH
|
|
29
|
+
except Exception: # pragma: no cover
|
|
30
|
+
DEFAULT_STRENGTH = None
|
|
31
|
+
|
|
32
|
+
console = Console()
|
|
33
|
+
|
|
34
|
+
__all__ = ["run_agentic_bug"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _check_gh_cli() -> bool:
|
|
38
|
+
"""Check if the GitHub CLI (gh) is installed and available on PATH."""
|
|
39
|
+
return shutil.which("gh") is not None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _parse_github_url(url: str) -> Optional[Tuple[str, str, int]]:
|
|
43
|
+
"""
|
|
44
|
+
Parse a GitHub issue URL to extract owner, repo, and issue number.
|
|
45
|
+
|
|
46
|
+
Supported formats:
|
|
47
|
+
- https://github.com/{owner}/{repo}/issues/{number}
|
|
48
|
+
- https://www.github.com/{owner}/{repo}/issues/{number}
|
|
49
|
+
- github.com/{owner}/{repo}/issues/{number}
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
url: The URL string to parse.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Tuple of (owner, repo, issue_number) if successful, else None.
|
|
56
|
+
"""
|
|
57
|
+
# Remove protocol and www if present
|
|
58
|
+
clean_url = url.replace("https://", "").replace("http://", "").replace("www.", "")
|
|
59
|
+
|
|
60
|
+
# Regex for github.com/owner/repo/issues/number
|
|
61
|
+
# Allows for optional trailing slash or query params (though query params usually follow ?)
|
|
62
|
+
pattern = r"^github\.com/([^/]+)/([^/]+)/issues/(\d+)"
|
|
63
|
+
match = re.match(pattern, clean_url)
|
|
64
|
+
|
|
65
|
+
if match:
|
|
66
|
+
owner, repo, number_str = match.groups()
|
|
67
|
+
try:
|
|
68
|
+
return owner, repo, int(number_str)
|
|
69
|
+
except ValueError:
|
|
70
|
+
return None
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _fetch_issue_data(owner: str, repo: str, number: int) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
|
|
75
|
+
"""
|
|
76
|
+
Fetch issue metadata and content using `gh api`.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
owner: Repository owner.
|
|
80
|
+
repo: Repository name.
|
|
81
|
+
number: Issue number.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
(data_dict, error_message)
|
|
85
|
+
- data_dict: JSON response from GitHub API if successful.
|
|
86
|
+
- error_message: Error string if failed.
|
|
87
|
+
"""
|
|
88
|
+
cmd = [
|
|
89
|
+
"gh", "api",
|
|
90
|
+
f"repos/{owner}/{repo}/issues/{number}",
|
|
91
|
+
"--header", "Accept: application/vnd.github+json"
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
96
|
+
return json.loads(result.stdout), None
|
|
97
|
+
except subprocess.CalledProcessError as e:
|
|
98
|
+
err_msg = e.stderr.strip() or str(e)
|
|
99
|
+
return None, f"Failed to fetch issue: {err_msg}"
|
|
100
|
+
except json.JSONDecodeError:
|
|
101
|
+
return None, "Failed to parse GitHub API response"
|
|
102
|
+
except Exception as e:
|
|
103
|
+
return None, str(e)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _fetch_comments(comments_url: str) -> str:
|
|
107
|
+
"""
|
|
108
|
+
Fetch comments for an issue to provide full context.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
comments_url: API URL for comments (provided in issue metadata).
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Concatenated string of comments formatted as "User: Comment".
|
|
115
|
+
"""
|
|
116
|
+
# The comments_url from API is full URL like https://api.github.com/repos/...
|
|
117
|
+
# gh api expects path relative to api root or full URL.
|
|
118
|
+
cmd = ["gh", "api", comments_url]
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
122
|
+
comments_data = json.loads(result.stdout)
|
|
123
|
+
|
|
124
|
+
formatted_comments = []
|
|
125
|
+
for comment in comments_data:
|
|
126
|
+
user = comment.get("user", {}).get("login", "Unknown")
|
|
127
|
+
body = comment.get("body", "")
|
|
128
|
+
formatted_comments.append(f"--- Comment by {user} ---\n{body}\n")
|
|
129
|
+
|
|
130
|
+
return "\n".join(formatted_comments)
|
|
131
|
+
except Exception:
|
|
132
|
+
# If comments fail, we proceed with just the issue body
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _ensure_repo_context(owner: str, repo: str, cwd: Path, quiet: bool = False) -> bool:
|
|
137
|
+
"""
|
|
138
|
+
Ensure the current working directory contains the repository.
|
|
139
|
+
If not, clone it into the current directory.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
owner: Repo owner.
|
|
143
|
+
repo: Repo name.
|
|
144
|
+
cwd: Current working directory.
|
|
145
|
+
quiet: Suppress output.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
True if successful (exists or cloned), False otherwise.
|
|
149
|
+
"""
|
|
150
|
+
# Check if .git exists
|
|
151
|
+
if (cwd / ".git").exists():
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
# Attempt clone
|
|
155
|
+
repo_url = f"https://github.com/{owner}/{repo}.git"
|
|
156
|
+
if not quiet:
|
|
157
|
+
console.print(f"[blue]Cloning {repo_url} into {cwd}...[/blue]")
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
# Clone into current directory (.)
|
|
161
|
+
subprocess.run(["git", "clone", repo_url, "."], cwd=cwd, check=True, capture_output=True, text=True)
|
|
162
|
+
return True
|
|
163
|
+
except subprocess.CalledProcessError as e:
|
|
164
|
+
if not quiet:
|
|
165
|
+
err = e.stderr.strip() if e.stderr else str(e)
|
|
166
|
+
console.print(f"[red]Failed to clone repository: {err}[/red]")
|
|
167
|
+
return False
|
|
168
|
+
except Exception as e:
|
|
169
|
+
if not quiet:
|
|
170
|
+
console.print(f"[red]Error during clone: {e}[/red]")
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def run_agentic_bug(
|
|
175
|
+
issue_url: str,
|
|
176
|
+
*,
|
|
177
|
+
verbose: bool = False,
|
|
178
|
+
quiet: bool = False,
|
|
179
|
+
timeout_adder: float = 0.0,
|
|
180
|
+
use_github_state: bool = True,
|
|
181
|
+
# Legacy/Manual mode arguments (handled via *args in a real CLI, but here explicit for type safety if called directly)
|
|
182
|
+
manual_args: Optional[Tuple[str, str, str, str, str]] = None
|
|
183
|
+
) -> Tuple[bool, str, float, str, List[str]]:
|
|
184
|
+
"""
|
|
185
|
+
Entry point for the agentic bug investigation.
|
|
186
|
+
|
|
187
|
+
Parses the GitHub issue, fetches context, and invokes the orchestrator.
|
|
188
|
+
|
|
189
|
+
If `manual_args` is provided (simulating the --manual flag logic from a CLI wrapper),
|
|
190
|
+
it delegates to the legacy `bug_main` logic.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
issue_url: The GitHub issue URL to investigate.
|
|
194
|
+
verbose: Enable verbose logging.
|
|
195
|
+
quiet: Suppress informational logging.
|
|
196
|
+
timeout_adder: Additional time to add to step timeouts.
|
|
197
|
+
use_github_state: Whether to use GitHub state (comments, PRs) during orchestration.
|
|
198
|
+
manual_args: Optional tuple of (prompt_file, code_file, program_file, current_out, desired_out)
|
|
199
|
+
to trigger legacy manual mode.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
(success, message, total_cost, model_used, changed_files)
|
|
203
|
+
"""
|
|
204
|
+
# 1. Handle Legacy Manual Mode
|
|
205
|
+
if manual_args:
|
|
206
|
+
if not quiet:
|
|
207
|
+
console.print("[blue]Running in manual mode (legacy)...[/blue]")
|
|
208
|
+
|
|
209
|
+
prompt_file, code_file, program_file, current_out, desired_out = manual_args
|
|
210
|
+
|
|
211
|
+
# Mock context for bug_main
|
|
212
|
+
class MockContext:
|
|
213
|
+
obj = {
|
|
214
|
+
'force': True,
|
|
215
|
+
'quiet': quiet,
|
|
216
|
+
'strength': DEFAULT_STRENGTH,
|
|
217
|
+
'temperature': 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
# bug_main returns (unit_test_content, cost, model)
|
|
222
|
+
# It writes the test file to disk as a side effect if 'output' arg is used,
|
|
223
|
+
# but here we just capture the return.
|
|
224
|
+
# We need to adapt the return signature to match run_agentic_bug.
|
|
225
|
+
unit_test, cost, model = bug_main(
|
|
226
|
+
ctx=MockContext(), # type: ignore
|
|
227
|
+
prompt_file=prompt_file,
|
|
228
|
+
code_file=code_file,
|
|
229
|
+
program_file=program_file,
|
|
230
|
+
current_output=current_out,
|
|
231
|
+
desired_output=desired_out,
|
|
232
|
+
language='Python'
|
|
233
|
+
)
|
|
234
|
+
return True, "Manual test generation successful", cost, model, []
|
|
235
|
+
except Exception as e:
|
|
236
|
+
return False, f"Manual mode failed: {e}", 0.0, "", []
|
|
237
|
+
|
|
238
|
+
# 2. Validate Environment
|
|
239
|
+
if not _check_gh_cli():
|
|
240
|
+
msg = "gh CLI not found. Please install GitHub CLI."
|
|
241
|
+
if not quiet:
|
|
242
|
+
console.print(f"[red]{msg}[/red]")
|
|
243
|
+
return False, msg, 0.0, "", []
|
|
244
|
+
|
|
245
|
+
# 3. Parse URL
|
|
246
|
+
parsed = _parse_github_url(issue_url)
|
|
247
|
+
if not parsed:
|
|
248
|
+
msg = f"Invalid GitHub URL: {issue_url}"
|
|
249
|
+
if not quiet:
|
|
250
|
+
console.print(f"[red]{msg}[/red]")
|
|
251
|
+
return False, msg, 0.0, "", []
|
|
252
|
+
|
|
253
|
+
owner, repo, issue_number = parsed
|
|
254
|
+
if not quiet:
|
|
255
|
+
console.print(f"[blue]Investigating {owner}/{repo} issue #{issue_number}[/blue]")
|
|
256
|
+
|
|
257
|
+
# 4. Fetch Issue Data
|
|
258
|
+
issue_data, error = _fetch_issue_data(owner, repo, issue_number)
|
|
259
|
+
if error or not issue_data:
|
|
260
|
+
msg = f"Issue not found or API error: {error}"
|
|
261
|
+
if not quiet:
|
|
262
|
+
console.print(f"[red]{msg}[/red]")
|
|
263
|
+
return False, msg, 0.0, "", []
|
|
264
|
+
|
|
265
|
+
# 5. Extract Metadata
|
|
266
|
+
try:
|
|
267
|
+
issue_title = issue_data.get("title", "")
|
|
268
|
+
issue_body = issue_data.get("body", "") or ""
|
|
269
|
+
issue_author = issue_data.get("user", {}).get("login", "unknown")
|
|
270
|
+
comments_url = issue_data.get("comments_url", "")
|
|
271
|
+
|
|
272
|
+
# Fetch comments for context
|
|
273
|
+
comments_text = _fetch_comments(comments_url) if comments_url else ""
|
|
274
|
+
|
|
275
|
+
full_content = (
|
|
276
|
+
f"Title: {issue_title}\n"
|
|
277
|
+
f"Author: {issue_author}\n"
|
|
278
|
+
f"Description:\n{issue_body}\n\n"
|
|
279
|
+
f"Comments:\n{comments_text}"
|
|
280
|
+
)
|
|
281
|
+
except Exception as e:
|
|
282
|
+
msg = f"Failed to process issue data: {e}"
|
|
283
|
+
if not quiet:
|
|
284
|
+
console.print(f"[red]{msg}[/red]")
|
|
285
|
+
return False, msg, 0.0, "", []
|
|
286
|
+
|
|
287
|
+
# 6. Prepare Workspace (Repo Context)
|
|
288
|
+
cwd = Path.cwd()
|
|
289
|
+
if not _ensure_repo_context(owner, repo, cwd, quiet=quiet):
|
|
290
|
+
msg = "Failed to clone repository"
|
|
291
|
+
if not quiet:
|
|
292
|
+
console.print(f"[red]{msg}[/red]")
|
|
293
|
+
return False, msg, 0.0, "", []
|
|
294
|
+
|
|
295
|
+
# 7. Invoke Orchestrator
|
|
296
|
+
if not quiet:
|
|
297
|
+
console.print(f"[green]Starting agentic workflow for: '{issue_title}'[/green]")
|
|
298
|
+
|
|
299
|
+
try:
|
|
300
|
+
success, message, cost, model, changed_files = run_agentic_bug_orchestrator(
|
|
301
|
+
issue_url=issue_url,
|
|
302
|
+
issue_content=full_content,
|
|
303
|
+
repo_owner=owner,
|
|
304
|
+
repo_name=repo,
|
|
305
|
+
issue_number=issue_number,
|
|
306
|
+
issue_author=issue_author,
|
|
307
|
+
issue_title=issue_title,
|
|
308
|
+
cwd=cwd,
|
|
309
|
+
verbose=verbose,
|
|
310
|
+
quiet=quiet,
|
|
311
|
+
timeout_adder=timeout_adder,
|
|
312
|
+
use_github_state=use_github_state
|
|
313
|
+
)
|
|
314
|
+
return success, message, cost, model, changed_files
|
|
315
|
+
|
|
316
|
+
except Exception as e:
|
|
317
|
+
msg = f"Orchestrator failed: {e}"
|
|
318
|
+
if not quiet:
|
|
319
|
+
console.print(f"[red]{msg}[/red]")
|
|
320
|
+
if verbose:
|
|
321
|
+
import traceback
|
|
322
|
+
console.print(traceback.format_exc())
|
|
323
|
+
return False, msg, 0.0, "", []
|