agentic-devtools 0.2.0__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.
- agdt_ai_helpers/__init__.py +34 -0
- agentic_devtools/__init__.py +8 -0
- agentic_devtools/background_tasks.py +598 -0
- agentic_devtools/cli/__init__.py +1 -0
- agentic_devtools/cli/azure_devops/__init__.py +222 -0
- agentic_devtools/cli/azure_devops/async_commands.py +1218 -0
- agentic_devtools/cli/azure_devops/auth.py +34 -0
- agentic_devtools/cli/azure_devops/commands.py +728 -0
- agentic_devtools/cli/azure_devops/config.py +49 -0
- agentic_devtools/cli/azure_devops/file_review_commands.py +1038 -0
- agentic_devtools/cli/azure_devops/helpers.py +561 -0
- agentic_devtools/cli/azure_devops/mark_reviewed.py +756 -0
- agentic_devtools/cli/azure_devops/pipeline_commands.py +724 -0
- agentic_devtools/cli/azure_devops/pr_summary_commands.py +579 -0
- agentic_devtools/cli/azure_devops/pull_request_details_commands.py +596 -0
- agentic_devtools/cli/azure_devops/review_commands.py +700 -0
- agentic_devtools/cli/azure_devops/review_helpers.py +191 -0
- agentic_devtools/cli/azure_devops/review_jira.py +308 -0
- agentic_devtools/cli/azure_devops/review_prompts.py +263 -0
- agentic_devtools/cli/azure_devops/run_details_commands.py +935 -0
- agentic_devtools/cli/azure_devops/vpn_toggle.py +1220 -0
- agentic_devtools/cli/git/__init__.py +91 -0
- agentic_devtools/cli/git/async_commands.py +294 -0
- agentic_devtools/cli/git/commands.py +399 -0
- agentic_devtools/cli/git/core.py +152 -0
- agentic_devtools/cli/git/diff.py +210 -0
- agentic_devtools/cli/git/operations.py +737 -0
- agentic_devtools/cli/jira/__init__.py +114 -0
- agentic_devtools/cli/jira/adf.py +105 -0
- agentic_devtools/cli/jira/async_commands.py +439 -0
- agentic_devtools/cli/jira/async_status.py +27 -0
- agentic_devtools/cli/jira/commands.py +28 -0
- agentic_devtools/cli/jira/comment_commands.py +141 -0
- agentic_devtools/cli/jira/config.py +69 -0
- agentic_devtools/cli/jira/create_commands.py +293 -0
- agentic_devtools/cli/jira/formatting.py +131 -0
- agentic_devtools/cli/jira/get_commands.py +287 -0
- agentic_devtools/cli/jira/helpers.py +278 -0
- agentic_devtools/cli/jira/parse_error_report.py +352 -0
- agentic_devtools/cli/jira/role_commands.py +560 -0
- agentic_devtools/cli/jira/state_helpers.py +39 -0
- agentic_devtools/cli/jira/update_commands.py +222 -0
- agentic_devtools/cli/jira/vpn_wrapper.py +58 -0
- agentic_devtools/cli/release/__init__.py +5 -0
- agentic_devtools/cli/release/commands.py +113 -0
- agentic_devtools/cli/release/helpers.py +113 -0
- agentic_devtools/cli/runner.py +318 -0
- agentic_devtools/cli/state.py +174 -0
- agentic_devtools/cli/subprocess_utils.py +109 -0
- agentic_devtools/cli/tasks/__init__.py +28 -0
- agentic_devtools/cli/tasks/commands.py +851 -0
- agentic_devtools/cli/testing.py +442 -0
- agentic_devtools/cli/workflows/__init__.py +80 -0
- agentic_devtools/cli/workflows/advancement.py +204 -0
- agentic_devtools/cli/workflows/base.py +240 -0
- agentic_devtools/cli/workflows/checklist.py +278 -0
- agentic_devtools/cli/workflows/commands.py +1610 -0
- agentic_devtools/cli/workflows/manager.py +802 -0
- agentic_devtools/cli/workflows/preflight.py +323 -0
- agentic_devtools/cli/workflows/worktree_setup.py +1110 -0
- agentic_devtools/dispatcher.py +704 -0
- agentic_devtools/file_locking.py +203 -0
- agentic_devtools/prompts/__init__.py +38 -0
- agentic_devtools/prompts/apply-pull-request-review-suggestions/default-initiate-prompt.md +82 -0
- agentic_devtools/prompts/create-jira-epic/default-initiate-prompt.md +63 -0
- agentic_devtools/prompts/create-jira-issue/default-initiate-prompt.md +306 -0
- agentic_devtools/prompts/create-jira-subtask/default-initiate-prompt.md +57 -0
- agentic_devtools/prompts/loader.py +377 -0
- agentic_devtools/prompts/pull-request-review/default-completion-prompt.md +45 -0
- agentic_devtools/prompts/pull-request-review/default-decision-prompt.md +63 -0
- agentic_devtools/prompts/pull-request-review/default-file-review-prompt.md +69 -0
- agentic_devtools/prompts/pull-request-review/default-initiate-prompt.md +50 -0
- agentic_devtools/prompts/pull-request-review/default-summary-prompt.md +40 -0
- agentic_devtools/prompts/update-jira-issue/default-initiate-prompt.md +78 -0
- agentic_devtools/prompts/work-on-jira-issue/default-checklist-creation-prompt.md +58 -0
- agentic_devtools/prompts/work-on-jira-issue/default-commit-prompt.md +47 -0
- agentic_devtools/prompts/work-on-jira-issue/default-completion-prompt.md +65 -0
- agentic_devtools/prompts/work-on-jira-issue/default-implementation-prompt.md +66 -0
- agentic_devtools/prompts/work-on-jira-issue/default-implementation-review-prompt.md +60 -0
- agentic_devtools/prompts/work-on-jira-issue/default-initiate-prompt.md +67 -0
- agentic_devtools/prompts/work-on-jira-issue/default-planning-prompt.md +50 -0
- agentic_devtools/prompts/work-on-jira-issue/default-pull-request-prompt.md +56 -0
- agentic_devtools/prompts/work-on-jira-issue/default-retrieve-prompt.md +29 -0
- agentic_devtools/prompts/work-on-jira-issue/default-setup-prompt.md +19 -0
- agentic_devtools/prompts/work-on-jira-issue/default-verification-prompt.md +73 -0
- agentic_devtools/state.py +754 -0
- agentic_devtools/task_state.py +902 -0
- agentic_devtools-0.2.0.dist-info/METADATA +544 -0
- agentic_devtools-0.2.0.dist-info/RECORD +92 -0
- agentic_devtools-0.2.0.dist-info/WHEEL +4 -0
- agentic_devtools-0.2.0.dist-info/entry_points.txt +79 -0
- agentic_devtools-0.2.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async Azure DevOps command wrappers.
|
|
3
|
+
|
|
4
|
+
Provides async versions of Azure DevOps commands that run in background processes.
|
|
5
|
+
All commands that make HTTP requests to Azure DevOps should have async versions here.
|
|
6
|
+
|
|
7
|
+
These async commands call the sync functions directly via run_function_in_background,
|
|
8
|
+
not via CLI entry points.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
from agentic_devtools.background_tasks import run_function_in_background
|
|
16
|
+
from agentic_devtools.state import get_value, set_value
|
|
17
|
+
from agentic_devtools.task_state import print_task_tracking_info
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _set_value_if_provided(key: str, value: Optional[str]) -> None:
|
|
21
|
+
"""Set a state value if provided (not None)."""
|
|
22
|
+
if value is not None:
|
|
23
|
+
set_value(key, value)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _require_value(key: str, cli_example: str) -> str:
|
|
27
|
+
"""Get a required state value or exit with error."""
|
|
28
|
+
value = get_value(key)
|
|
29
|
+
if not value:
|
|
30
|
+
print(
|
|
31
|
+
f"Error: {key} is required. Use: {cli_example}",
|
|
32
|
+
file=sys.stderr,
|
|
33
|
+
)
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
return str(value)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Module paths for the sync functions
|
|
39
|
+
_COMMANDS_MODULE = "agentic_devtools.cli.azure_devops.commands"
|
|
40
|
+
_FILE_REVIEW_MODULE = "agentic_devtools.cli.azure_devops.file_review_commands"
|
|
41
|
+
_PIPELINE_MODULE = "agentic_devtools.cli.azure_devops.pipeline_commands"
|
|
42
|
+
_REVIEW_MODULE = "agentic_devtools.cli.azure_devops.review_commands"
|
|
43
|
+
_PR_DETAILS_MODULE = "agentic_devtools.cli.azure_devops.pull_request_details_commands"
|
|
44
|
+
_PR_SUMMARY_MODULE = "agentic_devtools.cli.azure_devops.pr_summary_commands"
|
|
45
|
+
_RUN_DETAILS_MODULE = "agentic_devtools.cli.azure_devops.run_details_commands"
|
|
46
|
+
_MARK_REVIEWED_MODULE = "agentic_devtools.cli.azure_devops.mark_reviewed"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# =============================================================================
|
|
50
|
+
# Pull Request Commands (Async)
|
|
51
|
+
# =============================================================================
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def add_pull_request_comment_async() -> None:
|
|
55
|
+
"""
|
|
56
|
+
Add a comment to a pull request asynchronously in the background.
|
|
57
|
+
|
|
58
|
+
State keys:
|
|
59
|
+
pull_request_id (required): PR ID
|
|
60
|
+
content (required): Comment content
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
agdt-set pull_request_id 12345
|
|
64
|
+
agdt-set content "LGTM!"
|
|
65
|
+
agdt-add-pull-request-comment
|
|
66
|
+
"""
|
|
67
|
+
task = run_function_in_background(
|
|
68
|
+
_COMMANDS_MODULE,
|
|
69
|
+
"add_pull_request_comment",
|
|
70
|
+
command_display_name="agdt-add-pull-request-comment",
|
|
71
|
+
)
|
|
72
|
+
print_task_tracking_info(task, "Adding comment to pull request")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def approve_pull_request_async() -> None:
|
|
76
|
+
"""
|
|
77
|
+
Approve a pull request asynchronously in the background.
|
|
78
|
+
|
|
79
|
+
State keys:
|
|
80
|
+
pull_request_id (required): PR ID
|
|
81
|
+
|
|
82
|
+
Usage:
|
|
83
|
+
agdt-set pull_request_id 12345
|
|
84
|
+
agdt-approve-pull-request
|
|
85
|
+
"""
|
|
86
|
+
task = run_function_in_background(
|
|
87
|
+
_COMMANDS_MODULE,
|
|
88
|
+
"approve_pull_request",
|
|
89
|
+
command_display_name="agdt-approve-pull-request",
|
|
90
|
+
)
|
|
91
|
+
print_task_tracking_info(task, "Approving pull request")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def create_pull_request_async(
|
|
95
|
+
source_branch: Optional[str] = None,
|
|
96
|
+
title: Optional[str] = None,
|
|
97
|
+
description: Optional[str] = None,
|
|
98
|
+
) -> None:
|
|
99
|
+
"""
|
|
100
|
+
Create a pull request asynchronously in the background.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
source_branch: Source branch name (overrides state)
|
|
104
|
+
title: PR title (overrides state)
|
|
105
|
+
description: PR description (overrides state)
|
|
106
|
+
|
|
107
|
+
State keys (used as fallbacks):
|
|
108
|
+
source_branch (required): Source branch name
|
|
109
|
+
title (required): PR title
|
|
110
|
+
description (optional): PR description
|
|
111
|
+
|
|
112
|
+
Usage:
|
|
113
|
+
agdt-create-pull-request --source-branch "feature/my-feature" --title "Add feature"
|
|
114
|
+
|
|
115
|
+
# Or using state:
|
|
116
|
+
agdt-set source_branch "feature/my-feature"
|
|
117
|
+
agdt-set title "Add new feature"
|
|
118
|
+
agdt-create-pull-request
|
|
119
|
+
"""
|
|
120
|
+
# Store CLI args in state if provided
|
|
121
|
+
_set_value_if_provided("source_branch", source_branch)
|
|
122
|
+
_set_value_if_provided("title", title)
|
|
123
|
+
_set_value_if_provided("description", description)
|
|
124
|
+
|
|
125
|
+
# Validate required values
|
|
126
|
+
_require_value("source_branch", 'agdt-create-pull-request --source-branch "branch-name"')
|
|
127
|
+
_require_value("title", 'agdt-create-pull-request --title "PR title"')
|
|
128
|
+
|
|
129
|
+
task = run_function_in_background(
|
|
130
|
+
_COMMANDS_MODULE,
|
|
131
|
+
"create_pull_request",
|
|
132
|
+
command_display_name="agdt-create-pull-request",
|
|
133
|
+
)
|
|
134
|
+
print_task_tracking_info(task, "Creating pull request")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def create_pull_request_async_cli() -> None:
|
|
138
|
+
"""CLI entry point for create_pull_request_async with argument parsing."""
|
|
139
|
+
parser = argparse.ArgumentParser(
|
|
140
|
+
description="Create a pull request (async)",
|
|
141
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
142
|
+
epilog="""
|
|
143
|
+
Examples:
|
|
144
|
+
agdt-create-pull-request --source-branch "feature/my-feature" --title "Add feature"
|
|
145
|
+
agdt-create-pull-request -b "feature/DFLY-1234" -t "feature(DFLY-1234): add feature" -d "Description"
|
|
146
|
+
|
|
147
|
+
# Or using state:
|
|
148
|
+
agdt-set source_branch "feature/my-feature"
|
|
149
|
+
agdt-set title "Add new feature"
|
|
150
|
+
agdt-create-pull-request
|
|
151
|
+
""",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--source-branch",
|
|
155
|
+
"-b",
|
|
156
|
+
type=str,
|
|
157
|
+
default=None,
|
|
158
|
+
help="Source branch name (falls back to source_branch state)",
|
|
159
|
+
)
|
|
160
|
+
parser.add_argument(
|
|
161
|
+
"--title",
|
|
162
|
+
"-t",
|
|
163
|
+
type=str,
|
|
164
|
+
default=None,
|
|
165
|
+
help="PR title (falls back to title state)",
|
|
166
|
+
)
|
|
167
|
+
parser.add_argument(
|
|
168
|
+
"--description",
|
|
169
|
+
"-d",
|
|
170
|
+
type=str,
|
|
171
|
+
default=None,
|
|
172
|
+
help="PR description (falls back to description state)",
|
|
173
|
+
)
|
|
174
|
+
args = parser.parse_args()
|
|
175
|
+
create_pull_request_async(
|
|
176
|
+
source_branch=args.source_branch,
|
|
177
|
+
title=args.title,
|
|
178
|
+
description=args.description,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def get_pull_request_threads_async() -> None:
|
|
183
|
+
"""
|
|
184
|
+
Get pull request threads asynchronously in the background.
|
|
185
|
+
|
|
186
|
+
State keys:
|
|
187
|
+
pull_request_id (required): PR ID
|
|
188
|
+
|
|
189
|
+
Usage:
|
|
190
|
+
agdt-set pull_request_id 12345
|
|
191
|
+
agdt-get-pull-request-threads
|
|
192
|
+
"""
|
|
193
|
+
task = run_function_in_background(
|
|
194
|
+
_COMMANDS_MODULE,
|
|
195
|
+
"get_pull_request_threads",
|
|
196
|
+
command_display_name="agdt-get-pull-request-threads",
|
|
197
|
+
)
|
|
198
|
+
print_task_tracking_info(task, "Getting pull request threads")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def reply_to_pull_request_thread_async() -> None:
|
|
202
|
+
"""
|
|
203
|
+
Reply to a pull request thread asynchronously in the background.
|
|
204
|
+
|
|
205
|
+
State keys:
|
|
206
|
+
pull_request_id (required): PR ID
|
|
207
|
+
thread_id (required): Thread ID
|
|
208
|
+
content (required): Reply content
|
|
209
|
+
|
|
210
|
+
Usage:
|
|
211
|
+
agdt-set pull_request_id 12345
|
|
212
|
+
agdt-set thread_id 67890
|
|
213
|
+
agdt-set content "Thanks for the review!"
|
|
214
|
+
agdt-reply-to-pull-request-thread
|
|
215
|
+
"""
|
|
216
|
+
task = run_function_in_background(
|
|
217
|
+
_COMMANDS_MODULE,
|
|
218
|
+
"reply_to_pull_request_thread",
|
|
219
|
+
command_display_name="agdt-reply-to-pull-request-thread",
|
|
220
|
+
)
|
|
221
|
+
print_task_tracking_info(task, "Replying to pull request thread")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def resolve_thread_async() -> None:
|
|
225
|
+
"""
|
|
226
|
+
Resolve a pull request thread asynchronously in the background.
|
|
227
|
+
|
|
228
|
+
State keys:
|
|
229
|
+
pull_request_id (required): PR ID
|
|
230
|
+
thread_id (required): Thread ID
|
|
231
|
+
|
|
232
|
+
Usage:
|
|
233
|
+
agdt-set pull_request_id 12345
|
|
234
|
+
agdt-set thread_id 67890
|
|
235
|
+
agdt-resolve-thread
|
|
236
|
+
"""
|
|
237
|
+
task = run_function_in_background(
|
|
238
|
+
_COMMANDS_MODULE,
|
|
239
|
+
"resolve_thread",
|
|
240
|
+
command_display_name="agdt-resolve-thread",
|
|
241
|
+
)
|
|
242
|
+
print_task_tracking_info(task, "Resolving pull request thread")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def mark_pull_request_draft_async() -> None:
|
|
246
|
+
"""
|
|
247
|
+
Mark a pull request as draft asynchronously in the background.
|
|
248
|
+
|
|
249
|
+
State keys:
|
|
250
|
+
pull_request_id (required): PR ID
|
|
251
|
+
|
|
252
|
+
Usage:
|
|
253
|
+
agdt-set pull_request_id 12345
|
|
254
|
+
agdt-mark-pull-request-draft
|
|
255
|
+
"""
|
|
256
|
+
task = run_function_in_background(
|
|
257
|
+
_COMMANDS_MODULE,
|
|
258
|
+
"mark_pull_request_draft",
|
|
259
|
+
command_display_name="agdt-mark-pull-request-draft",
|
|
260
|
+
)
|
|
261
|
+
print_task_tracking_info(task, "Marking pull request as draft")
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def publish_pull_request_async() -> None:
|
|
265
|
+
"""
|
|
266
|
+
Publish a pull request (remove draft status) asynchronously in the background.
|
|
267
|
+
|
|
268
|
+
State keys:
|
|
269
|
+
pull_request_id (required): PR ID
|
|
270
|
+
|
|
271
|
+
Usage:
|
|
272
|
+
agdt-set pull_request_id 12345
|
|
273
|
+
agdt-publish-pull-request
|
|
274
|
+
"""
|
|
275
|
+
task = run_function_in_background(
|
|
276
|
+
_COMMANDS_MODULE,
|
|
277
|
+
"publish_pull_request",
|
|
278
|
+
command_display_name="agdt-publish-pull-request",
|
|
279
|
+
)
|
|
280
|
+
print_task_tracking_info(task, "Publishing pull request")
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_pull_request_details_async() -> None:
|
|
284
|
+
"""
|
|
285
|
+
Get pull request details asynchronously in the background.
|
|
286
|
+
|
|
287
|
+
State keys:
|
|
288
|
+
pull_request_id (required): PR ID
|
|
289
|
+
|
|
290
|
+
Usage:
|
|
291
|
+
agdt-set pull_request_id 12345
|
|
292
|
+
agdt-get-pull-request-details
|
|
293
|
+
"""
|
|
294
|
+
task = run_function_in_background(
|
|
295
|
+
_PR_DETAILS_MODULE,
|
|
296
|
+
"get_pull_request_details",
|
|
297
|
+
command_display_name="agdt-get-pull-request-details",
|
|
298
|
+
)
|
|
299
|
+
print_task_tracking_info(task, "Getting pull request details")
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
# =============================================================================
|
|
303
|
+
# Pipeline Commands (Async)
|
|
304
|
+
# =============================================================================
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def run_e2e_tests_synapse_async() -> None:
|
|
308
|
+
"""
|
|
309
|
+
Run Synapse E2E tests pipeline asynchronously in the background.
|
|
310
|
+
|
|
311
|
+
State keys:
|
|
312
|
+
branch (required): Branch to test
|
|
313
|
+
e2e.stage: DEV or INT (default: DEV)
|
|
314
|
+
|
|
315
|
+
Usage:
|
|
316
|
+
agdt-set branch feature/my-branch
|
|
317
|
+
agdt-set e2e.stage DEV
|
|
318
|
+
agdt-run-e2e-tests-synapse
|
|
319
|
+
"""
|
|
320
|
+
task = run_function_in_background(
|
|
321
|
+
_PIPELINE_MODULE,
|
|
322
|
+
"run_e2e_tests_synapse",
|
|
323
|
+
command_display_name="agdt-run-e2e-tests-synapse",
|
|
324
|
+
)
|
|
325
|
+
print_task_tracking_info(task, "Running E2E tests pipeline (Synapse)")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def run_e2e_tests_fabric_async() -> None:
|
|
329
|
+
"""
|
|
330
|
+
Run Fabric E2E tests pipeline asynchronously in the background.
|
|
331
|
+
|
|
332
|
+
Note: Fabric tests only run in DEV (Fabric DAP is not deployed to INT).
|
|
333
|
+
|
|
334
|
+
State keys:
|
|
335
|
+
branch (required): Branch to test
|
|
336
|
+
|
|
337
|
+
Usage:
|
|
338
|
+
agdt-set branch feature/my-branch
|
|
339
|
+
agdt-run-e2e-tests-fabric
|
|
340
|
+
"""
|
|
341
|
+
task = run_function_in_background(
|
|
342
|
+
_PIPELINE_MODULE,
|
|
343
|
+
"run_e2e_tests_fabric",
|
|
344
|
+
command_display_name="agdt-run-e2e-tests-fabric",
|
|
345
|
+
)
|
|
346
|
+
print_task_tracking_info(task, "Running E2E tests pipeline (Fabric, DEV only)")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def run_wb_patch_async() -> None:
|
|
350
|
+
"""
|
|
351
|
+
Run workbench patch pipeline asynchronously in the background.
|
|
352
|
+
|
|
353
|
+
State keys:
|
|
354
|
+
workbench (required): Workbench identifier
|
|
355
|
+
|
|
356
|
+
Usage:
|
|
357
|
+
agdt-set workbench STND
|
|
358
|
+
agdt-run-wb-patch
|
|
359
|
+
"""
|
|
360
|
+
task = run_function_in_background(
|
|
361
|
+
_PIPELINE_MODULE,
|
|
362
|
+
"run_wb_patch",
|
|
363
|
+
command_display_name="agdt-run-wb-patch",
|
|
364
|
+
)
|
|
365
|
+
print_task_tracking_info(task, "Running workbench patch pipeline")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def get_run_details_async() -> None:
|
|
369
|
+
"""
|
|
370
|
+
Get pipeline run details asynchronously in the background.
|
|
371
|
+
|
|
372
|
+
State keys:
|
|
373
|
+
run_id (required): Pipeline run ID
|
|
374
|
+
pipeline_id (optional): Pipeline definition ID
|
|
375
|
+
fetch_logs (optional): If "true", fetch logs from failed tasks
|
|
376
|
+
vpn_toggle (optional): If "true", temporarily disconnect VPN when fetching logs
|
|
377
|
+
|
|
378
|
+
CLI args:
|
|
379
|
+
--fetch-logs: Fetch and save logs from failed tasks
|
|
380
|
+
--vpn-toggle: Temporarily disconnect VPN when fetching logs
|
|
381
|
+
--run-id: Override run_id from state
|
|
382
|
+
|
|
383
|
+
Usage:
|
|
384
|
+
agdt-set run_id 12345
|
|
385
|
+
agdt-get-run-details
|
|
386
|
+
agdt-get-run-details --fetch-logs
|
|
387
|
+
agdt-get-run-details --fetch-logs --vpn-toggle
|
|
388
|
+
"""
|
|
389
|
+
# Parse CLI args and store in state for background process
|
|
390
|
+
parser = argparse.ArgumentParser(description="Get run details", add_help=False)
|
|
391
|
+
parser.add_argument("--fetch-logs", action="store_true")
|
|
392
|
+
parser.add_argument("--vpn-toggle", action="store_true")
|
|
393
|
+
parser.add_argument("--run-id", type=int)
|
|
394
|
+
args, _ = parser.parse_known_args()
|
|
395
|
+
|
|
396
|
+
if args.fetch_logs:
|
|
397
|
+
set_value("fetch_logs", "true")
|
|
398
|
+
if args.vpn_toggle:
|
|
399
|
+
set_value("vpn_toggle", "true")
|
|
400
|
+
if args.run_id:
|
|
401
|
+
set_value("run_id", str(args.run_id))
|
|
402
|
+
|
|
403
|
+
task = run_function_in_background(
|
|
404
|
+
_RUN_DETAILS_MODULE,
|
|
405
|
+
"get_run_details",
|
|
406
|
+
command_display_name="agdt-get-run-details",
|
|
407
|
+
)
|
|
408
|
+
print_task_tracking_info(task, "Getting pipeline run details")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def wait_for_run_async() -> None:
|
|
412
|
+
"""
|
|
413
|
+
Wait for a pipeline run to complete asynchronously in the background.
|
|
414
|
+
|
|
415
|
+
Polls the run status until it finishes. Succeeds when run completes
|
|
416
|
+
(regardless of pipeline result). Only fails if unable to fetch
|
|
417
|
+
run details repeatedly.
|
|
418
|
+
|
|
419
|
+
State keys:
|
|
420
|
+
run_id (required): Pipeline run ID
|
|
421
|
+
poll_interval (optional): Seconds between polls (default: 30)
|
|
422
|
+
max_failures (optional): Max consecutive fetch failures (default: 3)
|
|
423
|
+
fetch_logs (optional): If "true", fetch logs from failed tasks
|
|
424
|
+
vpn_toggle (optional): If "true", temporarily disconnect VPN when fetching logs
|
|
425
|
+
|
|
426
|
+
CLI args:
|
|
427
|
+
--fetch-logs: Fetch and save logs from failed tasks
|
|
428
|
+
--vpn-toggle: Temporarily disconnect VPN when fetching logs
|
|
429
|
+
--run-id: Override run_id from state
|
|
430
|
+
--poll-interval: Override poll interval from state
|
|
431
|
+
|
|
432
|
+
Usage:
|
|
433
|
+
agdt-set run_id 12345
|
|
434
|
+
agdt-wait-for-run
|
|
435
|
+
agdt-wait-for-run --fetch-logs
|
|
436
|
+
agdt-wait-for-run --fetch-logs --vpn-toggle
|
|
437
|
+
agdt-task-wait
|
|
438
|
+
"""
|
|
439
|
+
# Parse CLI args and store in state for background process
|
|
440
|
+
parser = argparse.ArgumentParser(description="Wait for run", add_help=False)
|
|
441
|
+
parser.add_argument("--fetch-logs", action="store_true")
|
|
442
|
+
parser.add_argument("--vpn-toggle", action="store_true")
|
|
443
|
+
parser.add_argument("--run-id", type=int)
|
|
444
|
+
parser.add_argument("--poll-interval", type=int)
|
|
445
|
+
args, _ = parser.parse_known_args()
|
|
446
|
+
|
|
447
|
+
if args.fetch_logs:
|
|
448
|
+
set_value("fetch_logs", "true")
|
|
449
|
+
if args.vpn_toggle:
|
|
450
|
+
set_value("vpn_toggle", "true")
|
|
451
|
+
if args.run_id:
|
|
452
|
+
set_value("run_id", str(args.run_id))
|
|
453
|
+
if args.poll_interval:
|
|
454
|
+
set_value("poll_interval", str(args.poll_interval))
|
|
455
|
+
|
|
456
|
+
task = run_function_in_background(
|
|
457
|
+
_RUN_DETAILS_MODULE,
|
|
458
|
+
"wait_for_run",
|
|
459
|
+
command_display_name="agdt-wait-for-run",
|
|
460
|
+
)
|
|
461
|
+
print_task_tracking_info(task, "Waiting for pipeline run to complete")
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def list_pipelines_async() -> None:
|
|
465
|
+
"""
|
|
466
|
+
List Azure DevOps pipelines asynchronously in the background.
|
|
467
|
+
|
|
468
|
+
State keys:
|
|
469
|
+
pipeline.name_filter (optional): Name or prefix to filter (supports wildcards like "mgmt*")
|
|
470
|
+
|
|
471
|
+
Usage:
|
|
472
|
+
agdt-set pipeline.name_filter "mgmt*"
|
|
473
|
+
agdt-list-pipelines
|
|
474
|
+
"""
|
|
475
|
+
task = run_function_in_background(
|
|
476
|
+
_PIPELINE_MODULE,
|
|
477
|
+
"list_pipelines",
|
|
478
|
+
command_display_name="agdt-list-pipelines",
|
|
479
|
+
)
|
|
480
|
+
print_task_tracking_info(task, "Listing pipelines")
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def get_pipeline_id_async() -> None:
|
|
484
|
+
"""
|
|
485
|
+
Get a pipeline ID by name asynchronously in the background.
|
|
486
|
+
|
|
487
|
+
State keys:
|
|
488
|
+
pipeline.name (required): Exact name of the pipeline
|
|
489
|
+
|
|
490
|
+
Output:
|
|
491
|
+
Sets pipeline.id in state for use by subsequent commands.
|
|
492
|
+
|
|
493
|
+
Usage:
|
|
494
|
+
agdt-set pipeline.name "mgmt-e2e-tests"
|
|
495
|
+
agdt-get-pipeline-id
|
|
496
|
+
"""
|
|
497
|
+
task = run_function_in_background(
|
|
498
|
+
_PIPELINE_MODULE,
|
|
499
|
+
"get_pipeline_id",
|
|
500
|
+
command_display_name="agdt-get-pipeline-id",
|
|
501
|
+
)
|
|
502
|
+
print_task_tracking_info(task, "Getting pipeline ID")
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def create_pipeline_async() -> None:
|
|
506
|
+
"""
|
|
507
|
+
Create a new Azure DevOps pipeline asynchronously in the background.
|
|
508
|
+
|
|
509
|
+
State keys:
|
|
510
|
+
pipeline.name (required): Name for the new pipeline
|
|
511
|
+
pipeline.yaml_path (required): Path to YAML file in repo (e.g., "/mgmt-frontend/azure-pipelines/file.yml")
|
|
512
|
+
pipeline.description (optional): Description for the pipeline
|
|
513
|
+
pipeline.folder_path (optional): Folder to create pipeline in
|
|
514
|
+
pipeline.skip_first_run (optional): Skip first run (default: true)
|
|
515
|
+
branch (optional): Branch to associate with pipeline (default: main)
|
|
516
|
+
|
|
517
|
+
Output:
|
|
518
|
+
Sets pipeline.id in state with the created pipeline's ID.
|
|
519
|
+
|
|
520
|
+
Usage:
|
|
521
|
+
agdt-set pipeline.name "mgmt-e2e-tests-fabric"
|
|
522
|
+
agdt-set pipeline.yaml_path "/mgmt-frontend/azure-pipelines/azure-pipelines-e2e-tests-fabric.yml"
|
|
523
|
+
agdt-set pipeline.description "Fabric E2E tests pipeline"
|
|
524
|
+
agdt-create-pipeline
|
|
525
|
+
"""
|
|
526
|
+
task = run_function_in_background(
|
|
527
|
+
_PIPELINE_MODULE,
|
|
528
|
+
"create_pipeline",
|
|
529
|
+
command_display_name="agdt-create-pipeline",
|
|
530
|
+
)
|
|
531
|
+
print_task_tracking_info(task, "Creating pipeline")
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def update_pipeline_async() -> None:
|
|
535
|
+
"""
|
|
536
|
+
Update an existing Azure DevOps pipeline asynchronously in the background.
|
|
537
|
+
|
|
538
|
+
State keys:
|
|
539
|
+
pipeline.id (required): ID of pipeline to update (use dfly-get-pipeline-id first)
|
|
540
|
+
pipeline.new_name (optional): New name for the pipeline
|
|
541
|
+
pipeline.yaml_path (optional): New YAML file path
|
|
542
|
+
pipeline.new_folder_path (optional): New folder to move pipeline to
|
|
543
|
+
pipeline.description (optional): New description
|
|
544
|
+
|
|
545
|
+
At least one of new_name, yaml_path, new_folder_path, or description must be provided.
|
|
546
|
+
|
|
547
|
+
Usage (rename existing pipeline):
|
|
548
|
+
agdt-set pipeline.name "mgmt-e2e-tests"
|
|
549
|
+
agdt-get-pipeline-id # waits for ID
|
|
550
|
+
agdt-set pipeline.new_name "mgmt-e2e-tests-synapse"
|
|
551
|
+
agdt-set pipeline.yaml_path "/mgmt-frontend/azure-pipelines/azure-pipelines-e2e-tests-synapse.yml"
|
|
552
|
+
agdt-update-pipeline
|
|
553
|
+
"""
|
|
554
|
+
task = run_function_in_background(
|
|
555
|
+
_PIPELINE_MODULE,
|
|
556
|
+
"update_pipeline",
|
|
557
|
+
command_display_name="agdt-update-pipeline",
|
|
558
|
+
)
|
|
559
|
+
print_task_tracking_info(task, "Updating pipeline")
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
# =============================================================================
|
|
563
|
+
# File Review Commands (Async)
|
|
564
|
+
# =============================================================================
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _auto_advance_after_submission(
|
|
568
|
+
task_id: str,
|
|
569
|
+
file_path: str,
|
|
570
|
+
outcome: str,
|
|
571
|
+
) -> None:
|
|
572
|
+
"""
|
|
573
|
+
Handle auto-advancement after submitting a file review.
|
|
574
|
+
|
|
575
|
+
Marks the file as submission-pending in the queue, then prints
|
|
576
|
+
the next file prompt (or checks for failures).
|
|
577
|
+
|
|
578
|
+
Args:
|
|
579
|
+
task_id: Background task ID
|
|
580
|
+
file_path: Path of file being submitted
|
|
581
|
+
outcome: Review outcome ('Approve', 'Changes', 'Suggest')
|
|
582
|
+
"""
|
|
583
|
+
from .file_review_commands import (
|
|
584
|
+
mark_file_as_submission_pending,
|
|
585
|
+
print_next_file_prompt,
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
pr_id = get_value("pull_request_id")
|
|
589
|
+
if not pr_id:
|
|
590
|
+
return
|
|
591
|
+
|
|
592
|
+
pr_id_int = int(pr_id)
|
|
593
|
+
|
|
594
|
+
# Mark file as submission-pending
|
|
595
|
+
mark_file_as_submission_pending(pr_id_int, file_path, task_id, outcome)
|
|
596
|
+
|
|
597
|
+
# Print the next file prompt
|
|
598
|
+
print_next_file_prompt(pr_id_int)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def approve_file_async(
|
|
602
|
+
file_path: Optional[str] = None,
|
|
603
|
+
content: Optional[str] = None,
|
|
604
|
+
pull_request_id: Optional[int] = None,
|
|
605
|
+
) -> None:
|
|
606
|
+
"""
|
|
607
|
+
Approve a file in a pull request asynchronously in the background.
|
|
608
|
+
|
|
609
|
+
After spawning the background task, immediately marks the file as
|
|
610
|
+
submission-pending and shows the next file to review.
|
|
611
|
+
|
|
612
|
+
Args:
|
|
613
|
+
file_path: Path of file to approve (overrides state)
|
|
614
|
+
content: Approval comment content (overrides state)
|
|
615
|
+
pull_request_id: PR ID (overrides state)
|
|
616
|
+
|
|
617
|
+
State keys (used as fallbacks):
|
|
618
|
+
pull_request_id (required): PR ID
|
|
619
|
+
file_review.file_path (required): Path of file to approve
|
|
620
|
+
content (required): Approval comment
|
|
621
|
+
|
|
622
|
+
Usage:
|
|
623
|
+
agdt-approve-file --file-path "src/app/component.ts" --content "LGTM"
|
|
624
|
+
|
|
625
|
+
# Or using state:
|
|
626
|
+
agdt-set pull_request_id 12345
|
|
627
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
628
|
+
agdt-set content "LGTM"
|
|
629
|
+
agdt-approve-file
|
|
630
|
+
"""
|
|
631
|
+
# Store CLI args in state if provided
|
|
632
|
+
_set_value_if_provided("file_review.file_path", file_path)
|
|
633
|
+
_set_value_if_provided("content", content)
|
|
634
|
+
if pull_request_id is not None:
|
|
635
|
+
set_value("pull_request_id", pull_request_id)
|
|
636
|
+
|
|
637
|
+
# Validate required values
|
|
638
|
+
_require_value("pull_request_id", "agdt-approve-file --pull-request-id 12345")
|
|
639
|
+
resolved_file_path = _require_value("file_review.file_path", 'agdt-approve-file --file-path "path/to/file"')
|
|
640
|
+
_require_value("content", 'agdt-approve-file --content "Approval comment"')
|
|
641
|
+
|
|
642
|
+
task = run_function_in_background(
|
|
643
|
+
_FILE_REVIEW_MODULE,
|
|
644
|
+
"approve_file",
|
|
645
|
+
command_display_name="agdt-approve-file",
|
|
646
|
+
)
|
|
647
|
+
print_task_tracking_info(task, f"Approving file: {resolved_file_path}")
|
|
648
|
+
|
|
649
|
+
# Auto-advance: mark as submission-pending and show next file
|
|
650
|
+
_auto_advance_after_submission(task.id, resolved_file_path, "Approve")
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def approve_file_async_cli() -> None:
|
|
654
|
+
"""CLI entry point for approve_file_async with argument parsing."""
|
|
655
|
+
parser = argparse.ArgumentParser(
|
|
656
|
+
description="Approve a file in a pull request review (async)",
|
|
657
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
658
|
+
epilog="""
|
|
659
|
+
Examples:
|
|
660
|
+
agdt-approve-file --file-path "src/app/component.ts" --content "LGTM"
|
|
661
|
+
agdt-approve-file --pull-request-id 12345 --file-path "src/app/component.ts" --content "Approved"
|
|
662
|
+
|
|
663
|
+
# Or using state:
|
|
664
|
+
agdt-set pull_request_id 12345
|
|
665
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
666
|
+
agdt-set content "LGTM"
|
|
667
|
+
agdt-approve-file
|
|
668
|
+
""",
|
|
669
|
+
)
|
|
670
|
+
parser.add_argument(
|
|
671
|
+
"--file-path",
|
|
672
|
+
"-f",
|
|
673
|
+
type=str,
|
|
674
|
+
default=None,
|
|
675
|
+
help="Path of file to approve (falls back to file_review.file_path state)",
|
|
676
|
+
)
|
|
677
|
+
parser.add_argument(
|
|
678
|
+
"--content",
|
|
679
|
+
"-c",
|
|
680
|
+
type=str,
|
|
681
|
+
default=None,
|
|
682
|
+
help="Approval comment content (falls back to content state)",
|
|
683
|
+
)
|
|
684
|
+
parser.add_argument(
|
|
685
|
+
"--pull-request-id",
|
|
686
|
+
"-p",
|
|
687
|
+
type=int,
|
|
688
|
+
default=None,
|
|
689
|
+
help="Pull request ID (falls back to pull_request_id state)",
|
|
690
|
+
)
|
|
691
|
+
args = parser.parse_args()
|
|
692
|
+
approve_file_async(
|
|
693
|
+
file_path=args.file_path,
|
|
694
|
+
content=args.content,
|
|
695
|
+
pull_request_id=args.pull_request_id,
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def submit_file_review_async() -> None:
|
|
700
|
+
"""
|
|
701
|
+
Submit a file review asynchronously in the background.
|
|
702
|
+
|
|
703
|
+
State keys:
|
|
704
|
+
pull_request_id (required): PR ID
|
|
705
|
+
file_path (required): Path of file
|
|
706
|
+
content (required): Review content
|
|
707
|
+
|
|
708
|
+
Usage:
|
|
709
|
+
agdt-set pull_request_id 12345
|
|
710
|
+
agdt-set file_path "src/app/component.ts"
|
|
711
|
+
agdt-set content "Review comments..."
|
|
712
|
+
agdt-submit-file-review
|
|
713
|
+
"""
|
|
714
|
+
task = run_function_in_background(
|
|
715
|
+
_FILE_REVIEW_MODULE,
|
|
716
|
+
"submit_file_review",
|
|
717
|
+
command_display_name="agdt-submit-file-review",
|
|
718
|
+
)
|
|
719
|
+
print_task_tracking_info(task, "Submitting file review")
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
def request_changes_async(
|
|
723
|
+
file_path: Optional[str] = None,
|
|
724
|
+
content: Optional[str] = None,
|
|
725
|
+
line: Optional[int] = None,
|
|
726
|
+
pull_request_id: Optional[int] = None,
|
|
727
|
+
) -> None:
|
|
728
|
+
"""
|
|
729
|
+
Request changes on a file asynchronously in the background.
|
|
730
|
+
|
|
731
|
+
After spawning the background task, immediately marks the file as
|
|
732
|
+
submission-pending and shows the next file to review.
|
|
733
|
+
|
|
734
|
+
Args:
|
|
735
|
+
file_path: Path of file (overrides state)
|
|
736
|
+
content: Change request content (overrides state)
|
|
737
|
+
line: Line number for comment (overrides state)
|
|
738
|
+
pull_request_id: PR ID (overrides state)
|
|
739
|
+
|
|
740
|
+
State keys (used as fallbacks):
|
|
741
|
+
pull_request_id (required): PR ID
|
|
742
|
+
file_review.file_path (required): Path of file
|
|
743
|
+
content (required): Change request content
|
|
744
|
+
line (required): Line number for comment
|
|
745
|
+
|
|
746
|
+
Usage:
|
|
747
|
+
agdt-request-changes --file-path "src/app/component.ts" --content "Issue here" --line 42
|
|
748
|
+
|
|
749
|
+
# Or using state:
|
|
750
|
+
agdt-set pull_request_id 12345
|
|
751
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
752
|
+
agdt-set content "Please fix this issue..."
|
|
753
|
+
agdt-set line 42
|
|
754
|
+
agdt-request-changes
|
|
755
|
+
"""
|
|
756
|
+
# Store CLI args in state if provided
|
|
757
|
+
_set_value_if_provided("file_review.file_path", file_path)
|
|
758
|
+
_set_value_if_provided("content", content)
|
|
759
|
+
if line is not None:
|
|
760
|
+
set_value("line", line)
|
|
761
|
+
if pull_request_id is not None:
|
|
762
|
+
set_value("pull_request_id", pull_request_id)
|
|
763
|
+
|
|
764
|
+
# Validate required values
|
|
765
|
+
_require_value("pull_request_id", "agdt-request-changes --pull-request-id 12345")
|
|
766
|
+
resolved_file_path = _require_value("file_review.file_path", 'agdt-request-changes --file-path "path/to/file"')
|
|
767
|
+
_require_value("content", 'agdt-request-changes --content "Issue description"')
|
|
768
|
+
_require_value("line", "agdt-request-changes --line 42")
|
|
769
|
+
|
|
770
|
+
task = run_function_in_background(
|
|
771
|
+
_FILE_REVIEW_MODULE,
|
|
772
|
+
"request_changes",
|
|
773
|
+
command_display_name="agdt-request-changes",
|
|
774
|
+
)
|
|
775
|
+
print_task_tracking_info(task, f"Requesting changes on file: {resolved_file_path}")
|
|
776
|
+
|
|
777
|
+
# Auto-advance: mark as submission-pending and show next file
|
|
778
|
+
_auto_advance_after_submission(task.id, resolved_file_path, "Changes")
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def request_changes_async_cli() -> None:
|
|
782
|
+
"""CLI entry point for request_changes_async with argument parsing."""
|
|
783
|
+
parser = argparse.ArgumentParser(
|
|
784
|
+
description="Request changes on a file in a pull request review (async)",
|
|
785
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
786
|
+
epilog="""
|
|
787
|
+
Examples:
|
|
788
|
+
agdt-request-changes --file-path "src/app/component.ts" --content "Fix this" --line 42
|
|
789
|
+
agdt-request-changes -f "src/main.py" -c "Issue description" -l 100
|
|
790
|
+
|
|
791
|
+
# Or using state:
|
|
792
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
793
|
+
agdt-set content "Please fix this issue..."
|
|
794
|
+
agdt-set line 42
|
|
795
|
+
agdt-request-changes
|
|
796
|
+
""",
|
|
797
|
+
)
|
|
798
|
+
parser.add_argument(
|
|
799
|
+
"--file-path",
|
|
800
|
+
"-f",
|
|
801
|
+
type=str,
|
|
802
|
+
default=None,
|
|
803
|
+
help="Path of file (falls back to file_review.file_path state)",
|
|
804
|
+
)
|
|
805
|
+
parser.add_argument(
|
|
806
|
+
"--content",
|
|
807
|
+
"-c",
|
|
808
|
+
type=str,
|
|
809
|
+
default=None,
|
|
810
|
+
help="Change request content (falls back to content state)",
|
|
811
|
+
)
|
|
812
|
+
parser.add_argument(
|
|
813
|
+
"--line",
|
|
814
|
+
"-l",
|
|
815
|
+
type=int,
|
|
816
|
+
default=None,
|
|
817
|
+
help="Line number for comment (falls back to line state)",
|
|
818
|
+
)
|
|
819
|
+
parser.add_argument(
|
|
820
|
+
"--pull-request-id",
|
|
821
|
+
"-p",
|
|
822
|
+
type=int,
|
|
823
|
+
default=None,
|
|
824
|
+
help="Pull request ID (falls back to pull_request_id state)",
|
|
825
|
+
)
|
|
826
|
+
args = parser.parse_args()
|
|
827
|
+
request_changes_async(
|
|
828
|
+
file_path=args.file_path,
|
|
829
|
+
content=args.content,
|
|
830
|
+
line=args.line,
|
|
831
|
+
pull_request_id=args.pull_request_id,
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def request_changes_with_suggestion_async(
|
|
836
|
+
file_path: Optional[str] = None,
|
|
837
|
+
content: Optional[str] = None,
|
|
838
|
+
line: Optional[int] = None,
|
|
839
|
+
pull_request_id: Optional[int] = None,
|
|
840
|
+
) -> None:
|
|
841
|
+
"""
|
|
842
|
+
Request changes with a code suggestion asynchronously in the background.
|
|
843
|
+
|
|
844
|
+
After spawning the background task, immediately marks the file as
|
|
845
|
+
submission-pending and shows the next file to review.
|
|
846
|
+
|
|
847
|
+
Args:
|
|
848
|
+
file_path: Path of file (overrides state)
|
|
849
|
+
content: Change request with code suggestion (overrides state)
|
|
850
|
+
line: Line number for comment (overrides state)
|
|
851
|
+
pull_request_id: PR ID (overrides state)
|
|
852
|
+
|
|
853
|
+
State keys (used as fallbacks):
|
|
854
|
+
pull_request_id (required): PR ID
|
|
855
|
+
file_review.file_path (required): Path of file
|
|
856
|
+
content (required): Change request with suggestion
|
|
857
|
+
line (required): Line number for comment
|
|
858
|
+
|
|
859
|
+
Usage:
|
|
860
|
+
agdt-request-changes-with-suggestion --file-path "src/app/component.ts" --content "```suggestion
|
|
861
|
+
const x = 1;
|
|
862
|
+
```" --line 42
|
|
863
|
+
|
|
864
|
+
# Or using state:
|
|
865
|
+
agdt-set pull_request_id 12345
|
|
866
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
867
|
+
agdt-set content "Suggested change..."
|
|
868
|
+
agdt-set line 42
|
|
869
|
+
agdt-request-changes-with-suggestion
|
|
870
|
+
"""
|
|
871
|
+
# Store CLI args in state if provided
|
|
872
|
+
_set_value_if_provided("file_review.file_path", file_path)
|
|
873
|
+
_set_value_if_provided("content", content)
|
|
874
|
+
if line is not None:
|
|
875
|
+
set_value("line", line)
|
|
876
|
+
if pull_request_id is not None:
|
|
877
|
+
set_value("pull_request_id", pull_request_id)
|
|
878
|
+
|
|
879
|
+
# Validate required values
|
|
880
|
+
_require_value("pull_request_id", "agdt-request-changes-with-suggestion --pull-request-id 12345")
|
|
881
|
+
resolved_file_path = _require_value(
|
|
882
|
+
"file_review.file_path", 'agdt-request-changes-with-suggestion --file-path "path/to/file"'
|
|
883
|
+
)
|
|
884
|
+
_require_value("content", 'agdt-request-changes-with-suggestion --content "Suggestion"')
|
|
885
|
+
_require_value("line", "agdt-request-changes-with-suggestion --line 42")
|
|
886
|
+
|
|
887
|
+
task = run_function_in_background(
|
|
888
|
+
_FILE_REVIEW_MODULE,
|
|
889
|
+
"request_changes_with_suggestion",
|
|
890
|
+
command_display_name="agdt-request-changes-with-suggestion",
|
|
891
|
+
)
|
|
892
|
+
print_task_tracking_info(task, f"Requesting changes with suggestion on: {resolved_file_path}")
|
|
893
|
+
|
|
894
|
+
# Auto-advance: mark as submission-pending and show next file
|
|
895
|
+
_auto_advance_after_submission(task.id, resolved_file_path, "Suggest")
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def request_changes_with_suggestion_async_cli() -> None:
|
|
899
|
+
"""CLI entry point for request_changes_with_suggestion_async with argument parsing."""
|
|
900
|
+
parser = argparse.ArgumentParser(
|
|
901
|
+
description="Request changes with code suggestion (async)",
|
|
902
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
903
|
+
epilog="""
|
|
904
|
+
Examples:
|
|
905
|
+
agdt-request-changes-with-suggestion --file-path "src/app/component.ts" --content "```suggestion
|
|
906
|
+
const x = 1;
|
|
907
|
+
```" --line 42
|
|
908
|
+
|
|
909
|
+
# Or using state:
|
|
910
|
+
agdt-set file_review.file_path "src/app/component.ts"
|
|
911
|
+
agdt-set content "Suggested change..."
|
|
912
|
+
agdt-set line 42
|
|
913
|
+
agdt-request-changes-with-suggestion
|
|
914
|
+
""",
|
|
915
|
+
)
|
|
916
|
+
parser.add_argument(
|
|
917
|
+
"--file-path",
|
|
918
|
+
"-f",
|
|
919
|
+
type=str,
|
|
920
|
+
default=None,
|
|
921
|
+
help="Path of file (falls back to file_review.file_path state)",
|
|
922
|
+
)
|
|
923
|
+
parser.add_argument(
|
|
924
|
+
"--content",
|
|
925
|
+
"-c",
|
|
926
|
+
type=str,
|
|
927
|
+
default=None,
|
|
928
|
+
help="Change request with code suggestion (falls back to content state)",
|
|
929
|
+
)
|
|
930
|
+
parser.add_argument(
|
|
931
|
+
"--line",
|
|
932
|
+
"-l",
|
|
933
|
+
type=int,
|
|
934
|
+
default=None,
|
|
935
|
+
help="Line number for comment (falls back to line state)",
|
|
936
|
+
)
|
|
937
|
+
parser.add_argument(
|
|
938
|
+
"--pull-request-id",
|
|
939
|
+
"-p",
|
|
940
|
+
type=int,
|
|
941
|
+
default=None,
|
|
942
|
+
help="Pull request ID (falls back to pull_request_id state)",
|
|
943
|
+
)
|
|
944
|
+
args = parser.parse_args()
|
|
945
|
+
request_changes_with_suggestion_async(
|
|
946
|
+
file_path=args.file_path,
|
|
947
|
+
content=args.content,
|
|
948
|
+
line=args.line,
|
|
949
|
+
pull_request_id=args.pull_request_id,
|
|
950
|
+
)
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
def mark_file_reviewed_async() -> None:
|
|
954
|
+
"""
|
|
955
|
+
Mark a file as reviewed asynchronously in the background.
|
|
956
|
+
|
|
957
|
+
State keys:
|
|
958
|
+
pull_request_id (required): PR ID
|
|
959
|
+
file_path (required): Path of file
|
|
960
|
+
|
|
961
|
+
Usage:
|
|
962
|
+
agdt-set pull_request_id 12345
|
|
963
|
+
agdt-set file_path "src/app/component.ts"
|
|
964
|
+
agdt-mark-file-reviewed
|
|
965
|
+
"""
|
|
966
|
+
task = run_function_in_background(
|
|
967
|
+
_MARK_REVIEWED_MODULE,
|
|
968
|
+
"mark_file_reviewed_cli",
|
|
969
|
+
command_display_name="agdt-mark-file-reviewed",
|
|
970
|
+
)
|
|
971
|
+
print_task_tracking_info(task, "Marking file as reviewed")
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
# =============================================================================
|
|
975
|
+
# Review Workflow Commands (Async)
|
|
976
|
+
# =============================================================================
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def checkout_and_sync_branch_async() -> None:
|
|
980
|
+
"""
|
|
981
|
+
Checkout PR source branch and sync with main asynchronously.
|
|
982
|
+
|
|
983
|
+
This function:
|
|
984
|
+
1. Loads PR details from temp file to get source branch
|
|
985
|
+
2. Checkouts the source branch
|
|
986
|
+
3. Fetches and rebases onto origin/main
|
|
987
|
+
4. Saves files changed on branch to JSON for later use
|
|
988
|
+
|
|
989
|
+
State keys:
|
|
990
|
+
pull_request_id (required): PR ID
|
|
991
|
+
|
|
992
|
+
Usage:
|
|
993
|
+
agdt-set pull_request_id 12345
|
|
994
|
+
# Then called internally by workflow
|
|
995
|
+
"""
|
|
996
|
+
from pathlib import Path
|
|
997
|
+
|
|
998
|
+
# Get PR ID from state
|
|
999
|
+
pr_id = get_value("pull_request_id")
|
|
1000
|
+
if not pr_id:
|
|
1001
|
+
print("Error: pull_request_id is required in state", file=sys.stderr)
|
|
1002
|
+
sys.exit(1)
|
|
1003
|
+
|
|
1004
|
+
# Load PR details to get source branch
|
|
1005
|
+
scripts_dir = Path(__file__).parent.parent.parent.parent.parent
|
|
1006
|
+
temp_dir = scripts_dir / "temp"
|
|
1007
|
+
details_path = temp_dir / "temp-get-pull-request-details-response.json"
|
|
1008
|
+
|
|
1009
|
+
if not details_path.exists():
|
|
1010
|
+
print(f"Error: PR details file not found: {details_path}", file=sys.stderr)
|
|
1011
|
+
print("Run get_pull_request_details first.", file=sys.stderr)
|
|
1012
|
+
sys.exit(1)
|
|
1013
|
+
|
|
1014
|
+
import json
|
|
1015
|
+
|
|
1016
|
+
with open(details_path, encoding="utf-8") as f:
|
|
1017
|
+
pr_details = json.load(f)
|
|
1018
|
+
|
|
1019
|
+
pr_info = pr_details.get("pullRequest", pr_details)
|
|
1020
|
+
source_branch = pr_info.get("sourceRefName", "").replace("refs/heads/", "")
|
|
1021
|
+
|
|
1022
|
+
if not source_branch:
|
|
1023
|
+
print("Error: Could not determine source branch from PR details", file=sys.stderr)
|
|
1024
|
+
sys.exit(1)
|
|
1025
|
+
|
|
1026
|
+
# Run checkout and sync in background
|
|
1027
|
+
task = run_function_in_background(
|
|
1028
|
+
_REVIEW_MODULE,
|
|
1029
|
+
"checkout_and_sync_branch",
|
|
1030
|
+
source_branch,
|
|
1031
|
+
int(pr_id),
|
|
1032
|
+
True, # save_files_on_branch=True
|
|
1033
|
+
command_display_name="checkout-and-sync-branch",
|
|
1034
|
+
)
|
|
1035
|
+
print_task_tracking_info(task, f"Checking out branch '{source_branch}' and syncing with main")
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
def generate_review_prompts_async() -> None:
|
|
1039
|
+
"""
|
|
1040
|
+
Generate review prompts and queue.json asynchronously.
|
|
1041
|
+
|
|
1042
|
+
This function:
|
|
1043
|
+
1. Loads PR details from temp file
|
|
1044
|
+
2. Loads files_on_branch from JSON (if available)
|
|
1045
|
+
3. Generates queue.json and individual file prompts
|
|
1046
|
+
4. Initializes the workflow state
|
|
1047
|
+
|
|
1048
|
+
State keys:
|
|
1049
|
+
pull_request_id (required): PR ID
|
|
1050
|
+
|
|
1051
|
+
Usage:
|
|
1052
|
+
agdt-set pull_request_id 12345
|
|
1053
|
+
# Then called internally by workflow
|
|
1054
|
+
"""
|
|
1055
|
+
# Get PR ID from state
|
|
1056
|
+
pr_id = get_value("pull_request_id")
|
|
1057
|
+
if not pr_id:
|
|
1058
|
+
print("Error: pull_request_id is required in state", file=sys.stderr)
|
|
1059
|
+
sys.exit(1)
|
|
1060
|
+
|
|
1061
|
+
# Run generate prompts in background
|
|
1062
|
+
task = run_function_in_background(
|
|
1063
|
+
_REVIEW_MODULE,
|
|
1064
|
+
"generate_review_prompts",
|
|
1065
|
+
int(pr_id),
|
|
1066
|
+
None, # pr_details - will be loaded from file
|
|
1067
|
+
False, # include_reviewed
|
|
1068
|
+
None, # files_on_branch - will be loaded from file
|
|
1069
|
+
command_display_name="generate-review-prompts",
|
|
1070
|
+
)
|
|
1071
|
+
print_task_tracking_info(task, "Generating review prompts and queue")
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
def setup_pull_request_review_async(
|
|
1075
|
+
pull_request_id: Optional[int] = None,
|
|
1076
|
+
jira_issue_key: Optional[str] = None,
|
|
1077
|
+
) -> None:
|
|
1078
|
+
"""
|
|
1079
|
+
Set up a pull request review asynchronously in the background.
|
|
1080
|
+
|
|
1081
|
+
This is the main entry point for initiating a PR review workflow.
|
|
1082
|
+
It orchestrates the complete setup process:
|
|
1083
|
+
1. Fetches PR details
|
|
1084
|
+
2. Fetches Jira issue details (if key provided)
|
|
1085
|
+
3. Checkouts source branch and syncs with main
|
|
1086
|
+
4. Generates queue.json and file prompts
|
|
1087
|
+
5. Initializes workflow state
|
|
1088
|
+
|
|
1089
|
+
Args:
|
|
1090
|
+
pull_request_id: PR ID (uses state if not provided)
|
|
1091
|
+
jira_issue_key: Optional Jira issue key (uses state if not provided)
|
|
1092
|
+
|
|
1093
|
+
State keys:
|
|
1094
|
+
pull_request_id (required): PR ID
|
|
1095
|
+
jira.issue_key (optional): Jira issue key
|
|
1096
|
+
|
|
1097
|
+
Usage:
|
|
1098
|
+
agdt-set pull_request_id 12345
|
|
1099
|
+
agdt-set jira.issue_key DFLY-1234
|
|
1100
|
+
# Then called internally by agdt-initiate-pull-request-review-workflow
|
|
1101
|
+
"""
|
|
1102
|
+
# Get PR ID from parameter or state
|
|
1103
|
+
pr_id = pull_request_id
|
|
1104
|
+
if pr_id is None:
|
|
1105
|
+
pr_id_str = get_value("pull_request_id")
|
|
1106
|
+
if not pr_id_str:
|
|
1107
|
+
print("Error: pull_request_id is required", file=sys.stderr)
|
|
1108
|
+
sys.exit(1)
|
|
1109
|
+
pr_id = int(pr_id_str)
|
|
1110
|
+
|
|
1111
|
+
# Get Jira issue key from parameter or state
|
|
1112
|
+
jira_key = jira_issue_key
|
|
1113
|
+
if jira_key is None:
|
|
1114
|
+
jira_key = get_value("jira.issue_key")
|
|
1115
|
+
|
|
1116
|
+
# Ensure values are in state for the background function
|
|
1117
|
+
set_value("pull_request_id", pr_id)
|
|
1118
|
+
if jira_key:
|
|
1119
|
+
set_value("jira.issue_key", jira_key)
|
|
1120
|
+
|
|
1121
|
+
# Run setup in background
|
|
1122
|
+
task = run_function_in_background(
|
|
1123
|
+
_REVIEW_MODULE,
|
|
1124
|
+
"setup_pull_request_review",
|
|
1125
|
+
command_display_name="setup-pull-request-review",
|
|
1126
|
+
)
|
|
1127
|
+
print_task_tracking_info(task, f"Setting up PR #{pr_id} review workflow")
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
def generate_pr_summary_async() -> None:
|
|
1131
|
+
"""
|
|
1132
|
+
Generate PR summary asynchronously in the background.
|
|
1133
|
+
|
|
1134
|
+
State keys:
|
|
1135
|
+
pull_request_id (required): PR ID
|
|
1136
|
+
|
|
1137
|
+
Usage:
|
|
1138
|
+
agdt-set pull_request_id 12345
|
|
1139
|
+
agdt-generate-pr-summary
|
|
1140
|
+
"""
|
|
1141
|
+
task = run_function_in_background(
|
|
1142
|
+
_PR_SUMMARY_MODULE,
|
|
1143
|
+
"generate_overarching_pr_comments_cli",
|
|
1144
|
+
command_display_name="agdt-generate-pr-summary",
|
|
1145
|
+
)
|
|
1146
|
+
print_task_tracking_info(task, "Generating PR summary")
|
|
1147
|
+
|
|
1148
|
+
|
|
1149
|
+
# =============================================================================
|
|
1150
|
+
# Cross-Context Lookup Functions (for context switching)
|
|
1151
|
+
# =============================================================================
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
def lookup_jira_issue_from_pr_async(pull_request_id: int) -> None:
|
|
1155
|
+
"""
|
|
1156
|
+
Look up Jira issue key from a PR and save to state.
|
|
1157
|
+
|
|
1158
|
+
Searches for Jira issue key (e.g., DFLY-1234) in:
|
|
1159
|
+
1. PR source branch name (e.g., feature/DFLY-1234/my-feature)
|
|
1160
|
+
2. PR title
|
|
1161
|
+
3. PR description
|
|
1162
|
+
|
|
1163
|
+
This is designed for background execution and silently saves jira.issue_key if found.
|
|
1164
|
+
|
|
1165
|
+
Args:
|
|
1166
|
+
pull_request_id: PR ID to look up
|
|
1167
|
+
"""
|
|
1168
|
+
from .helpers import find_jira_issue_from_pr
|
|
1169
|
+
|
|
1170
|
+
try:
|
|
1171
|
+
issue_key = find_jira_issue_from_pr(pull_request_id)
|
|
1172
|
+
|
|
1173
|
+
if issue_key:
|
|
1174
|
+
# Only set if not already set (avoid overwriting user intent)
|
|
1175
|
+
current = get_value("jira.issue_key")
|
|
1176
|
+
if not current:
|
|
1177
|
+
set_value("jira.issue_key", issue_key)
|
|
1178
|
+
print(f"✓ Found Jira issue {issue_key} from PR #{pull_request_id}")
|
|
1179
|
+
return
|
|
1180
|
+
|
|
1181
|
+
print(f"ℹ️ No Jira issue key found in PR #{pull_request_id}")
|
|
1182
|
+
|
|
1183
|
+
except Exception as e:
|
|
1184
|
+
# Silently fail - this is a background enhancement, not critical
|
|
1185
|
+
print(f"⚠️ Could not look up Jira issue from PR: {e}")
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
def lookup_pr_from_jira_issue_async(issue_key: str) -> None:
|
|
1189
|
+
"""
|
|
1190
|
+
Look up active PR from a Jira issue key and save to state.
|
|
1191
|
+
|
|
1192
|
+
Searches multiple sources in order of reliability:
|
|
1193
|
+
1. Jira issue comments/description for PR links (e.g., "PR: #1234")
|
|
1194
|
+
2. Azure DevOps PRs where issue key appears in branch/title/description
|
|
1195
|
+
|
|
1196
|
+
This is designed for background execution and silently saves pull_request_id if found.
|
|
1197
|
+
|
|
1198
|
+
Args:
|
|
1199
|
+
issue_key: Jira issue key (e.g., "DFLY-1234")
|
|
1200
|
+
"""
|
|
1201
|
+
from .helpers import find_pr_from_jira_issue
|
|
1202
|
+
|
|
1203
|
+
try:
|
|
1204
|
+
pr_id = find_pr_from_jira_issue(issue_key)
|
|
1205
|
+
|
|
1206
|
+
if pr_id:
|
|
1207
|
+
# Only set if not already set (avoid overwriting user intent)
|
|
1208
|
+
current = get_value("pull_request_id")
|
|
1209
|
+
if not current:
|
|
1210
|
+
set_value("pull_request_id", str(pr_id))
|
|
1211
|
+
print(f"✓ Found PR #{pr_id} for Jira issue {issue_key}")
|
|
1212
|
+
return
|
|
1213
|
+
|
|
1214
|
+
print(f"ℹ️ No active PR found for Jira issue {issue_key}")
|
|
1215
|
+
|
|
1216
|
+
except Exception as e:
|
|
1217
|
+
# Silently fail - this is a background enhancement, not critical
|
|
1218
|
+
print(f"⚠️ Could not look up PR from Jira issue: {e}")
|