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,1110 @@
1
+ """
2
+ Worktree setup automation for workflows.
3
+
4
+ This module provides functions to automatically set up git worktrees,
5
+ install agentic-devtools, and open VS Code workspaces for workflow execution.
6
+ It also includes placeholder issue creation for create workflows.
7
+ """
8
+
9
+ import os
10
+ import platform
11
+ import subprocess
12
+ import sys
13
+ from dataclasses import dataclass
14
+ from pathlib import Path
15
+ from typing import Optional, Tuple
16
+
17
+ # Exported for dynamic invocation by run_function_in_background
18
+ __all__ = ["_setup_worktree_from_state"]
19
+
20
+ # Name of the VS Code workspace file at repo root
21
+ WORKSPACE_FILE = "agdt-platform-management.code-workspace"
22
+
23
+
24
+ def generate_workflow_branch_name(
25
+ issue_key: str,
26
+ issue_type: str,
27
+ workflow_name: str,
28
+ parent_key: Optional[str] = None,
29
+ ) -> str:
30
+ """
31
+ Generate a branch name based on issue type and workflow.
32
+
33
+ Patterns:
34
+ - Create workflows: <issueType>/<issue_key>/create-<issueType>
35
+ - Update workflows: <issueType>/<issue_key>/update-<issueType>
36
+ - Subtask create: subtask/<parent_key>/<issue_key>/create-subtask
37
+
38
+ Args:
39
+ issue_key: The Jira issue key (e.g., "DFLY-1234")
40
+ issue_type: The issue type (Task, Epic, Sub-task, Bug, etc.)
41
+ workflow_name: The workflow name (create-jira-issue, create-jira-epic, etc.)
42
+ parent_key: For subtasks, the parent issue key (e.g., "DFLY-1233")
43
+
44
+ Returns:
45
+ The branch name following the pattern
46
+ """
47
+ # Normalize issue type to lowercase for branch naming
48
+ normalized_type = issue_type.lower().replace(" ", "-")
49
+
50
+ # Handle Sub-task specially
51
+ if normalized_type == "sub-task":
52
+ normalized_type = "subtask"
53
+
54
+ # Determine workflow action from workflow name
55
+ if "update" in workflow_name.lower():
56
+ action = f"update-{normalized_type}"
57
+ else:
58
+ action = f"create-{normalized_type}"
59
+
60
+ # For subtasks with a parent, include parent key
61
+ if normalized_type == "subtask" and parent_key:
62
+ return f"{normalized_type}/{parent_key}/{issue_key}/{action}"
63
+
64
+ # Standard pattern: <type>/<key>/<action>
65
+ return f"{normalized_type}/{issue_key}/{action}"
66
+
67
+
68
+ @dataclass
69
+ class WorktreeSetupResult:
70
+ """Result of worktree setup operation."""
71
+
72
+ success: bool
73
+ worktree_path: str
74
+ branch_name: str
75
+ error_message: Optional[str] = None
76
+ vscode_opened: bool = False
77
+ helpers_installed: bool = False
78
+
79
+
80
+ def is_in_worktree() -> bool:
81
+ """
82
+ Check if we're currently in a git worktree (not the main repo).
83
+
84
+ Returns:
85
+ True if in a worktree, False if in main repo or not in a git repo.
86
+ """
87
+ try:
88
+ # git rev-parse --is-inside-work-tree returns "true" if in a work tree
89
+ # git worktree list shows all worktrees
90
+ # Simplest check: compare git-dir to git-common-dir
91
+ result_dir = subprocess.run(
92
+ ["git", "rev-parse", "--git-dir"],
93
+ capture_output=True,
94
+ text=True,
95
+ check=False,
96
+ )
97
+ result_common = subprocess.run(
98
+ ["git", "rev-parse", "--git-common-dir"],
99
+ capture_output=True,
100
+ text=True,
101
+ check=False,
102
+ )
103
+
104
+ if result_dir.returncode != 0 or result_common.returncode != 0:
105
+ return False
106
+
107
+ git_dir = Path(result_dir.stdout.strip()).resolve()
108
+ git_common_dir = Path(result_common.stdout.strip()).resolve()
109
+
110
+ # In main repo: git_dir == ".git" (resolves to same as git_common_dir)
111
+ # In worktree: git_dir is a file pointing elsewhere, or is different path
112
+ # The git-dir in a worktree points to .git/worktrees/<name>
113
+ return git_dir != git_common_dir
114
+
115
+ except (FileNotFoundError, OSError):
116
+ return False
117
+
118
+
119
+ def get_current_branch() -> Optional[str]:
120
+ """
121
+ Get the current git branch name.
122
+
123
+ Returns:
124
+ The current branch name, or None if not in a git repo or detached HEAD.
125
+ """
126
+ try:
127
+ result = subprocess.run(
128
+ ["git", "branch", "--show-current"],
129
+ capture_output=True,
130
+ text=True,
131
+ check=False,
132
+ )
133
+ if result.returncode == 0:
134
+ return result.stdout.strip() or None
135
+ return None
136
+ except (FileNotFoundError, OSError):
137
+ return None
138
+
139
+
140
+ def switch_to_main_branch() -> bool:
141
+ """
142
+ Switch to the main branch.
143
+
144
+ Returns:
145
+ True if switch was successful, False otherwise.
146
+ """
147
+ try:
148
+ result = subprocess.run(
149
+ ["git", "switch", "main"],
150
+ capture_output=True,
151
+ text=True,
152
+ check=False,
153
+ )
154
+ return result.returncode == 0
155
+ except (FileNotFoundError, OSError):
156
+ return False
157
+
158
+
159
+ def get_main_repo_root() -> Optional[str]:
160
+ """
161
+ Get the root directory of the main git repository (not worktree).
162
+
163
+ For worktrees, this returns the path to the main repository.
164
+ For the main repo, this returns the repo root.
165
+
166
+ Returns:
167
+ The absolute path to the main repo root, or None if not in a git repo.
168
+ """
169
+ try:
170
+ # First, get the common git directory (shared between main repo and worktrees)
171
+ result = subprocess.run(
172
+ ["git", "rev-parse", "--git-common-dir"],
173
+ capture_output=True,
174
+ text=True,
175
+ check=False,
176
+ )
177
+ if result.returncode != 0:
178
+ return None
179
+
180
+ git_common_dir = result.stdout.strip()
181
+
182
+ # The git-common-dir is usually .git in main repo or path/to/main/.git for worktrees
183
+ # We need the parent of the .git directory
184
+ git_path = Path(git_common_dir).resolve()
185
+
186
+ # If it ends with .git, go to parent
187
+ if git_path.name == ".git":
188
+ return str(git_path.parent)
189
+
190
+ # For worktrees, git-common-dir points to main/.git directly
191
+ return str(git_path.parent)
192
+
193
+ except (FileNotFoundError, OSError):
194
+ return None
195
+
196
+
197
+ def get_repos_parent_dir() -> Optional[str]:
198
+ """
199
+ Get the parent directory where repos are stored.
200
+
201
+ This is typically one level up from the main repo root (e.g., c:\\repos).
202
+
203
+ Returns:
204
+ The absolute path to the repos parent directory, or None if not determinable.
205
+ """
206
+ main_repo = get_main_repo_root()
207
+ if main_repo:
208
+ return str(Path(main_repo).parent)
209
+ return None
210
+
211
+
212
+ def create_worktree(
213
+ issue_key: str,
214
+ branch_prefix: str = "feature",
215
+ branch_name: Optional[str] = None,
216
+ use_existing_branch: bool = False,
217
+ ) -> WorktreeSetupResult:
218
+ """
219
+ Create a git worktree for the given issue key.
220
+
221
+ The worktree will be created as a sibling directory to the main repo,
222
+ named after the issue key (e.g., ../DFLY-1234).
223
+
224
+ Args:
225
+ issue_key: The issue key (e.g., "DFLY-1234")
226
+ branch_prefix: Prefix for the branch name (default: "feature").
227
+ Ignored if branch_name is provided.
228
+ branch_name: Exact branch name to use. If provided, branch_prefix is ignored.
229
+ Used for PR review workflows where the branch already exists on origin.
230
+ use_existing_branch: If True and branch_name is provided, checkout the
231
+ existing branch from origin instead of creating a new one.
232
+ Enables safety checks before proceeding.
233
+
234
+ Returns:
235
+ WorktreeSetupResult with success status and paths
236
+ """
237
+ from ..git.operations import (
238
+ check_branch_safe_to_recreate,
239
+ fetch_branch,
240
+ )
241
+
242
+ repos_parent = get_repos_parent_dir()
243
+ if not repos_parent:
244
+ return WorktreeSetupResult(
245
+ success=False,
246
+ worktree_path="",
247
+ branch_name="",
248
+ error_message="Could not determine repository parent directory",
249
+ )
250
+
251
+ worktree_path = os.path.join(repos_parent, issue_key)
252
+
253
+ # Determine the branch name to use
254
+ if branch_name:
255
+ resolved_branch_name = branch_name
256
+ else:
257
+ resolved_branch_name = f"{branch_prefix}/{issue_key}/implementation"
258
+
259
+ # Check if worktree already exists
260
+ if os.path.exists(worktree_path):
261
+ # Verify it's a valid git worktree
262
+ git_file = os.path.join(worktree_path, ".git")
263
+ if os.path.exists(git_file):
264
+ return WorktreeSetupResult(
265
+ success=True,
266
+ worktree_path=worktree_path,
267
+ branch_name=resolved_branch_name,
268
+ error_message=None,
269
+ )
270
+ else:
271
+ return WorktreeSetupResult(
272
+ success=False,
273
+ worktree_path=worktree_path,
274
+ branch_name=resolved_branch_name,
275
+ error_message=f"Directory {worktree_path} exists but is not a git worktree",
276
+ )
277
+
278
+ # Check if we're currently on the target branch in the main repo.
279
+ # Git doesn't allow creating a worktree for a branch that's already checked out.
280
+ # If we're on the target branch in main repo, we need to switch to main first.
281
+ current_branch = get_current_branch()
282
+ in_worktree = is_in_worktree()
283
+
284
+ if current_branch == resolved_branch_name and not in_worktree:
285
+ print(f"Currently on branch '{resolved_branch_name}' in main repo.")
286
+ print("Switching to 'main' branch to allow worktree creation...")
287
+ if not switch_to_main_branch():
288
+ return WorktreeSetupResult(
289
+ success=False,
290
+ worktree_path=worktree_path,
291
+ branch_name=resolved_branch_name,
292
+ error_message="Failed to switch to main branch. Cannot create worktree while on target branch.",
293
+ )
294
+ print("Switched to 'main' branch successfully.")
295
+
296
+ # For PR review workflows with existing branches, perform safety checks
297
+ if use_existing_branch and branch_name:
298
+ print(f"Checking if branch '{branch_name}' is safe to use...")
299
+
300
+ # First fetch the branch from origin
301
+ fetch_branch(branch_name)
302
+
303
+ # Perform safety check
304
+ safety_result = check_branch_safe_to_recreate(branch_name)
305
+
306
+ if not safety_result.is_safe:
307
+ return WorktreeSetupResult(
308
+ success=False,
309
+ worktree_path=worktree_path,
310
+ branch_name=resolved_branch_name,
311
+ error_message=f"Cannot safely create worktree:\n{safety_result.message}",
312
+ )
313
+
314
+ print(f"Safety check passed: {safety_result.message}")
315
+
316
+ # Create the worktree
317
+ try:
318
+ print(f"Creating worktree at {worktree_path}...")
319
+
320
+ if use_existing_branch and branch_name:
321
+ # For PR review: checkout existing branch from origin
322
+ result = subprocess.run(
323
+ ["git", "worktree", "add", worktree_path, branch_name],
324
+ capture_output=True,
325
+ text=True,
326
+ check=False,
327
+ )
328
+
329
+ if result.returncode != 0:
330
+ # Try tracking the remote branch
331
+ result = subprocess.run(
332
+ ["git", "worktree", "add", worktree_path, "--track", "-b", branch_name, f"origin/{branch_name}"],
333
+ capture_output=True,
334
+ text=True,
335
+ check=False,
336
+ )
337
+ else:
338
+ # Standard flow: create new branch
339
+ result = subprocess.run(
340
+ ["git", "worktree", "add", worktree_path, "-b", resolved_branch_name],
341
+ capture_output=True,
342
+ text=True,
343
+ check=False,
344
+ )
345
+
346
+ if result.returncode != 0:
347
+ # Check if branch already exists - try without -b
348
+ if "already exists" in result.stderr:
349
+ print(f"Branch {resolved_branch_name} already exists, using existing branch...")
350
+ result = subprocess.run(
351
+ ["git", "worktree", "add", worktree_path, resolved_branch_name],
352
+ capture_output=True,
353
+ text=True,
354
+ check=False,
355
+ )
356
+
357
+ if result.returncode != 0:
358
+ return WorktreeSetupResult(
359
+ success=False,
360
+ worktree_path=worktree_path,
361
+ branch_name=resolved_branch_name,
362
+ error_message=f"Failed to create worktree: {result.stderr.strip()}",
363
+ )
364
+
365
+ print(f"Worktree created successfully at {worktree_path}")
366
+ return WorktreeSetupResult(
367
+ success=True,
368
+ worktree_path=worktree_path,
369
+ branch_name=resolved_branch_name,
370
+ )
371
+
372
+ except (FileNotFoundError, OSError) as e:
373
+ return WorktreeSetupResult(
374
+ success=False,
375
+ worktree_path=worktree_path,
376
+ branch_name=branch_name,
377
+ error_message=f"Error creating worktree: {e}",
378
+ )
379
+
380
+
381
+ def install_agentic_devtools(worktree_path: str) -> bool:
382
+ """
383
+ Set up development tools in the worktree using setup-dev-tools.py.
384
+
385
+ This runs `python setup-dev-tools.py` in the worktree to:
386
+ - Create a local .dfly-venv with agentic-devtools installed
387
+ - Configure git hooks
388
+ - Install cspell and ruff
389
+
390
+ The venv approach enables multi-worktree development where each worktree
391
+ has its own isolated installation of agentic-devtools.
392
+
393
+ Args:
394
+ worktree_path: Path to the worktree directory
395
+
396
+ Returns:
397
+ True if setup succeeded, False otherwise
398
+ """
399
+ setup_script = os.path.join(worktree_path, "setup-dev-tools.py")
400
+
401
+ if not os.path.exists(setup_script):
402
+ print(f"Warning: setup-dev-tools.py not found at {setup_script}", file=sys.stderr)
403
+ return False
404
+
405
+ print(f"Running setup-dev-tools.py in {worktree_path}...")
406
+
407
+ try:
408
+ # Use the same Python interpreter that's running this script
409
+ python_exe = sys.executable
410
+
411
+ result = subprocess.run(
412
+ [python_exe, setup_script],
413
+ cwd=worktree_path,
414
+ capture_output=True,
415
+ text=True,
416
+ check=False,
417
+ )
418
+
419
+ if result.returncode != 0:
420
+ print(f"Warning: Failed to run setup-dev-tools.py: {result.stderr}", file=sys.stderr)
421
+ # Print stdout too as it may contain useful info
422
+ if result.stdout:
423
+ print(result.stdout, file=sys.stderr)
424
+ return False
425
+
426
+ print("Development tools configured successfully in worktree")
427
+ return True
428
+
429
+ except (FileNotFoundError, OSError) as e:
430
+ print(f"Warning: Error running setup-dev-tools.py: {e}", file=sys.stderr)
431
+ return False
432
+
433
+
434
+ # Backward-compatible alias
435
+ install_agdt_ai_helpers = install_agentic_devtools
436
+
437
+
438
+ def open_vscode_workspace(worktree_path: str) -> bool:
439
+ """
440
+ Open VS Code with the workspace file in the worktree.
441
+
442
+ This opens a new VS Code window with the agdt-platform-management.code-workspace
443
+ file from the worktree.
444
+
445
+ Args:
446
+ worktree_path: Path to the worktree directory
447
+
448
+ Returns:
449
+ True if VS Code was opened, False otherwise
450
+ """
451
+ workspace_file = os.path.join(worktree_path, WORKSPACE_FILE)
452
+
453
+ if not os.path.exists(workspace_file):
454
+ print(f"Warning: Workspace file not found at {workspace_file}", file=sys.stderr)
455
+ return False
456
+
457
+ print(f"Opening VS Code workspace: {workspace_file}")
458
+
459
+ try:
460
+ # Open VS Code in a new window (non-blocking)
461
+ # Check actual platform (not mocked) for subprocess flags availability
462
+ if platform.system() == "Windows" and hasattr(subprocess, "DETACHED_PROCESS"):
463
+ # On Windows, 'code' is a .cmd batch file, so we need shell=True
464
+ # to find it via PATH. We also use creationflags to detach the process.
465
+ subprocess.Popen(
466
+ ["code", workspace_file],
467
+ stdout=subprocess.DEVNULL,
468
+ stderr=subprocess.DEVNULL,
469
+ shell=True,
470
+ creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP,
471
+ )
472
+ else:
473
+ # On Unix-like systems, start_new_session works correctly
474
+ subprocess.Popen(
475
+ ["code", workspace_file],
476
+ stdout=subprocess.DEVNULL,
477
+ stderr=subprocess.DEVNULL,
478
+ start_new_session=True,
479
+ )
480
+
481
+ print("VS Code window opened")
482
+ return True
483
+
484
+ except (FileNotFoundError, OSError) as e:
485
+ print(f"Warning: Could not open VS Code: {e}", file=sys.stderr)
486
+ return False
487
+
488
+
489
+ def setup_worktree_environment(
490
+ issue_key: str,
491
+ branch_prefix: str = "feature",
492
+ branch_name: Optional[str] = None,
493
+ use_existing_branch: bool = False,
494
+ install_helpers: bool = True,
495
+ open_vscode: bool = True,
496
+ ) -> WorktreeSetupResult:
497
+ """
498
+ Complete worktree setup: create worktree, install helpers, open VS Code.
499
+
500
+ This is the main entry point for setting up a new development environment
501
+ for an issue. It:
502
+ 1. Creates a git worktree for the issue
503
+ 2. Installs agentic-devtools in the worktree
504
+ 3. Opens VS Code with the workspace file
505
+
506
+ Args:
507
+ issue_key: The issue key (e.g., "DFLY-1234")
508
+ branch_prefix: Prefix for the branch name (default: "feature").
509
+ Ignored if branch_name is provided.
510
+ branch_name: Exact branch name to use. If provided, branch_prefix is ignored.
511
+ Used for PR review workflows where the branch already exists on origin.
512
+ use_existing_branch: If True and branch_name is provided, checkout the
513
+ existing branch from origin instead of creating a new one.
514
+ install_helpers: Whether to install agentic-devtools (default: True)
515
+ open_vscode: Whether to open VS Code (default: True)
516
+
517
+ Returns:
518
+ WorktreeSetupResult with success status and details
519
+ """
520
+ # Step 1: Create worktree
521
+ result = create_worktree(
522
+ issue_key=issue_key,
523
+ branch_prefix=branch_prefix,
524
+ branch_name=branch_name,
525
+ use_existing_branch=use_existing_branch,
526
+ )
527
+
528
+ if not result.success:
529
+ return result
530
+
531
+ # Step 2: Install agentic-devtools
532
+ if install_helpers:
533
+ result.helpers_installed = install_agentic_devtools(result.worktree_path)
534
+
535
+ # Step 3: Open VS Code
536
+ if open_vscode:
537
+ result.vscode_opened = open_vscode_workspace(result.worktree_path)
538
+
539
+ return result
540
+
541
+
542
+ def check_worktree_exists(issue_key: str) -> Optional[str]:
543
+ """
544
+ Check if a worktree for the given issue key already exists.
545
+
546
+ Args:
547
+ issue_key: The issue key to check for
548
+
549
+ Returns:
550
+ The worktree path if it exists, None otherwise
551
+ """
552
+ repos_parent = get_repos_parent_dir()
553
+ if not repos_parent:
554
+ return None
555
+
556
+ worktree_path = os.path.join(repos_parent, issue_key)
557
+
558
+ if os.path.exists(worktree_path):
559
+ # Verify it's a valid git worktree
560
+ git_file = os.path.join(worktree_path, ".git")
561
+ if os.path.exists(git_file):
562
+ return worktree_path
563
+
564
+ return None
565
+
566
+
567
+ def get_worktree_continuation_prompt(
568
+ issue_key: str,
569
+ workflow_name: str,
570
+ user_request: Optional[str] = None,
571
+ additional_params: Optional[dict] = None,
572
+ ) -> str:
573
+ """
574
+ Generate a prompt for continuing a workflow in a new VS Code window.
575
+
576
+ This generates a copy/paste ready command that the user can paste into
577
+ the AI chat in the new VS Code window to continue the workflow.
578
+
579
+ Args:
580
+ issue_key: The issue key
581
+ workflow_name: The workflow name (e.g., "work-on-jira-issue", "pull-request-review")
582
+ user_request: The user's explanation/request for what they want
583
+ (AI will use this to populate Jira fields appropriately)
584
+ additional_params: Additional parameters to include in the command
585
+ (e.g., {"pull_request_id": "12345"})
586
+
587
+ Returns:
588
+ A formatted prompt string to paste in the new VS Code window
589
+ """
590
+ # Build the base command for each workflow
591
+ workflow_base_commands = {
592
+ "work-on-jira-issue": "agdt-initiate-work-on-jira-issue-workflow",
593
+ "pull-request-review": "agdt-initiate-pull-request-review-workflow",
594
+ "create-jira-issue": "agdt-initiate-create-jira-issue-workflow",
595
+ "create-jira-epic": "agdt-initiate-create-jira-epic-workflow",
596
+ "create-jira-subtask": "agdt-initiate-create-jira-subtask-workflow",
597
+ "update-jira-issue": "agdt-initiate-update-jira-issue-workflow",
598
+ }
599
+
600
+ base_command = workflow_base_commands.get(workflow_name, "")
601
+
602
+ if not base_command:
603
+ return f"Continue working on issue {issue_key} in the new VS Code window."
604
+
605
+ # Build the full command with all parameters
606
+ command_parts = [base_command, f"--issue-key {issue_key}"]
607
+
608
+ # Add user-request if provided (for create workflows)
609
+ if user_request:
610
+ # Escape quotes in the value for shell safety
611
+ escaped_request = user_request.replace('"', '\\"')
612
+ command_parts.append(f'--user-request "{escaped_request}"')
613
+
614
+ # Add additional parameters if provided
615
+ if additional_params:
616
+ param_order = ["parent_key", "pull_request_id"]
617
+ for param_name in param_order:
618
+ if param_name in additional_params and additional_params[param_name]:
619
+ value = str(additional_params[param_name])
620
+ # Escape quotes in the value for shell safety
621
+ escaped_value = value.replace('"', '\\"')
622
+ cli_param = param_name.replace("_", "-")
623
+ command_parts.append(f'--{cli_param} "{escaped_value}"')
624
+
625
+ full_command = " ".join(command_parts)
626
+
627
+ # Generate a friendly description of what to do
628
+ return f"""
629
+ ================================================================================
630
+ šŸ“‹ COPY THE COMMAND BELOW INTO THE NEW VS CODE WINDOW
631
+ ================================================================================
632
+
633
+ In the new VS Code window's AI chat (Copilot/Claude), paste this command:
634
+
635
+ ```
636
+ {full_command}
637
+ ```
638
+
639
+ This will continue the {workflow_name} workflow with all the context preserved.
640
+ ================================================================================"""
641
+
642
+
643
+ def get_ai_agent_continuation_prompt(
644
+ issue_key: str,
645
+ workflow_name: str = "work-on-jira-issue",
646
+ user_request: Optional[str] = None,
647
+ additional_params: Optional[dict] = None,
648
+ ) -> str:
649
+ """
650
+ Generate a detailed prompt for AI agents to continue working on an issue.
651
+
652
+ This is used when a new VS Code window is opened in a worktree to provide
653
+ the AI agent with clear instructions on how to proceed.
654
+
655
+ Args:
656
+ issue_key: The Jira issue key (e.g., "DFLY-1234") or PR identifier (e.g., "PR24031")
657
+ workflow_name: The workflow being executed (e.g., "update-jira-issue")
658
+ user_request: The user's request/explanation for the workflow
659
+ additional_params: Additional parameters for the command (e.g., {"pull_request_id": "24031"})
660
+
661
+ Returns:
662
+ A detailed prompt string formatted for AI agents
663
+ """
664
+ # Build the base command for each workflow
665
+ workflow_base_commands = {
666
+ "work-on-jira-issue": "agdt-initiate-work-on-jira-issue-workflow",
667
+ "pull-request-review": "agdt-initiate-pull-request-review-workflow",
668
+ "create-jira-issue": "agdt-initiate-create-jira-issue-workflow",
669
+ "create-jira-epic": "agdt-initiate-create-jira-epic-workflow",
670
+ "create-jira-subtask": "agdt-initiate-create-jira-subtask-workflow",
671
+ "update-jira-issue": "agdt-initiate-update-jira-issue-workflow",
672
+ }
673
+
674
+ base_command = workflow_base_commands.get(workflow_name, "agdt-initiate-work-on-jira-issue-workflow")
675
+
676
+ # Build the full command with parameters
677
+ # For pull-request-review, use --pull-request-id instead of --issue-key
678
+ if workflow_name == "pull-request-review" and additional_params and additional_params.get("pull_request_id"):
679
+ pull_request_id = additional_params["pull_request_id"]
680
+ command_parts = [base_command, f"--pull-request-id {pull_request_id}"]
681
+ else:
682
+ command_parts = [base_command, f"--issue-key {issue_key}"]
683
+
684
+ if user_request:
685
+ # Escape quotes for shell safety
686
+ escaped_request = user_request.replace('"', '\\"')
687
+ command_parts.append(f'--user-request "{escaped_request}"')
688
+
689
+ full_command = " ".join(command_parts)
690
+
691
+ # Generate workflow-appropriate prompt text
692
+ if workflow_name == "update-jira-issue":
693
+ task_description = "assigned to update a Jira issue's metadata"
694
+ action_description = (
695
+ "update the Jira issue fields (summary, description, acceptance criteria) as specified in the user request"
696
+ )
697
+ elif workflow_name in ("create-jira-issue", "create-jira-epic", "create-jira-subtask"):
698
+ task_description = "assigned to create a new Jira issue"
699
+ action_description = (
700
+ "populate the placeholder Jira issue with proper summary, description, "
701
+ "and acceptance criteria based on the user request"
702
+ )
703
+ elif workflow_name == "pull-request-review":
704
+ task_description = "assigned to review a pull request"
705
+ action_description = "review the pull request thoroughly and provide feedback"
706
+ else:
707
+ task_description = "assigned an issue to work on"
708
+ action_description = "work on the issue until you have completed the workflow"
709
+
710
+ return f"""You are a senior software engineer and expert architect who has been {task_description}.
711
+
712
+ Please run the following command:
713
+
714
+ {full_command}
715
+
716
+ to initiate the workflow and then follow the instructions logged to the console to {action_description}.
717
+
718
+ Work as independently as possible, only pausing to ask questions or seek approval if absolutely \
719
+ necessary. As a senior software engineer and expert architect you don't want or need individual \
720
+ approval for every command that you execute, so use the example commands which can be auto approved \
721
+ and you will be able to develop a quality solution much more efficiently.
722
+
723
+ It is anyway not sensible to ask questions or ask for approval, because once your work is complete \
724
+ another senior software engineer and expert architect in your team will thoroughly review your work. \
725
+ So work through the entire process to the best of your abilities knowing that a trusted colleague \
726
+ will review it all thoroughly and provide feedback at that time if necessary."""
727
+
728
+
729
+ def setup_worktree_in_background_sync(
730
+ issue_key: str,
731
+ branch_prefix: str = "feature",
732
+ branch_name: Optional[str] = None,
733
+ use_existing_branch: bool = False,
734
+ workflow_name: str = "work-on-jira-issue",
735
+ user_request: Optional[str] = None,
736
+ additional_params: Optional[dict] = None,
737
+ ) -> None:
738
+ """
739
+ Perform worktree setup synchronously (called from background task).
740
+
741
+ This function is designed to be called from a background task runner.
742
+ It performs the full worktree setup and prints the continuation prompt.
743
+
744
+ Args:
745
+ issue_key: The Jira issue key
746
+ branch_prefix: Prefix for the branch name (default: "feature").
747
+ Ignored if branch_name is provided.
748
+ branch_name: Exact branch name to use. If provided, branch_prefix is ignored.
749
+ Used for PR review workflows where the branch already exists on origin.
750
+ use_existing_branch: If True and branch_name is provided, checkout the
751
+ existing branch from origin instead of creating a new one.
752
+ workflow_name: The workflow name for continuation prompt
753
+ user_request: The user's explanation of what they want
754
+ additional_params: Additional parameters for continuation command
755
+ """
756
+ print(f"\n{'=' * 80}")
757
+ print("BACKGROUND WORKTREE SETUP")
758
+ print("=" * 80)
759
+
760
+ # Check if worktree already exists
761
+ existing_path = check_worktree_exists(issue_key)
762
+ if existing_path:
763
+ print(f"\nWorktree already exists at: {existing_path}")
764
+ print("Ensuring development tools are configured...")
765
+
766
+ # Ensure helpers are installed
767
+ helpers_installed = install_agentic_devtools(existing_path)
768
+ print(f" Helpers installed: {'Yes' if helpers_installed else 'No'}")
769
+
770
+ # Open VS Code
771
+ vscode_opened = open_vscode_workspace(existing_path)
772
+ print(f" VS Code opened: {'Yes' if vscode_opened else 'No'}")
773
+
774
+ print("\nāœ… Environment ready!")
775
+ print(get_worktree_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
776
+ print("\n" + "=" * 80)
777
+ print("AI AGENT INSTRUCTIONS - IMPORTANT")
778
+ print("=" * 80)
779
+ print("""
780
+ Please provide the following prompt in your chat response to the user so they
781
+ can copy and paste it into the new VS Code window that just opened:
782
+ """)
783
+ print("--- BEGIN PROMPT FOR USER TO COPY ---")
784
+ print(get_ai_agent_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
785
+ print("--- END PROMPT FOR USER TO COPY ---")
786
+ return
787
+
788
+ # Create new worktree environment
789
+ print(f"\nCreating worktree for issue {issue_key}...")
790
+ if use_existing_branch and branch_name:
791
+ print(f" Using existing branch from origin: {branch_name}")
792
+
793
+ result = setup_worktree_environment(
794
+ issue_key=issue_key,
795
+ branch_prefix=branch_prefix,
796
+ branch_name=branch_name,
797
+ use_existing_branch=use_existing_branch,
798
+ install_helpers=True,
799
+ open_vscode=True,
800
+ )
801
+
802
+ if result.success:
803
+ print("\nāœ… Environment setup complete!")
804
+ print(f" Worktree: {result.worktree_path}")
805
+ print(f" Branch: {result.branch_name}")
806
+ print(f" Helpers installed: {'Yes' if result.helpers_installed else 'No'}")
807
+ print(f" VS Code opened: {'Yes' if result.vscode_opened else 'No'}")
808
+ print(get_worktree_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
809
+ print("\n" + "=" * 80)
810
+ print("AI AGENT INSTRUCTIONS - IMPORTANT")
811
+ print("=" * 80)
812
+ print("""
813
+ Please provide the following prompt in your chat response to the user so they
814
+ can copy and paste it into the new VS Code window that just opened:
815
+ """)
816
+ print("--- BEGIN PROMPT FOR USER TO COPY ---")
817
+ print(get_ai_agent_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
818
+ print("--- END PROMPT FOR USER TO COPY ---")
819
+ else:
820
+ print(f"\nāŒ Setup failed: {result.error_message}")
821
+ raise RuntimeError(f"Worktree setup failed: {result.error_message}")
822
+
823
+
824
+ def _setup_worktree_from_state() -> None:
825
+ """
826
+ Wrapper function for background task execution.
827
+
828
+ This function is called dynamically by run_function_in_background
829
+ via string reference (see __all__ export at module top).
830
+
831
+ This reads parameters from state and calls setup_worktree_in_background_sync.
832
+ Used by run_function_in_background since it only supports parameterless functions.
833
+ """
834
+ import json
835
+
836
+ from ...state import get_value
837
+
838
+ # Read parameters from state
839
+ issue_key = get_value("worktree_setup.issue_key")
840
+ branch_prefix = get_value("worktree_setup.branch_prefix") or "feature"
841
+ branch_name = get_value("worktree_setup.branch_name")
842
+ use_existing_branch = get_value("worktree_setup.use_existing_branch") == "true"
843
+ workflow_name = get_value("worktree_setup.workflow_name") or "work-on-jira-issue"
844
+ user_request = get_value("worktree_setup.user_request")
845
+ additional_params_str = get_value("worktree_setup.additional_params")
846
+
847
+ additional_params = None
848
+ if additional_params_str:
849
+ try:
850
+ additional_params = json.loads(additional_params_str)
851
+ except json.JSONDecodeError:
852
+ pass
853
+
854
+ if not issue_key:
855
+ raise ValueError("worktree_setup.issue_key not set in state")
856
+
857
+ # Call the actual setup function
858
+ setup_worktree_in_background_sync(
859
+ issue_key=issue_key,
860
+ branch_prefix=branch_prefix,
861
+ branch_name=branch_name,
862
+ use_existing_branch=use_existing_branch,
863
+ workflow_name=workflow_name,
864
+ user_request=user_request,
865
+ additional_params=additional_params,
866
+ )
867
+
868
+
869
+ def start_worktree_setup_background(
870
+ issue_key: str,
871
+ branch_prefix: str = "feature",
872
+ branch_name: Optional[str] = None,
873
+ use_existing_branch: bool = False,
874
+ workflow_name: str = "work-on-jira-issue",
875
+ user_request: Optional[str] = None,
876
+ additional_params: Optional[dict] = None,
877
+ ) -> str:
878
+ """
879
+ Start worktree setup as a background task.
880
+
881
+ This spawns a background process to create the worktree, install helpers,
882
+ and open VS Code. The calling process returns immediately, allowing the
883
+ command line to be available.
884
+
885
+ Args:
886
+ issue_key: The Jira issue key
887
+ branch_prefix: Prefix for the branch name (default: "feature").
888
+ Ignored if branch_name is provided.
889
+ branch_name: Exact branch name to use. If provided, branch_prefix is ignored.
890
+ Used for PR review workflows where the branch already exists on origin.
891
+ use_existing_branch: If True and branch_name is provided, checkout the
892
+ existing branch from origin instead of creating a new one.
893
+ workflow_name: The workflow name for continuation prompt
894
+ user_request: The user's explanation of what they want
895
+ additional_params: Additional parameters for continuation command
896
+
897
+ Returns:
898
+ The background task ID for tracking progress
899
+ """
900
+ import json
901
+
902
+ from ...background_tasks import run_function_in_background
903
+ from ...state import set_value
904
+
905
+ # Store parameters in state for the background function to read
906
+ set_value("worktree_setup.issue_key", issue_key)
907
+ set_value("worktree_setup.branch_prefix", branch_prefix)
908
+ set_value("worktree_setup.workflow_name", workflow_name)
909
+ if branch_name:
910
+ set_value("worktree_setup.branch_name", branch_name)
911
+ if use_existing_branch:
912
+ set_value("worktree_setup.use_existing_branch", "true")
913
+ if user_request:
914
+ set_value("worktree_setup.user_request", user_request)
915
+ if additional_params:
916
+ set_value("worktree_setup.additional_params", json.dumps(additional_params))
917
+
918
+ # Build display name for the task
919
+ display_name = f"agdt-setup-worktree-background --issue-key {issue_key}"
920
+
921
+ # Start background task using function-based runner
922
+ # This avoids the need for global CLI commands to be installed
923
+ task = run_function_in_background(
924
+ module_path="agentic_devtools.cli.workflows.worktree_setup",
925
+ function_name="_setup_worktree_from_state",
926
+ command_display_name=display_name,
927
+ args={
928
+ "issue_key": issue_key,
929
+ "branch_prefix": branch_prefix,
930
+ "branch_name": branch_name,
931
+ "use_existing_branch": use_existing_branch,
932
+ "workflow_name": workflow_name,
933
+ },
934
+ )
935
+
936
+ return task.id
937
+
938
+
939
+ # =============================================================================
940
+ # Placeholder Issue Creation for Create Workflows
941
+ # =============================================================================
942
+
943
+
944
+ @dataclass
945
+ class PlaceholderIssueResult:
946
+ """Result of placeholder issue creation."""
947
+
948
+ success: bool
949
+ issue_key: Optional[str] = None
950
+ error_message: Optional[str] = None
951
+
952
+
953
+ def create_placeholder_issue(
954
+ project_key: str,
955
+ issue_type: str = "Task",
956
+ parent_key: Optional[str] = None,
957
+ ) -> PlaceholderIssueResult:
958
+ """
959
+ Create a placeholder Jira issue with minimal fields.
960
+
961
+ This creates an issue with a placeholder summary and description
962
+ that will be updated later in the workflow.
963
+
964
+ Args:
965
+ project_key: Jira project key (e.g., "DFLY")
966
+ issue_type: Issue type (Task, Epic, Sub-task)
967
+ parent_key: Parent issue key (required for Sub-task type)
968
+
969
+ Returns:
970
+ PlaceholderIssueResult with success status and issue key
971
+ """
972
+ try:
973
+ from ..jira.create_commands import create_issue_sync
974
+
975
+ # Generate placeholder values
976
+ placeholder_summary = f"[Placeholder] {issue_type} created via workflow"
977
+ placeholder_description = (
978
+ "This issue was created as a placeholder by the workflow automation.\n\n"
979
+ "Please update the summary, description, and other fields as needed."
980
+ )
981
+ placeholder_labels = ["workflow-placeholder"]
982
+
983
+ # For Epic, we need an epic name
984
+ epic_name = None
985
+ if issue_type.lower() == "epic":
986
+ epic_name = placeholder_summary
987
+
988
+ print(f"Creating placeholder {issue_type} in project {project_key}...")
989
+
990
+ result = create_issue_sync(
991
+ project_key=project_key,
992
+ summary=placeholder_summary,
993
+ issue_type=issue_type,
994
+ description=placeholder_description,
995
+ labels=placeholder_labels,
996
+ epic_name=epic_name,
997
+ parent_key=parent_key,
998
+ )
999
+
1000
+ issue_key = result.get("key")
1001
+ if issue_key:
1002
+ print(f"āœ… Placeholder {issue_type} created: {issue_key}")
1003
+ return PlaceholderIssueResult(success=True, issue_key=issue_key)
1004
+ else:
1005
+ return PlaceholderIssueResult(
1006
+ success=False,
1007
+ error_message="API did not return an issue key",
1008
+ )
1009
+
1010
+ except Exception as e:
1011
+ return PlaceholderIssueResult(
1012
+ success=False,
1013
+ error_message=str(e),
1014
+ )
1015
+
1016
+
1017
+ def create_placeholder_and_setup_worktree(
1018
+ project_key: str,
1019
+ issue_type: str = "Task",
1020
+ parent_key: Optional[str] = None,
1021
+ workflow_name: str = "create-jira-issue",
1022
+ user_request: Optional[str] = None,
1023
+ additional_params: Optional[dict] = None,
1024
+ ) -> Tuple[bool, Optional[str]]:
1025
+ """
1026
+ Create a placeholder issue and set up a worktree for it.
1027
+
1028
+ This is the main entry point for create workflows that need both
1029
+ issue creation and environment setup.
1030
+
1031
+ Args:
1032
+ project_key: Jira project key (e.g., "DFLY")
1033
+ issue_type: Issue type (Task, Epic, Sub-task)
1034
+ parent_key: Parent issue key (required for Sub-task type)
1035
+ workflow_name: Name of the workflow for continuation prompt
1036
+ user_request: The user's explanation of what they want to create
1037
+ (AI will use this to populate Jira fields appropriately)
1038
+ additional_params: Additional parameters to include in the continuation
1039
+ command (e.g., {"parent_key": "DFLY-1234"})
1040
+
1041
+ Returns:
1042
+ Tuple of (success, issue_key). If success is True, issue_key contains
1043
+ the created issue key. If success is False, issue_key is None.
1044
+ """
1045
+ print(f"\n{'=' * 80}")
1046
+ print(f"CREATE WORKFLOW: {workflow_name}")
1047
+ print("=" * 80)
1048
+
1049
+ # Step 1: Create placeholder issue
1050
+ print("\nšŸ“ Step 1: Creating placeholder Jira issue...")
1051
+ issue_result = create_placeholder_issue(
1052
+ project_key=project_key,
1053
+ issue_type=issue_type,
1054
+ parent_key=parent_key,
1055
+ )
1056
+
1057
+ if not issue_result.success:
1058
+ print(f"\nāŒ Failed to create placeholder issue: {issue_result.error_message}")
1059
+ return False, None
1060
+
1061
+ issue_key = issue_result.issue_key
1062
+ print(f" Issue key: {issue_key}")
1063
+
1064
+ # Set the issue key in state for later use
1065
+ from ...state import set_value
1066
+
1067
+ set_value("jira.issue_key", issue_key)
1068
+
1069
+ # Step 2: Set up worktree environment
1070
+ print("\nšŸ”§ Step 2: Setting up worktree environment...")
1071
+
1072
+ # Check if worktree already exists (unlikely for new issue, but check anyway)
1073
+ existing_path = check_worktree_exists(issue_key)
1074
+ if existing_path:
1075
+ print(f" Worktree already exists at: {existing_path}")
1076
+ install_agentic_devtools(existing_path)
1077
+ open_vscode_workspace(existing_path)
1078
+ print(get_worktree_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
1079
+ return True, issue_key
1080
+
1081
+ # Generate branch name based on issue type and workflow
1082
+ branch_name = generate_workflow_branch_name(
1083
+ issue_key=issue_key,
1084
+ issue_type=issue_type,
1085
+ workflow_name=workflow_name,
1086
+ parent_key=parent_key,
1087
+ )
1088
+
1089
+ # Create new worktree with generated branch name
1090
+ result = setup_worktree_environment(
1091
+ issue_key=issue_key,
1092
+ branch_name=branch_name,
1093
+ install_helpers=True,
1094
+ open_vscode=True,
1095
+ )
1096
+
1097
+ if result.success:
1098
+ print("\nāœ… Environment setup complete!")
1099
+ print(f" Worktree: {result.worktree_path}")
1100
+ print(f" Branch: {result.branch_name}")
1101
+ print(f" Helpers installed: {'Yes' if result.helpers_installed else 'No'}")
1102
+ print(f" VS Code opened: {'Yes' if result.vscode_opened else 'No'}")
1103
+ print(get_worktree_continuation_prompt(issue_key, workflow_name, user_request, additional_params))
1104
+ return True, issue_key
1105
+ else:
1106
+ print(f"\nāŒ Worktree setup failed: {result.error_message}")
1107
+ print(f" Issue {issue_key} was created but environment setup failed.")
1108
+ print(" Please set up the worktree manually:")
1109
+ print(f" git worktree add ../{issue_key} -b {branch_name}")
1110
+ return False, issue_key