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.
Files changed (92) hide show
  1. agdt_ai_helpers/__init__.py +34 -0
  2. agentic_devtools/__init__.py +8 -0
  3. agentic_devtools/background_tasks.py +598 -0
  4. agentic_devtools/cli/__init__.py +1 -0
  5. agentic_devtools/cli/azure_devops/__init__.py +222 -0
  6. agentic_devtools/cli/azure_devops/async_commands.py +1218 -0
  7. agentic_devtools/cli/azure_devops/auth.py +34 -0
  8. agentic_devtools/cli/azure_devops/commands.py +728 -0
  9. agentic_devtools/cli/azure_devops/config.py +49 -0
  10. agentic_devtools/cli/azure_devops/file_review_commands.py +1038 -0
  11. agentic_devtools/cli/azure_devops/helpers.py +561 -0
  12. agentic_devtools/cli/azure_devops/mark_reviewed.py +756 -0
  13. agentic_devtools/cli/azure_devops/pipeline_commands.py +724 -0
  14. agentic_devtools/cli/azure_devops/pr_summary_commands.py +579 -0
  15. agentic_devtools/cli/azure_devops/pull_request_details_commands.py +596 -0
  16. agentic_devtools/cli/azure_devops/review_commands.py +700 -0
  17. agentic_devtools/cli/azure_devops/review_helpers.py +191 -0
  18. agentic_devtools/cli/azure_devops/review_jira.py +308 -0
  19. agentic_devtools/cli/azure_devops/review_prompts.py +263 -0
  20. agentic_devtools/cli/azure_devops/run_details_commands.py +935 -0
  21. agentic_devtools/cli/azure_devops/vpn_toggle.py +1220 -0
  22. agentic_devtools/cli/git/__init__.py +91 -0
  23. agentic_devtools/cli/git/async_commands.py +294 -0
  24. agentic_devtools/cli/git/commands.py +399 -0
  25. agentic_devtools/cli/git/core.py +152 -0
  26. agentic_devtools/cli/git/diff.py +210 -0
  27. agentic_devtools/cli/git/operations.py +737 -0
  28. agentic_devtools/cli/jira/__init__.py +114 -0
  29. agentic_devtools/cli/jira/adf.py +105 -0
  30. agentic_devtools/cli/jira/async_commands.py +439 -0
  31. agentic_devtools/cli/jira/async_status.py +27 -0
  32. agentic_devtools/cli/jira/commands.py +28 -0
  33. agentic_devtools/cli/jira/comment_commands.py +141 -0
  34. agentic_devtools/cli/jira/config.py +69 -0
  35. agentic_devtools/cli/jira/create_commands.py +293 -0
  36. agentic_devtools/cli/jira/formatting.py +131 -0
  37. agentic_devtools/cli/jira/get_commands.py +287 -0
  38. agentic_devtools/cli/jira/helpers.py +278 -0
  39. agentic_devtools/cli/jira/parse_error_report.py +352 -0
  40. agentic_devtools/cli/jira/role_commands.py +560 -0
  41. agentic_devtools/cli/jira/state_helpers.py +39 -0
  42. agentic_devtools/cli/jira/update_commands.py +222 -0
  43. agentic_devtools/cli/jira/vpn_wrapper.py +58 -0
  44. agentic_devtools/cli/release/__init__.py +5 -0
  45. agentic_devtools/cli/release/commands.py +113 -0
  46. agentic_devtools/cli/release/helpers.py +113 -0
  47. agentic_devtools/cli/runner.py +318 -0
  48. agentic_devtools/cli/state.py +174 -0
  49. agentic_devtools/cli/subprocess_utils.py +109 -0
  50. agentic_devtools/cli/tasks/__init__.py +28 -0
  51. agentic_devtools/cli/tasks/commands.py +851 -0
  52. agentic_devtools/cli/testing.py +442 -0
  53. agentic_devtools/cli/workflows/__init__.py +80 -0
  54. agentic_devtools/cli/workflows/advancement.py +204 -0
  55. agentic_devtools/cli/workflows/base.py +240 -0
  56. agentic_devtools/cli/workflows/checklist.py +278 -0
  57. agentic_devtools/cli/workflows/commands.py +1610 -0
  58. agentic_devtools/cli/workflows/manager.py +802 -0
  59. agentic_devtools/cli/workflows/preflight.py +323 -0
  60. agentic_devtools/cli/workflows/worktree_setup.py +1110 -0
  61. agentic_devtools/dispatcher.py +704 -0
  62. agentic_devtools/file_locking.py +203 -0
  63. agentic_devtools/prompts/__init__.py +38 -0
  64. agentic_devtools/prompts/apply-pull-request-review-suggestions/default-initiate-prompt.md +82 -0
  65. agentic_devtools/prompts/create-jira-epic/default-initiate-prompt.md +63 -0
  66. agentic_devtools/prompts/create-jira-issue/default-initiate-prompt.md +306 -0
  67. agentic_devtools/prompts/create-jira-subtask/default-initiate-prompt.md +57 -0
  68. agentic_devtools/prompts/loader.py +377 -0
  69. agentic_devtools/prompts/pull-request-review/default-completion-prompt.md +45 -0
  70. agentic_devtools/prompts/pull-request-review/default-decision-prompt.md +63 -0
  71. agentic_devtools/prompts/pull-request-review/default-file-review-prompt.md +69 -0
  72. agentic_devtools/prompts/pull-request-review/default-initiate-prompt.md +50 -0
  73. agentic_devtools/prompts/pull-request-review/default-summary-prompt.md +40 -0
  74. agentic_devtools/prompts/update-jira-issue/default-initiate-prompt.md +78 -0
  75. agentic_devtools/prompts/work-on-jira-issue/default-checklist-creation-prompt.md +58 -0
  76. agentic_devtools/prompts/work-on-jira-issue/default-commit-prompt.md +47 -0
  77. agentic_devtools/prompts/work-on-jira-issue/default-completion-prompt.md +65 -0
  78. agentic_devtools/prompts/work-on-jira-issue/default-implementation-prompt.md +66 -0
  79. agentic_devtools/prompts/work-on-jira-issue/default-implementation-review-prompt.md +60 -0
  80. agentic_devtools/prompts/work-on-jira-issue/default-initiate-prompt.md +67 -0
  81. agentic_devtools/prompts/work-on-jira-issue/default-planning-prompt.md +50 -0
  82. agentic_devtools/prompts/work-on-jira-issue/default-pull-request-prompt.md +56 -0
  83. agentic_devtools/prompts/work-on-jira-issue/default-retrieve-prompt.md +29 -0
  84. agentic_devtools/prompts/work-on-jira-issue/default-setup-prompt.md +19 -0
  85. agentic_devtools/prompts/work-on-jira-issue/default-verification-prompt.md +73 -0
  86. agentic_devtools/state.py +754 -0
  87. agentic_devtools/task_state.py +902 -0
  88. agentic_devtools-0.2.0.dist-info/METADATA +544 -0
  89. agentic_devtools-0.2.0.dist-info/RECORD +92 -0
  90. agentic_devtools-0.2.0.dist-info/WHEEL +4 -0
  91. agentic_devtools-0.2.0.dist-info/entry_points.txt +79 -0
  92. 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}")