aidp 0.33.0 → 0.34.0
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.
- checksums.yaml +4 -4
- data/README.md +35 -0
- data/lib/aidp/analyze/tree_sitter_scan.rb +3 -0
- data/lib/aidp/cli/eval_command.rb +399 -0
- data/lib/aidp/cli/harness_command.rb +1 -1
- data/lib/aidp/cli/security_command.rb +416 -0
- data/lib/aidp/cli/tools_command.rb +6 -4
- data/lib/aidp/cli.rb +170 -3
- data/lib/aidp/concurrency/exec.rb +3 -0
- data/lib/aidp/config.rb +113 -0
- data/lib/aidp/config_paths.rb +20 -0
- data/lib/aidp/daemon/runner.rb +8 -4
- data/lib/aidp/errors.rb +134 -0
- data/lib/aidp/evaluations/context_capture.rb +205 -0
- data/lib/aidp/evaluations/evaluation_record.rb +114 -0
- data/lib/aidp/evaluations/evaluation_storage.rb +250 -0
- data/lib/aidp/evaluations.rb +23 -0
- data/lib/aidp/execute/async_work_loop_runner.rb +4 -1
- data/lib/aidp/execute/interactive_repl.rb +6 -2
- data/lib/aidp/execute/prompt_evaluator.rb +359 -0
- data/lib/aidp/execute/repl_macros.rb +100 -1
- data/lib/aidp/execute/work_loop_runner.rb +399 -47
- data/lib/aidp/execute/work_loop_state.rb +4 -1
- data/lib/aidp/execute/workflow_selector.rb +3 -0
- data/lib/aidp/harness/ai_decision_engine.rb +79 -0
- data/lib/aidp/harness/capability_registry.rb +2 -0
- data/lib/aidp/harness/condition_detector.rb +3 -0
- data/lib/aidp/harness/config_loader.rb +3 -0
- data/lib/aidp/harness/enhanced_runner.rb +14 -11
- data/lib/aidp/harness/error_handler.rb +3 -0
- data/lib/aidp/harness/provider_factory.rb +3 -0
- data/lib/aidp/harness/provider_manager.rb +6 -0
- data/lib/aidp/harness/runner.rb +5 -1
- data/lib/aidp/harness/state/persistence.rb +3 -0
- data/lib/aidp/harness/state_manager.rb +3 -0
- data/lib/aidp/harness/status_display.rb +28 -20
- data/lib/aidp/harness/thinking_depth_manager.rb +32 -32
- data/lib/aidp/harness/ui/enhanced_tui.rb +4 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +4 -0
- data/lib/aidp/harness/ui/error_handler.rb +3 -0
- data/lib/aidp/harness/ui/job_monitor.rb +4 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +2 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +6 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +3 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +3 -0
- data/lib/aidp/harness/user_interface.rb +3 -0
- data/lib/aidp/loader.rb +2 -2
- data/lib/aidp/logger.rb +3 -0
- data/lib/aidp/message_display.rb +31 -0
- data/lib/aidp/pr_worktree_manager.rb +18 -6
- data/lib/aidp/provider_manager.rb +3 -0
- data/lib/aidp/providers/base.rb +2 -0
- data/lib/aidp/security/rule_of_two_enforcer.rb +210 -0
- data/lib/aidp/security/secrets_proxy.rb +328 -0
- data/lib/aidp/security/secrets_registry.rb +227 -0
- data/lib/aidp/security/trifecta_state.rb +220 -0
- data/lib/aidp/security/watch_mode_handler.rb +306 -0
- data/lib/aidp/security/work_loop_adapter.rb +277 -0
- data/lib/aidp/security.rb +56 -0
- data/lib/aidp/setup/wizard.rb +4 -2
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/auto_merger.rb +274 -0
- data/lib/aidp/watch/auto_pr_processor.rb +125 -7
- data/lib/aidp/watch/build_processor.rb +16 -1
- data/lib/aidp/watch/change_request_processor.rb +680 -286
- data/lib/aidp/watch/ci_fix_processor.rb +262 -4
- data/lib/aidp/watch/feedback_collector.rb +191 -0
- data/lib/aidp/watch/hierarchical_pr_strategy.rb +256 -0
- data/lib/aidp/watch/implementation_verifier.rb +142 -1
- data/lib/aidp/watch/plan_generator.rb +70 -13
- data/lib/aidp/watch/plan_processor.rb +12 -5
- data/lib/aidp/watch/projects_processor.rb +286 -0
- data/lib/aidp/watch/repository_client.rb +861 -53
- data/lib/aidp/watch/review_processor.rb +33 -6
- data/lib/aidp/watch/runner.rb +51 -11
- data/lib/aidp/watch/state_store.rb +233 -0
- data/lib/aidp/watch/sub_issue_creator.rb +221 -0
- data/lib/aidp/workflows/guided_agent.rb +4 -0
- data/lib/aidp/workstream_executor.rb +3 -0
- data/lib/aidp/worktree.rb +61 -11
- data/lib/aidp/worktree_branch_manager.rb +347 -101
- data/templates/implementation/iterative_implementation.md +46 -3
- metadata +20 -1
|
@@ -1,124 +1,341 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "fileutils"
|
|
3
|
+
require "open3"
|
|
3
4
|
|
|
4
5
|
module Aidp
|
|
5
6
|
# Manages git worktrees for pull request branches
|
|
6
7
|
class WorktreeBranchManager
|
|
7
8
|
class WorktreeCreationError < StandardError; end
|
|
8
9
|
class WorktreeLookupError < StandardError; end
|
|
10
|
+
class PullRequestBranchExtractionError < StandardError; end
|
|
9
11
|
|
|
10
12
|
# Initialize with a project directory and optional logger
|
|
11
13
|
def initialize(project_dir:, logger: Aidp.logger)
|
|
12
14
|
@project_dir = project_dir
|
|
13
15
|
@logger = logger
|
|
14
16
|
@worktree_registry_path = File.join(project_dir, ".aidp", "worktrees.json")
|
|
15
|
-
@pr_worktree_registry_path = File.join(project_dir, ".aidp", "pr_worktrees.json")
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
# Find an existing worktree for a given branch or PR
|
|
19
|
-
def find_worktree(branch:)
|
|
20
|
-
Aidp.log_debug("worktree_branch_manager", "finding_worktree",
|
|
19
|
+
# Find an existing worktree for a given branch or PR number
|
|
20
|
+
def find_worktree(branch: nil, pr_number: nil)
|
|
21
|
+
Aidp.log_debug("worktree_branch_manager", "finding_worktree",
|
|
22
|
+
branch: branch, pr_number: pr_number,
|
|
23
|
+
caller: caller(1..1).first)
|
|
21
24
|
|
|
25
|
+
# Validate inputs
|
|
22
26
|
raise WorktreeLookupError, "Invalid git repository: #{@project_dir}" unless git_repository?
|
|
23
27
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
# Handle PR number by extracting branch first
|
|
29
|
+
if pr_number
|
|
30
|
+
begin
|
|
31
|
+
branch = get_pr_branch(pr_number)
|
|
32
|
+
rescue => e
|
|
33
|
+
Aidp.log_warn("worktree_branch_manager", "pr_branch_extraction_failed",
|
|
34
|
+
pr_number: pr_number, error: e.message)
|
|
35
|
+
branch = nil
|
|
36
|
+
end
|
|
30
37
|
end
|
|
31
38
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
# Validate branch input after extraction attempt
|
|
40
|
+
raise WorktreeLookupError, "Branch or PR number must be provided" if branch.nil?
|
|
41
|
+
|
|
42
|
+
# Comprehensive lookup strategies
|
|
43
|
+
lookup_strategies = [
|
|
44
|
+
# 1. Check in-memory registry first
|
|
45
|
+
-> {
|
|
46
|
+
registry = read_registry
|
|
47
|
+
worktree_info = registry.find { |w| w["branch"] == branch }
|
|
48
|
+
|
|
49
|
+
if worktree_info
|
|
50
|
+
worktree_path = worktree_info["path"]
|
|
51
|
+
return worktree_path if File.directory?(worktree_path)
|
|
52
|
+
end
|
|
53
|
+
nil
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
# 2. Check PR-specific registry
|
|
57
|
+
-> {
|
|
58
|
+
pr_registry = read_pr_registry
|
|
59
|
+
pr_entry = pr_registry.find { |entry| entry["head_branch"] == branch }
|
|
60
|
+
|
|
61
|
+
if pr_entry
|
|
62
|
+
worktree_path = pr_entry["path"]
|
|
63
|
+
return worktree_path if File.directory?(worktree_path)
|
|
64
|
+
end
|
|
65
|
+
nil
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
# 3. Use git worktree list for broader search
|
|
69
|
+
-> {
|
|
70
|
+
begin
|
|
71
|
+
worktree_list_output = run_git_command(["git", "worktree", "list"])
|
|
72
|
+
worktree_list_output.split("\n").each do |line|
|
|
73
|
+
path, branch_info = line.split(" ", 2)
|
|
74
|
+
return path if branch_info&.include?(branch)
|
|
75
|
+
end
|
|
76
|
+
rescue => e
|
|
77
|
+
Aidp.log_warn("worktree_branch_manager", "git_worktree_list_fallback_failed",
|
|
78
|
+
error: e.message)
|
|
79
|
+
end
|
|
80
|
+
nil
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
# 4. Scan .worktrees directory manually
|
|
84
|
+
-> {
|
|
85
|
+
worktree_base_path = File.join(@project_dir, ".worktrees")
|
|
86
|
+
candidates = Dir.glob(File.join(worktree_base_path, "**")).select do |path|
|
|
87
|
+
File.directory?(path) && path.end_with?(branch.tr("/", "_"))
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
return candidates.first unless candidates.empty?
|
|
91
|
+
nil
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
# Execute lookup strategies
|
|
96
|
+
lookup_strategies.each do |strategy|
|
|
97
|
+
result = strategy.call
|
|
98
|
+
return result if result
|
|
37
99
|
end
|
|
38
100
|
|
|
101
|
+
# If no worktree found
|
|
102
|
+
Aidp.log_warn("worktree_branch_manager", "no_worktree_found",
|
|
103
|
+
branch: branch, pr_number: pr_number)
|
|
39
104
|
nil
|
|
40
105
|
rescue => e
|
|
41
106
|
Aidp.log_error("worktree_branch_manager", "worktree_lookup_failed",
|
|
42
|
-
error: e.message, branch: branch)
|
|
107
|
+
error: e.message, branch: branch, pr_number: pr_number)
|
|
43
108
|
raise
|
|
44
109
|
end
|
|
45
110
|
|
|
46
|
-
#
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
|
|
111
|
+
# Create a new worktree for a branch, with advanced PR-aware handling
|
|
112
|
+
def create_worktree(
|
|
113
|
+
branch: nil,
|
|
114
|
+
pr_number: nil,
|
|
115
|
+
base_branch: "main",
|
|
116
|
+
force_recreate: false,
|
|
117
|
+
unique_suffix: nil
|
|
118
|
+
)
|
|
119
|
+
# Log detailed input for debugging
|
|
120
|
+
Aidp.log_debug("worktree_branch_manager", "create_worktree_start",
|
|
121
|
+
input_branch: branch,
|
|
122
|
+
pr_number: pr_number,
|
|
123
|
+
base_branch: base_branch,
|
|
124
|
+
force_recreate: force_recreate,
|
|
125
|
+
caller: caller(1..1).first)
|
|
126
|
+
|
|
127
|
+
# Normalize branch input
|
|
128
|
+
branch ||= get_pr_branch(pr_number) if pr_number
|
|
129
|
+
|
|
130
|
+
# Validate inputs
|
|
131
|
+
validate_branch_name!(branch)
|
|
132
|
+
raise ArgumentError, "Branch must be provided" if branch.nil? || branch.empty?
|
|
50
133
|
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
134
|
+
# Comprehensive worktree name generation
|
|
135
|
+
worktree_name = branch.tr("/", "_")
|
|
136
|
+
worktree_name += "-#{unique_suffix}" if unique_suffix
|
|
137
|
+
worktree_path = File.join(@project_dir, ".worktrees", worktree_name)
|
|
54
138
|
|
|
55
|
-
#
|
|
56
|
-
|
|
57
|
-
worktree_path = pr_worktree["path"]
|
|
58
|
-
return worktree_path if File.directory?(worktree_path)
|
|
59
|
-
end
|
|
139
|
+
# Ensure .worktrees directory exists
|
|
140
|
+
FileUtils.mkdir_p(File.join(@project_dir, ".worktrees"))
|
|
60
141
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
142
|
+
# Existing worktree handling - only for non-PR-specific worktrees
|
|
143
|
+
# For PR-specific worktrees, we always create a new one with unique naming
|
|
144
|
+
unless pr_number
|
|
145
|
+
existing_worktree = find_worktree(branch: branch)
|
|
146
|
+
if existing_worktree
|
|
147
|
+
# Log existing worktree details
|
|
148
|
+
Aidp.log_debug("worktree_branch_manager", "existing_worktree_found",
|
|
149
|
+
existing_path: existing_worktree,
|
|
150
|
+
force_recreate: force_recreate)
|
|
151
|
+
|
|
152
|
+
if force_recreate
|
|
153
|
+
# Attempt to remove existing worktree
|
|
154
|
+
begin
|
|
155
|
+
run_git_command(["git", "worktree", "remove", existing_worktree])
|
|
156
|
+
Aidp.log_debug("worktree_branch_manager", "existing_worktree_removed",
|
|
157
|
+
path: existing_worktree)
|
|
158
|
+
rescue => e
|
|
159
|
+
Aidp.log_warn("worktree_branch_manager", "worktree_removal_failed",
|
|
160
|
+
path: existing_worktree,
|
|
161
|
+
error: e.message)
|
|
162
|
+
end
|
|
163
|
+
else
|
|
164
|
+
return existing_worktree
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
64
168
|
|
|
65
|
-
#
|
|
66
|
-
|
|
169
|
+
# Resolve base branch with multiple strategies
|
|
170
|
+
resolved_base_branch =
|
|
171
|
+
begin
|
|
172
|
+
resolve_base_branch(branch, base_branch, pr_number)
|
|
173
|
+
rescue => e
|
|
174
|
+
Aidp.log_warn("worktree_branch_manager", "base_branch_resolution_fallback",
|
|
175
|
+
error: e.message,
|
|
176
|
+
fallback_base: base_branch)
|
|
177
|
+
base_branch
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# For PR-specific worktrees, create unique branch name to avoid conflicts
|
|
181
|
+
effective_branch = pr_number ? "#{branch}-pr-#{pr_number}" : branch
|
|
182
|
+
|
|
183
|
+
# Comprehensive worktree creation strategies
|
|
184
|
+
creation_strategies = [
|
|
185
|
+
# Strategy 1: Create with remote tracking
|
|
186
|
+
-> {
|
|
187
|
+
Aidp.log_debug("worktree_branch_manager", "create_strategy_remote_tracking",
|
|
188
|
+
branch: effective_branch,
|
|
189
|
+
base_branch: resolved_base_branch)
|
|
190
|
+
run_git_command(["git", "fetch", "origin", resolved_base_branch])
|
|
191
|
+
run_git_command(["git", "worktree", "add", "-b", effective_branch, worktree_path, "origin/#{resolved_base_branch}"])
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
# Strategy 2: Create without remote tracking
|
|
195
|
+
-> {
|
|
196
|
+
Aidp.log_debug("worktree_branch_manager", "create_strategy_local_branch",
|
|
197
|
+
branch: effective_branch,
|
|
198
|
+
base_branch: resolved_base_branch)
|
|
199
|
+
run_git_command(["git", "worktree", "add", "-b", effective_branch, worktree_path, resolved_base_branch])
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
# Strategy 3: Checkout existing branch (for when branch already exists)
|
|
203
|
+
-> {
|
|
204
|
+
Aidp.log_debug("worktree_branch_manager", "create_strategy_existing_branch",
|
|
205
|
+
branch: effective_branch,
|
|
206
|
+
base_branch: resolved_base_branch)
|
|
207
|
+
run_git_command(["git", "worktree", "add", worktree_path, effective_branch])
|
|
208
|
+
}
|
|
209
|
+
]
|
|
210
|
+
|
|
211
|
+
# Attempt worktree creation with multiple strategies
|
|
212
|
+
creation_error = nil
|
|
213
|
+
creation_strategies.each do |strategy|
|
|
214
|
+
strategy.call
|
|
215
|
+
|
|
216
|
+
# Validate worktree creation
|
|
217
|
+
unless File.directory?(worktree_path)
|
|
218
|
+
raise WorktreeCreationError, "Worktree directory not created"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Update registries
|
|
222
|
+
update_registry(branch, worktree_path)
|
|
223
|
+
|
|
224
|
+
# Success logging
|
|
225
|
+
Aidp.log_debug("worktree_branch_manager", "worktree_created",
|
|
226
|
+
branch: branch,
|
|
227
|
+
path: worktree_path,
|
|
228
|
+
strategy: strategy.source_location&.first)
|
|
229
|
+
|
|
230
|
+
return worktree_path
|
|
231
|
+
rescue => e
|
|
232
|
+
# Log strategy failure
|
|
233
|
+
Aidp.log_warn("worktree_branch_manager", "worktree_creation_strategy_failed",
|
|
234
|
+
error: e.message,
|
|
235
|
+
strategy: strategy.source_location&.first)
|
|
236
|
+
creation_error = e
|
|
237
|
+
end
|
|
67
238
|
|
|
68
|
-
#
|
|
69
|
-
|
|
239
|
+
# If all strategies fail, raise comprehensive error
|
|
240
|
+
Aidp.log_error("worktree_branch_manager", "worktree_creation_failed",
|
|
241
|
+
branch: branch,
|
|
242
|
+
base_branch: resolved_base_branch,
|
|
243
|
+
pr_number: pr_number,
|
|
244
|
+
error: creation_error&.message)
|
|
70
245
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Aidp.log_error("worktree_branch_manager", "pr_worktree_creation_failed",
|
|
74
|
-
error: e.message, pr_number: pr_number, head_branch: head_branch)
|
|
75
|
-
raise
|
|
246
|
+
raise WorktreeCreationError,
|
|
247
|
+
"Failed to create worktree for branch #{branch}: #{creation_error&.message}"
|
|
76
248
|
end
|
|
77
249
|
|
|
78
|
-
#
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
# Validate branch name to prevent path traversal
|
|
84
|
-
validate_branch_name!(branch)
|
|
85
|
-
|
|
86
|
-
# Check if worktree already exists
|
|
87
|
-
existing_worktree = find_worktree(branch: branch)
|
|
88
|
-
return existing_worktree if existing_worktree
|
|
89
|
-
|
|
90
|
-
# Ensure base branch exists
|
|
91
|
-
base_ref = (branch == "main") ? "main" : "refs/heads/#{base_branch}"
|
|
92
|
-
base_exists_cmd = "git show-ref --verify --quiet #{base_ref}"
|
|
250
|
+
# Find or create a worktree specifically for a PR branch
|
|
251
|
+
def find_or_create_pr_worktree(pr_number:, head_branch:, base_branch: "main")
|
|
252
|
+
# Validate required parameters
|
|
253
|
+
raise ArgumentError, "PR number is required" if pr_number.nil?
|
|
254
|
+
raise WorktreeCreationError, "Head branch is required" if head_branch.nil? || head_branch.empty?
|
|
93
255
|
|
|
94
|
-
|
|
256
|
+
# Comprehensive logging of input parameters
|
|
257
|
+
Aidp.log_debug("worktree_branch_manager", "finding_or_creating_pr_worktree",
|
|
258
|
+
pr_number: pr_number,
|
|
259
|
+
head_branch: head_branch,
|
|
260
|
+
base_branch: base_branch)
|
|
95
261
|
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
262
|
+
# Check PR-specific registry first
|
|
263
|
+
pr_registry = read_pr_registry
|
|
264
|
+
existing_pr_entry = pr_registry.find { |entry| entry["pr_number"] == pr_number }
|
|
265
|
+
|
|
266
|
+
# Handle existing PR worktree
|
|
267
|
+
if existing_pr_entry
|
|
268
|
+
existing_path = existing_pr_entry["path"]
|
|
269
|
+
if File.directory?(existing_path)
|
|
270
|
+
Aidp.log_debug("worktree_branch_manager", "found_existing_pr_worktree",
|
|
271
|
+
pr_number: pr_number, path: existing_path)
|
|
272
|
+
return existing_path
|
|
273
|
+
else
|
|
274
|
+
# Clean up invalid entry
|
|
275
|
+
pr_registry.reject! { |entry| entry["pr_number"] == pr_number }
|
|
276
|
+
write_pr_registry(pr_registry)
|
|
277
|
+
end
|
|
99
278
|
end
|
|
100
279
|
|
|
101
|
-
#
|
|
102
|
-
|
|
103
|
-
worktree_path = File.join(@project_dir, ".worktrees", worktree_name)
|
|
104
|
-
|
|
105
|
-
# Ensure .worktrees directory exists
|
|
106
|
-
FileUtils.mkdir_p(File.join(@project_dir, ".worktrees"))
|
|
280
|
+
# Determine unique worktree name for PR-specific worktrees
|
|
281
|
+
unique_suffix = "pr-#{pr_number}"
|
|
107
282
|
|
|
108
|
-
# Create
|
|
109
|
-
|
|
110
|
-
|
|
283
|
+
# Create worktree using advanced method
|
|
284
|
+
begin
|
|
285
|
+
worktree_path = create_worktree(
|
|
286
|
+
branch: head_branch,
|
|
287
|
+
pr_number: pr_number,
|
|
288
|
+
base_branch: base_branch,
|
|
289
|
+
unique_suffix: unique_suffix
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Update PR-specific registry with complete metadata
|
|
293
|
+
update_pr_registry(pr_number, head_branch, base_branch, worktree_path)
|
|
294
|
+
|
|
295
|
+
Aidp.log_debug("worktree_branch_manager", "pr_worktree_created_complete",
|
|
296
|
+
pr_number: pr_number,
|
|
297
|
+
head_branch: head_branch,
|
|
298
|
+
path: worktree_path)
|
|
299
|
+
|
|
300
|
+
worktree_path
|
|
301
|
+
rescue => e
|
|
302
|
+
# Log comprehensive error details
|
|
303
|
+
Aidp.log_error("worktree_branch_manager", "pr_worktree_creation_failed",
|
|
304
|
+
pr_number: pr_number,
|
|
305
|
+
head_branch: head_branch,
|
|
306
|
+
base_branch: base_branch,
|
|
307
|
+
error: e.message,
|
|
308
|
+
backtrace: e.backtrace.first(5))
|
|
309
|
+
|
|
310
|
+
raise WorktreeCreationError,
|
|
311
|
+
"Failed to create PR worktree for PR ##{pr_number}: #{e.message}"
|
|
312
|
+
end
|
|
313
|
+
end
|
|
111
314
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
315
|
+
# Resolve the best base branch for a given branch or PR
|
|
316
|
+
def resolve_base_branch(branch, default_base_branch, pr_number = nil)
|
|
317
|
+
# If PR number is provided, try to get base branch from GitHub
|
|
318
|
+
if pr_number
|
|
319
|
+
begin
|
|
320
|
+
pr_info_output = run_git_command(["gh", "pr", "view", pr_number.to_s, "--json", "baseRefName"])
|
|
321
|
+
pr_base_branch = JSON.parse(pr_info_output)["baseRefName"]
|
|
322
|
+
return pr_base_branch if pr_base_branch && !pr_base_branch.empty?
|
|
323
|
+
rescue => e
|
|
324
|
+
Aidp.log_warn("worktree_branch_manager", "pr_base_branch_extraction_failed",
|
|
325
|
+
pr_number: pr_number, error: e.message)
|
|
326
|
+
end
|
|
116
327
|
end
|
|
117
328
|
|
|
118
|
-
#
|
|
119
|
-
|
|
329
|
+
# Try to fetch origin branches
|
|
330
|
+
begin
|
|
331
|
+
run_git_command(["git", "fetch", "origin", default_base_branch])
|
|
332
|
+
rescue => e
|
|
333
|
+
Aidp.log_debug("worktree_branch_manager", "base_branch_fetch_failed",
|
|
334
|
+
base_branch: default_base_branch, error: e.message)
|
|
335
|
+
end
|
|
120
336
|
|
|
121
|
-
|
|
337
|
+
# Return fallback base branch
|
|
338
|
+
default_base_branch
|
|
122
339
|
end
|
|
123
340
|
|
|
124
341
|
private
|
|
@@ -137,8 +354,10 @@ module Aidp
|
|
|
137
354
|
|
|
138
355
|
def run_git_command(cmd)
|
|
139
356
|
Dir.chdir(@project_dir) do
|
|
140
|
-
|
|
141
|
-
|
|
357
|
+
# Convert string command to array for safe execution (no shell interpolation)
|
|
358
|
+
args = cmd.is_a?(Array) ? cmd : cmd.split
|
|
359
|
+
output, status = Open3.capture2e(*args)
|
|
360
|
+
raise StandardError, output unless status.success?
|
|
142
361
|
output
|
|
143
362
|
end
|
|
144
363
|
end
|
|
@@ -156,19 +375,6 @@ module Aidp
|
|
|
156
375
|
end
|
|
157
376
|
end
|
|
158
377
|
|
|
159
|
-
# Read the PR-specific worktree registry
|
|
160
|
-
def read_pr_registry
|
|
161
|
-
return [] unless File.exist?(@pr_worktree_registry_path)
|
|
162
|
-
|
|
163
|
-
begin
|
|
164
|
-
JSON.parse(File.read(@pr_worktree_registry_path))
|
|
165
|
-
rescue JSON::ParserError
|
|
166
|
-
Aidp.log_warn("worktree_branch_manager", "invalid_pr_registry",
|
|
167
|
-
path: @pr_worktree_registry_path)
|
|
168
|
-
[]
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
378
|
# Update the worktree registry
|
|
173
379
|
def update_registry(branch, path)
|
|
174
380
|
# Ensure .aidp directory exists
|
|
@@ -186,31 +392,71 @@ module Aidp
|
|
|
186
392
|
"created_at" => Time.now.to_i
|
|
187
393
|
}
|
|
188
394
|
|
|
189
|
-
# Write updated registry
|
|
190
395
|
File.write(@worktree_registry_path, JSON.pretty_generate(registry))
|
|
191
396
|
end
|
|
192
397
|
|
|
193
|
-
#
|
|
194
|
-
def
|
|
398
|
+
# Read the PR-specific worktree registry
|
|
399
|
+
def read_pr_registry
|
|
400
|
+
pr_registry_path = File.join(@project_dir, ".aidp", "pr_worktrees.json")
|
|
401
|
+
return [] unless File.exist?(pr_registry_path)
|
|
402
|
+
|
|
403
|
+
begin
|
|
404
|
+
JSON.parse(File.read(pr_registry_path))
|
|
405
|
+
rescue JSON::ParserError
|
|
406
|
+
Aidp.log_warn("worktree_branch_manager", "invalid_pr_registry",
|
|
407
|
+
path: pr_registry_path)
|
|
408
|
+
[]
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# Write the PR-specific worktree registry
|
|
413
|
+
def write_pr_registry(registry)
|
|
414
|
+
pr_registry_path = File.join(@project_dir, ".aidp", "pr_worktrees.json")
|
|
415
|
+
|
|
195
416
|
# Ensure .aidp directory exists
|
|
196
|
-
FileUtils.mkdir_p(File.dirname(
|
|
417
|
+
FileUtils.mkdir_p(File.dirname(pr_registry_path))
|
|
418
|
+
|
|
419
|
+
File.write(pr_registry_path, JSON.pretty_generate(registry))
|
|
420
|
+
end
|
|
197
421
|
|
|
198
|
-
|
|
422
|
+
# Update the PR-specific worktree registry
|
|
423
|
+
def update_pr_registry(pr_number, head_branch, base_branch, path)
|
|
424
|
+
pr_registry = read_pr_registry
|
|
199
425
|
|
|
200
|
-
# Remove existing entries for the same PR
|
|
201
|
-
|
|
426
|
+
# Remove existing entries for the same PR number
|
|
427
|
+
pr_registry.reject! { |entry| entry["pr_number"] == pr_number }
|
|
202
428
|
|
|
203
429
|
# Add new entry
|
|
204
|
-
|
|
430
|
+
pr_registry << {
|
|
205
431
|
"pr_number" => pr_number,
|
|
206
432
|
"head_branch" => head_branch,
|
|
207
433
|
"base_branch" => base_branch,
|
|
208
|
-
"path" =>
|
|
434
|
+
"path" => path,
|
|
209
435
|
"created_at" => Time.now.to_i
|
|
210
436
|
}
|
|
211
437
|
|
|
212
|
-
|
|
213
|
-
|
|
438
|
+
write_pr_registry(pr_registry)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Extract the PR branch from a GitHub Pull Request number
|
|
442
|
+
public def get_pr_branch(pr_number)
|
|
443
|
+
Aidp.log_debug("worktree_branch_manager", "extracting_pr_branch", pr_number: pr_number)
|
|
444
|
+
|
|
445
|
+
# Fetch pull request information from GitHub
|
|
446
|
+
begin
|
|
447
|
+
pr_info_output = run_git_command(["gh", "pr", "view", pr_number.to_s, "--json", "headRefName"])
|
|
448
|
+
pr_branch = JSON.parse(pr_info_output)["headRefName"]
|
|
449
|
+
|
|
450
|
+
if pr_branch.nil? || pr_branch.empty?
|
|
451
|
+
raise PullRequestBranchExtractionError, "Could not extract branch for PR #{pr_number}"
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
pr_branch
|
|
455
|
+
rescue => e
|
|
456
|
+
Aidp.log_error("worktree_branch_manager", "pr_branch_extraction_failed",
|
|
457
|
+
pr_number: pr_number, error: e.message)
|
|
458
|
+
raise PullRequestBranchExtractionError, "Failed to extract branch for PR #{pr_number}: #{e.message}"
|
|
459
|
+
end
|
|
214
460
|
end
|
|
215
461
|
end
|
|
216
462
|
end
|
|
@@ -6,11 +6,54 @@ You are implementing a feature or fix within the AIDP work loop using an iterati
|
|
|
6
6
|
|
|
7
7
|
{{task_description}}
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## ⚠️ CRITICAL: Task Filing Required
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**You MUST file tasks BEFORE beginning implementation.** The work loop requires at least one task to be created and completed for the work to be considered done.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Why Tasks Are Required
|
|
14
|
+
|
|
15
|
+
1. **Prevents premature completion** - Tasks ensure all requirements are tracked
|
|
16
|
+
2. **Enables progress tracking** - Each task represents verifiable progress
|
|
17
|
+
3. **Supports iteration** - If tests fail, tasks show what remains
|
|
18
|
+
4. **Audit trail** - Tasks document what was actually implemented
|
|
19
|
+
|
|
20
|
+
### First Action: File Your Tasks
|
|
21
|
+
|
|
22
|
+
**IMMEDIATELY** in your first iteration, file tasks for this work:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
File task: "description" priority: high|medium|low tags: tag1,tag2
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Example - If implementing a feature:**
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
File task: "Create core implementation for [feature name]" priority: high tags: implementation
|
|
32
|
+
File task: "Add unit tests for [feature name]" priority: high tags: testing
|
|
33
|
+
File task: "Add integration tests if needed" priority: medium tags: testing
|
|
34
|
+
File task: "Update documentation" priority: low tags: docs
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Example - If fixing a bug:**
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
File task: "Identify root cause of [bug description]" priority: high tags: investigation
|
|
41
|
+
File task: "Implement fix for [bug description]" priority: high tags: bugfix
|
|
42
|
+
File task: "Add regression test" priority: high tags: testing
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Task Filing Guidelines
|
|
46
|
+
|
|
47
|
+
- **At least one task is required** - You cannot complete without tasks
|
|
48
|
+
- **Tasks should be specific** - "Implement user auth" not "Do the work"
|
|
49
|
+
- **Include testing tasks** - Every implementation needs tests
|
|
50
|
+
- **Cover the full scope** - Review the requirements and ensure all are covered by tasks
|
|
51
|
+
|
|
52
|
+
## Implementation Process
|
|
53
|
+
|
|
54
|
+
### 1. File Tasks First (REQUIRED)
|
|
55
|
+
|
|
56
|
+
Break down the work into concrete subtasks using the persistent tasklist:
|
|
14
57
|
|
|
15
58
|
```text
|
|
16
59
|
File task: "Subtask description here" priority: high|medium|low tags: tag1,tag2
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: aidp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.34.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bart Agapinan
|
|
@@ -303,6 +303,7 @@ files:
|
|
|
303
303
|
- lib/aidp/cli/config_command.rb
|
|
304
304
|
- lib/aidp/cli/devcontainer_commands.rb
|
|
305
305
|
- lib/aidp/cli/enhanced_input.rb
|
|
306
|
+
- lib/aidp/cli/eval_command.rb
|
|
306
307
|
- lib/aidp/cli/first_run_wizard.rb
|
|
307
308
|
- lib/aidp/cli/harness_command.rb
|
|
308
309
|
- lib/aidp/cli/issue_importer.rb
|
|
@@ -310,6 +311,7 @@ files:
|
|
|
310
311
|
- lib/aidp/cli/mcp_dashboard.rb
|
|
311
312
|
- lib/aidp/cli/models_command.rb
|
|
312
313
|
- lib/aidp/cli/providers_command.rb
|
|
314
|
+
- lib/aidp/cli/security_command.rb
|
|
313
315
|
- lib/aidp/cli/terminal_io.rb
|
|
314
316
|
- lib/aidp/cli/tools_command.rb
|
|
315
317
|
- lib/aidp/comment_consolidator.rb
|
|
@@ -324,6 +326,10 @@ files:
|
|
|
324
326
|
- lib/aidp/daemon/runner.rb
|
|
325
327
|
- lib/aidp/debug_mixin.rb
|
|
326
328
|
- lib/aidp/errors.rb
|
|
329
|
+
- lib/aidp/evaluations.rb
|
|
330
|
+
- lib/aidp/evaluations/context_capture.rb
|
|
331
|
+
- lib/aidp/evaluations/evaluation_record.rb
|
|
332
|
+
- lib/aidp/evaluations/evaluation_storage.rb
|
|
327
333
|
- lib/aidp/execute/agent_signal_parser.rb
|
|
328
334
|
- lib/aidp/execute/async_work_loop_runner.rb
|
|
329
335
|
- lib/aidp/execute/checkpoint.rb
|
|
@@ -335,6 +341,7 @@ files:
|
|
|
335
341
|
- lib/aidp/execute/interactive_repl.rb
|
|
336
342
|
- lib/aidp/execute/persistent_tasklist.rb
|
|
337
343
|
- lib/aidp/execute/progress.rb
|
|
344
|
+
- lib/aidp/execute/prompt_evaluator.rb
|
|
338
345
|
- lib/aidp/execute/prompt_manager.rb
|
|
339
346
|
- lib/aidp/execute/repl_macros.rb
|
|
340
347
|
- lib/aidp/execute/runner.rb
|
|
@@ -459,6 +466,13 @@ files:
|
|
|
459
466
|
- lib/aidp/providers/opencode.rb
|
|
460
467
|
- lib/aidp/rescue_logging.rb
|
|
461
468
|
- lib/aidp/safe_directory.rb
|
|
469
|
+
- lib/aidp/security.rb
|
|
470
|
+
- lib/aidp/security/rule_of_two_enforcer.rb
|
|
471
|
+
- lib/aidp/security/secrets_proxy.rb
|
|
472
|
+
- lib/aidp/security/secrets_registry.rb
|
|
473
|
+
- lib/aidp/security/trifecta_state.rb
|
|
474
|
+
- lib/aidp/security/watch_mode_handler.rb
|
|
475
|
+
- lib/aidp/security/work_loop_adapter.rb
|
|
462
476
|
- lib/aidp/setup/devcontainer/backup_manager.rb
|
|
463
477
|
- lib/aidp/setup/devcontainer/generator.rb
|
|
464
478
|
- lib/aidp/setup/devcontainer/parser.rb
|
|
@@ -486,16 +500,20 @@ files:
|
|
|
486
500
|
- lib/aidp/utils/devcontainer_detector.rb
|
|
487
501
|
- lib/aidp/version.rb
|
|
488
502
|
- lib/aidp/watch.rb
|
|
503
|
+
- lib/aidp/watch/auto_merger.rb
|
|
489
504
|
- lib/aidp/watch/auto_pr_processor.rb
|
|
490
505
|
- lib/aidp/watch/auto_processor.rb
|
|
491
506
|
- lib/aidp/watch/build_processor.rb
|
|
492
507
|
- lib/aidp/watch/change_request_processor.rb
|
|
493
508
|
- lib/aidp/watch/ci_fix_processor.rb
|
|
494
509
|
- lib/aidp/watch/ci_log_extractor.rb
|
|
510
|
+
- lib/aidp/watch/feedback_collector.rb
|
|
495
511
|
- lib/aidp/watch/github_state_extractor.rb
|
|
512
|
+
- lib/aidp/watch/hierarchical_pr_strategy.rb
|
|
496
513
|
- lib/aidp/watch/implementation_verifier.rb
|
|
497
514
|
- lib/aidp/watch/plan_generator.rb
|
|
498
515
|
- lib/aidp/watch/plan_processor.rb
|
|
516
|
+
- lib/aidp/watch/projects_processor.rb
|
|
499
517
|
- lib/aidp/watch/repository_client.rb
|
|
500
518
|
- lib/aidp/watch/repository_safety_checker.rb
|
|
501
519
|
- lib/aidp/watch/review_processor.rb
|
|
@@ -505,6 +523,7 @@ files:
|
|
|
505
523
|
- lib/aidp/watch/reviewers/senior_dev_reviewer.rb
|
|
506
524
|
- lib/aidp/watch/runner.rb
|
|
507
525
|
- lib/aidp/watch/state_store.rb
|
|
526
|
+
- lib/aidp/watch/sub_issue_creator.rb
|
|
508
527
|
- lib/aidp/workflows/definitions.rb
|
|
509
528
|
- lib/aidp/workflows/guided_agent.rb
|
|
510
529
|
- lib/aidp/workflows/selector.rb
|