aidp 0.25.0 → 0.26.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 +45 -6
- data/lib/aidp/analyze/error_handler.rb +11 -0
- data/lib/aidp/execute/work_loop_runner.rb +225 -55
- data/lib/aidp/harness/config_loader.rb +20 -11
- data/lib/aidp/harness/config_schema.rb +30 -8
- data/lib/aidp/harness/configuration.rb +73 -2
- data/lib/aidp/harness/filter_strategy.rb +45 -0
- data/lib/aidp/harness/generic_filter_strategy.rb +63 -0
- data/lib/aidp/harness/output_filter.rb +136 -0
- data/lib/aidp/harness/provider_manager.rb +18 -3
- data/lib/aidp/harness/rspec_filter_strategy.rb +82 -0
- data/lib/aidp/harness/test_runner.rb +165 -27
- data/lib/aidp/harness/ui/enhanced_tui.rb +4 -1
- data/lib/aidp/logger.rb +35 -5
- data/lib/aidp/message_display.rb +46 -0
- data/lib/aidp/safe_directory.rb +10 -3
- data/lib/aidp/storage/csv_storage.rb +9 -3
- data/lib/aidp/storage/file_manager.rb +8 -2
- data/lib/aidp/storage/json_storage.rb +9 -3
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +40 -1
- data/lib/aidp/watch/change_request_processor.rb +659 -0
- data/lib/aidp/watch/plan_processor.rb +71 -8
- data/lib/aidp/watch/repository_client.rb +85 -20
- data/lib/aidp/watch/runner.rb +37 -0
- data/lib/aidp/watch/state_store.rb +46 -1
- data/lib/aidp/workstream_executor.rb +5 -2
- data/lib/aidp.rb +4 -0
- data/templates/aidp.yml.example +53 -0
- metadata +6 -1
|
@@ -41,21 +41,46 @@ module Aidp
|
|
|
41
41
|
|
|
42
42
|
def process(issue)
|
|
43
43
|
number = issue[:number]
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
existing_plan = @state_store.plan_data(number)
|
|
45
|
+
|
|
46
|
+
if existing_plan
|
|
47
|
+
display_message("🔄 Re-planning for issue ##{number} (iteration #{@state_store.plan_iteration_count(number) + 1})", type: :info)
|
|
48
|
+
else
|
|
49
|
+
display_message("🧠 Generating plan for issue ##{number} (#{issue[:title]})", type: :info)
|
|
47
50
|
end
|
|
48
51
|
|
|
49
|
-
display_message("🧠 Generating plan for issue ##{number} (#{issue[:title]})", type: :info)
|
|
50
52
|
plan_data = @plan_generator.generate(issue)
|
|
51
53
|
|
|
52
54
|
# Fetch the user who added the most recent label
|
|
53
55
|
label_actor = @repository_client.most_recent_label_actor(number)
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
# If updating existing plan, archive the previous content
|
|
58
|
+
archived_content = existing_plan ? archive_previous_plan(number, existing_plan) : nil
|
|
59
|
+
|
|
60
|
+
comment_body = build_comment(issue: issue, plan: plan_data, label_actor: label_actor, archived_content: archived_content)
|
|
61
|
+
|
|
62
|
+
if existing_plan && existing_plan["comment_id"]
|
|
63
|
+
# Update existing comment
|
|
64
|
+
@repository_client.update_comment(existing_plan["comment_id"], comment_body)
|
|
65
|
+
display_message("📝 Updated plan comment for issue ##{number}", type: :success)
|
|
66
|
+
elsif existing_plan
|
|
67
|
+
# Try to find existing comment by header
|
|
68
|
+
existing_comment = @repository_client.find_comment(number, COMMENT_HEADER)
|
|
69
|
+
if existing_comment
|
|
70
|
+
@repository_client.update_comment(existing_comment[:id], comment_body)
|
|
71
|
+
display_message("📝 Updated plan comment for issue ##{number}", type: :success)
|
|
72
|
+
plan_data = plan_data.merge(comment_id: existing_comment[:id])
|
|
73
|
+
else
|
|
74
|
+
# Fallback to posting new comment if we can't find the old one
|
|
75
|
+
@repository_client.post_comment(number, comment_body)
|
|
76
|
+
display_message("💬 Posted new plan comment for issue ##{number}", type: :success)
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
# First time planning - post new comment
|
|
80
|
+
@repository_client.post_comment(number, comment_body)
|
|
81
|
+
display_message("💬 Posted plan comment for issue ##{number}", type: :success)
|
|
82
|
+
end
|
|
57
83
|
|
|
58
|
-
display_message("💬 Posted plan comment for issue ##{number}", type: :success)
|
|
59
84
|
@state_store.record_plan(number, plan_data.merge(comment_body: comment_body, comment_hint: COMMENT_HEADER))
|
|
60
85
|
|
|
61
86
|
# Update labels: remove plan trigger, add appropriate status label
|
|
@@ -64,6 +89,31 @@ module Aidp
|
|
|
64
89
|
|
|
65
90
|
private
|
|
66
91
|
|
|
92
|
+
def archive_previous_plan(number, existing_plan)
|
|
93
|
+
iteration = @state_store.plan_iteration_count(number)
|
|
94
|
+
timestamp = existing_plan["posted_at"] || "unknown"
|
|
95
|
+
|
|
96
|
+
archived_parts = []
|
|
97
|
+
archived_parts << "<!-- ARCHIVED_PLAN_START iteration=#{iteration} timestamp=#{timestamp} -->"
|
|
98
|
+
archived_parts << "<details>"
|
|
99
|
+
archived_parts << "<summary>📋 Previous Plan (Iteration #{iteration}) - #{timestamp}</summary>"
|
|
100
|
+
archived_parts << ""
|
|
101
|
+
archived_parts << "<!-- ARCHIVED_PLAN_SUMMARY_START -->"
|
|
102
|
+
archived_parts << "### Plan Summary"
|
|
103
|
+
archived_parts << existing_plan["summary"].to_s
|
|
104
|
+
archived_parts << "<!-- ARCHIVED_PLAN_SUMMARY_END -->"
|
|
105
|
+
archived_parts << ""
|
|
106
|
+
archived_parts << "<!-- ARCHIVED_PLAN_TASKS_START -->"
|
|
107
|
+
archived_parts << "### Proposed Tasks"
|
|
108
|
+
archived_parts << format_bullets(Array(existing_plan["tasks"]), placeholder: "_No tasks_")
|
|
109
|
+
archived_parts << "<!-- ARCHIVED_PLAN_TASKS_END -->"
|
|
110
|
+
archived_parts << ""
|
|
111
|
+
archived_parts << "</details>"
|
|
112
|
+
archived_parts << "<!-- ARCHIVED_PLAN_END -->"
|
|
113
|
+
|
|
114
|
+
archived_parts.join("\n")
|
|
115
|
+
end
|
|
116
|
+
|
|
67
117
|
def update_labels_after_plan(number, plan_data)
|
|
68
118
|
questions = Array(plan_data[:questions])
|
|
69
119
|
has_questions = questions.any? && !questions.all? { |q| q.to_s.strip.empty? }
|
|
@@ -85,7 +135,7 @@ module Aidp
|
|
|
85
135
|
end
|
|
86
136
|
end
|
|
87
137
|
|
|
88
|
-
def build_comment(issue:, plan:, label_actor: nil)
|
|
138
|
+
def build_comment(issue:, plan:, label_actor: nil, archived_content: nil)
|
|
89
139
|
summary = plan[:summary].to_s.strip
|
|
90
140
|
tasks = Array(plan[:tasks])
|
|
91
141
|
questions = Array(plan[:questions])
|
|
@@ -104,14 +154,27 @@ module Aidp
|
|
|
104
154
|
parts << "**Issue**: [##{issue[:number]}](#{issue[:url]})"
|
|
105
155
|
parts << "**Title**: #{issue[:title]}"
|
|
106
156
|
parts << ""
|
|
157
|
+
|
|
158
|
+
# Add archived content if this is a plan update
|
|
159
|
+
if archived_content
|
|
160
|
+
parts << archived_content
|
|
161
|
+
parts << ""
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
parts << "<!-- PLAN_SUMMARY_START -->"
|
|
107
165
|
parts << "### Plan Summary"
|
|
108
166
|
parts << (summary.empty? ? "_No summary generated_" : summary)
|
|
167
|
+
parts << "<!-- PLAN_SUMMARY_END -->"
|
|
109
168
|
parts << ""
|
|
169
|
+
parts << "<!-- PLAN_TASKS_START -->"
|
|
110
170
|
parts << "### Proposed Tasks"
|
|
111
171
|
parts << format_bullets(tasks, placeholder: "_Pending task breakdown_")
|
|
172
|
+
parts << "<!-- PLAN_TASKS_END -->"
|
|
112
173
|
parts << ""
|
|
174
|
+
parts << "<!-- CLARIFYING_QUESTIONS_START -->"
|
|
113
175
|
parts << "### Clarifying Questions"
|
|
114
176
|
parts << format_numbered(questions, placeholder: "_No questions identified_")
|
|
177
|
+
parts << "<!-- CLARIFYING_QUESTIONS_END -->"
|
|
115
178
|
parts << ""
|
|
116
179
|
|
|
117
180
|
# Add instructions based on whether there are questions
|
|
@@ -61,6 +61,14 @@ module Aidp
|
|
|
61
61
|
gh_available? ? post_comment_via_gh(number, body) : post_comment_via_api(number, body)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def find_comment(number, header_text)
|
|
65
|
+
gh_available? ? find_comment_via_gh(number, header_text) : find_comment_via_api(number, header_text)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def update_comment(comment_id, body)
|
|
69
|
+
gh_available? ? update_comment_via_gh(comment_id, body) : update_comment_via_api(comment_id, body)
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
def create_pull_request(title:, body:, head:, base:, issue_number:, draft: false, assignee: nil)
|
|
65
73
|
gh_available? ? create_pull_request_via_gh(title: title, body: body, head: head, base: base, issue_number: issue_number, draft: draft, assignee: assignee) : raise("GitHub CLI not available - cannot create PR")
|
|
66
74
|
end
|
|
@@ -108,6 +116,10 @@ module Aidp
|
|
|
108
116
|
gh_available? ? list_pull_requests_via_gh(labels: labels, state: state) : list_pull_requests_via_api(labels: labels, state: state)
|
|
109
117
|
end
|
|
110
118
|
|
|
119
|
+
def fetch_pr_comments(number)
|
|
120
|
+
gh_available? ? fetch_pr_comments_via_gh(number) : fetch_pr_comments_via_api(number)
|
|
121
|
+
end
|
|
122
|
+
|
|
111
123
|
private
|
|
112
124
|
|
|
113
125
|
def list_issues_via_gh(labels:, state:)
|
|
@@ -162,27 +174,11 @@ module Aidp
|
|
|
162
174
|
raise "GitHub API error (#{response.code})" unless response.code == "200"
|
|
163
175
|
|
|
164
176
|
data = JSON.parse(response.body)
|
|
165
|
-
comments =
|
|
177
|
+
comments = fetch_pr_comments_via_api(number)
|
|
166
178
|
data["comments"] = comments
|
|
167
179
|
normalize_issue_detail_api(data)
|
|
168
180
|
end
|
|
169
181
|
|
|
170
|
-
def fetch_comments_via_api(number)
|
|
171
|
-
uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/comments")
|
|
172
|
-
response = Net::HTTP.get_response(uri)
|
|
173
|
-
return [] unless response.code == "200"
|
|
174
|
-
|
|
175
|
-
JSON.parse(response.body).map do |raw|
|
|
176
|
-
{
|
|
177
|
-
"body" => raw["body"],
|
|
178
|
-
"author" => raw.dig("user", "login"),
|
|
179
|
-
"createdAt" => raw["created_at"]
|
|
180
|
-
}
|
|
181
|
-
end
|
|
182
|
-
rescue
|
|
183
|
-
[]
|
|
184
|
-
end
|
|
185
|
-
|
|
186
182
|
def post_comment_via_gh(number, body)
|
|
187
183
|
cmd = ["gh", "issue", "comment", number.to_s, "--repo", full_repo, "--body", body]
|
|
188
184
|
stdout, stderr, status = Open3.capture3(*cmd)
|
|
@@ -205,6 +201,44 @@ module Aidp
|
|
|
205
201
|
response.body
|
|
206
202
|
end
|
|
207
203
|
|
|
204
|
+
def find_comment_via_gh(number, header_text)
|
|
205
|
+
comments = fetch_pr_comments_via_gh(number)
|
|
206
|
+
comments.find { |comment| comment[:body]&.include?(header_text) }
|
|
207
|
+
rescue => e
|
|
208
|
+
Aidp.log_warn("repository_client", "Failed to find comment", error: e.message)
|
|
209
|
+
nil
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def find_comment_via_api(number, header_text)
|
|
213
|
+
comments = fetch_pr_comments_via_api(number)
|
|
214
|
+
comments.find { |comment| comment[:body]&.include?(header_text) }
|
|
215
|
+
rescue => e
|
|
216
|
+
Aidp.log_warn("repository_client", "Failed to find comment", error: e.message)
|
|
217
|
+
nil
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def update_comment_via_gh(comment_id, body)
|
|
221
|
+
cmd = ["gh", "api", "repos/#{full_repo}/issues/comments/#{comment_id}", "-X", "PATCH", "-f", "body=#{body}"]
|
|
222
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
|
223
|
+
raise "Failed to update comment via gh: #{stderr.strip}" unless status.success?
|
|
224
|
+
|
|
225
|
+
stdout.strip
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def update_comment_via_api(comment_id, body)
|
|
229
|
+
uri = URI("https://api.github.com/repos/#{full_repo}/issues/comments/#{comment_id}")
|
|
230
|
+
request = Net::HTTP::Patch.new(uri)
|
|
231
|
+
request["Content-Type"] = "application/json"
|
|
232
|
+
request.body = JSON.dump({body: body})
|
|
233
|
+
|
|
234
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
235
|
+
http.request(request)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
raise "GitHub API update comment failed (#{response.code})" unless response.code.start_with?("2")
|
|
239
|
+
response.body
|
|
240
|
+
end
|
|
241
|
+
|
|
208
242
|
def create_pull_request_via_gh(title:, body:, head:, base:, issue_number:, draft: false, assignee: nil)
|
|
209
243
|
cmd = [
|
|
210
244
|
"gh", "pr", "create",
|
|
@@ -535,6 +569,27 @@ module Aidp
|
|
|
535
569
|
response.body
|
|
536
570
|
end
|
|
537
571
|
|
|
572
|
+
def fetch_pr_comments_via_gh(number)
|
|
573
|
+
cmd = ["gh", "api", "repos/#{full_repo}/issues/#{number}/comments", "--jq", "."]
|
|
574
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
|
575
|
+
raise "Failed to fetch PR comments via gh: #{stderr.strip}" unless status.success?
|
|
576
|
+
|
|
577
|
+
JSON.parse(stdout).map { |raw| normalize_pr_comment(raw) }
|
|
578
|
+
rescue JSON::ParserError => e
|
|
579
|
+
raise "Failed to parse PR comments response: #{e.message}"
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
def fetch_pr_comments_via_api(number)
|
|
583
|
+
uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/comments")
|
|
584
|
+
response = Net::HTTP.get_response(uri)
|
|
585
|
+
return [] unless response.code == "200"
|
|
586
|
+
|
|
587
|
+
JSON.parse(response.body).map { |raw| normalize_pr_comment(raw) }
|
|
588
|
+
rescue => e
|
|
589
|
+
Aidp.log_warn("repository_client", "Failed to fetch PR comments", error: e.message)
|
|
590
|
+
[]
|
|
591
|
+
end
|
|
592
|
+
|
|
538
593
|
# Normalization methods for PRs
|
|
539
594
|
def normalize_pull_request(raw)
|
|
540
595
|
{
|
|
@@ -691,14 +746,24 @@ module Aidp
|
|
|
691
746
|
def normalize_comment(comment)
|
|
692
747
|
if comment.is_a?(Hash)
|
|
693
748
|
{
|
|
694
|
-
"body" => comment["body"],
|
|
695
|
-
"author" => comment["author"] || comment.dig("user", "login"),
|
|
696
|
-
"createdAt" => comment["createdAt"] || comment["created_at"]
|
|
749
|
+
"body" => comment["body"] || comment[:body],
|
|
750
|
+
"author" => comment["author"] || comment[:author] || comment.dig("user", "login"),
|
|
751
|
+
"createdAt" => comment["createdAt"] || comment[:created_at] || comment["created_at"]
|
|
697
752
|
}
|
|
698
753
|
else
|
|
699
754
|
{"body" => comment.to_s}
|
|
700
755
|
end
|
|
701
756
|
end
|
|
757
|
+
|
|
758
|
+
def normalize_pr_comment(raw)
|
|
759
|
+
{
|
|
760
|
+
id: raw["id"],
|
|
761
|
+
body: raw["body"],
|
|
762
|
+
author: raw.dig("user", "login"),
|
|
763
|
+
created_at: raw["created_at"],
|
|
764
|
+
updated_at: raw["updated_at"]
|
|
765
|
+
}
|
|
766
|
+
end
|
|
702
767
|
end
|
|
703
768
|
end
|
|
704
769
|
end
|
data/lib/aidp/watch/runner.rb
CHANGED
|
@@ -12,6 +12,7 @@ require_relative "build_processor"
|
|
|
12
12
|
require_relative "../auto_update"
|
|
13
13
|
require_relative "review_processor"
|
|
14
14
|
require_relative "ci_fix_processor"
|
|
15
|
+
require_relative "change_request_processor"
|
|
15
16
|
|
|
16
17
|
module Aidp
|
|
17
18
|
module Watch
|
|
@@ -77,6 +78,16 @@ module Aidp
|
|
|
77
78
|
label_config: label_config,
|
|
78
79
|
verbose: verbose
|
|
79
80
|
)
|
|
81
|
+
@change_request_processor = ChangeRequestProcessor.new(
|
|
82
|
+
repository_client: @repository_client,
|
|
83
|
+
state_store: @state_store,
|
|
84
|
+
provider_name: provider_name,
|
|
85
|
+
project_dir: project_dir,
|
|
86
|
+
label_config: label_config,
|
|
87
|
+
change_request_config: safety_config[:pr_change_requests] || safety_config["pr_change_requests"] || {},
|
|
88
|
+
safety_config: safety_config[:safety] || safety_config["safety"] || {},
|
|
89
|
+
verbose: verbose
|
|
90
|
+
)
|
|
80
91
|
end
|
|
81
92
|
|
|
82
93
|
def start
|
|
@@ -122,6 +133,7 @@ module Aidp
|
|
|
122
133
|
check_for_updates_if_due
|
|
123
134
|
process_review_triggers
|
|
124
135
|
process_ci_fix_triggers
|
|
136
|
+
process_change_request_triggers
|
|
125
137
|
end
|
|
126
138
|
|
|
127
139
|
def process_plan_triggers
|
|
@@ -230,6 +242,31 @@ module Aidp
|
|
|
230
242
|
end
|
|
231
243
|
end
|
|
232
244
|
|
|
245
|
+
def process_change_request_triggers
|
|
246
|
+
change_request_label = @change_request_processor.change_request_label
|
|
247
|
+
prs = @repository_client.list_pull_requests(labels: [change_request_label], state: "open")
|
|
248
|
+
Aidp.log_debug("watch_runner", "change_request_poll", label: change_request_label, total: prs.size)
|
|
249
|
+
prs.each do |pr|
|
|
250
|
+
unless pr_has_label?(pr, change_request_label)
|
|
251
|
+
Aidp.log_debug("watch_runner", "change_request_skip_label_mismatch", pr: pr[:number], labels: pr[:labels])
|
|
252
|
+
next
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
detailed = @repository_client.fetch_pull_request(pr[:number])
|
|
256
|
+
|
|
257
|
+
# Check author authorization before processing
|
|
258
|
+
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
259
|
+
Aidp.log_debug("watch_runner", "change_request_skip_unauthorized_author", pr: detailed[:number], author: detailed[:author])
|
|
260
|
+
next
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
Aidp.log_debug("watch_runner", "change_request_process", pr: detailed[:number])
|
|
264
|
+
@change_request_processor.process(detailed)
|
|
265
|
+
rescue RepositorySafetyChecker::UnauthorizedAuthorError => e
|
|
266
|
+
Aidp.log_warn("watch_runner", "unauthorized PR author", pr: pr[:number], error: e.message)
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
233
270
|
def issue_has_label?(issue, label)
|
|
234
271
|
Array(issue[:labels]).any? do |issue_label|
|
|
235
272
|
name = issue_label.is_a?(Hash) ? issue_label["name"] : issue_label.to_s
|
|
@@ -27,14 +27,26 @@ module Aidp
|
|
|
27
27
|
plans[issue_number.to_s]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def plan_iteration_count(issue_number)
|
|
31
|
+
plan = plans[issue_number.to_s]
|
|
32
|
+
return 0 unless plan
|
|
33
|
+
plan["iteration"] || 1
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
def record_plan(issue_number, data)
|
|
37
|
+
existing_plan = plans[issue_number.to_s]
|
|
38
|
+
iteration = existing_plan ? (existing_plan["iteration"] || 1) + 1 : 1
|
|
39
|
+
|
|
31
40
|
payload = {
|
|
32
41
|
"summary" => data[:summary],
|
|
33
42
|
"tasks" => data[:tasks],
|
|
34
43
|
"questions" => data[:questions],
|
|
35
44
|
"comment_body" => data[:comment_body],
|
|
36
45
|
"comment_hint" => data[:comment_hint],
|
|
37
|
-
"
|
|
46
|
+
"comment_id" => data[:comment_id],
|
|
47
|
+
"posted_at" => data[:posted_at] || Time.now.utc.iso8601,
|
|
48
|
+
"iteration" => iteration,
|
|
49
|
+
"previous_iteration_at" => existing_plan ? existing_plan["posted_at"] : nil
|
|
38
50
|
}.compact
|
|
39
51
|
|
|
40
52
|
plans[issue_number.to_s] = payload
|
|
@@ -96,6 +108,34 @@ module Aidp
|
|
|
96
108
|
save!
|
|
97
109
|
end
|
|
98
110
|
|
|
111
|
+
# Change request tracking methods
|
|
112
|
+
def change_request_processed?(pr_number)
|
|
113
|
+
change_requests.key?(pr_number.to_s)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def change_request_data(pr_number)
|
|
117
|
+
change_requests[pr_number.to_s]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def record_change_request(pr_number, data)
|
|
121
|
+
payload = {
|
|
122
|
+
"status" => data[:status],
|
|
123
|
+
"timestamp" => data[:timestamp] || Time.now.utc.iso8601,
|
|
124
|
+
"changes_applied" => data[:changes_applied],
|
|
125
|
+
"commits" => data[:commits],
|
|
126
|
+
"reason" => data[:reason],
|
|
127
|
+
"clarification_count" => data[:clarification_count]
|
|
128
|
+
}.compact
|
|
129
|
+
|
|
130
|
+
change_requests[pr_number.to_s] = payload
|
|
131
|
+
save!
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def reset_change_request_state(pr_number)
|
|
135
|
+
change_requests.delete(pr_number.to_s)
|
|
136
|
+
save!
|
|
137
|
+
end
|
|
138
|
+
|
|
99
139
|
private
|
|
100
140
|
|
|
101
141
|
def ensure_directory
|
|
@@ -126,6 +166,7 @@ module Aidp
|
|
|
126
166
|
base["builds"] ||= {}
|
|
127
167
|
base["reviews"] ||= {}
|
|
128
168
|
base["ci_fixes"] ||= {}
|
|
169
|
+
base["change_requests"] ||= {}
|
|
129
170
|
base
|
|
130
171
|
end
|
|
131
172
|
end
|
|
@@ -146,6 +187,10 @@ module Aidp
|
|
|
146
187
|
state["ci_fixes"]
|
|
147
188
|
end
|
|
148
189
|
|
|
190
|
+
def change_requests
|
|
191
|
+
state["change_requests"]
|
|
192
|
+
end
|
|
193
|
+
|
|
149
194
|
def stringify_keys(hash)
|
|
150
195
|
return {} unless hash
|
|
151
196
|
|
|
@@ -156,8 +156,11 @@ module Aidp
|
|
|
156
156
|
)
|
|
157
157
|
|
|
158
158
|
# Log error and exit
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
# Suppress backtrace noise during tests while keeping it for production debugging
|
|
160
|
+
unless ENV["RSPEC_RUNNING"] == "true"
|
|
161
|
+
warn("Error in workstream #{slug}: #{e.message}")
|
|
162
|
+
warn(e.backtrace.first(5).join("\n"))
|
|
163
|
+
end
|
|
161
164
|
exit(1)
|
|
162
165
|
end
|
|
163
166
|
|
data/lib/aidp.rb
CHANGED
|
@@ -93,6 +93,10 @@ require_relative "aidp/harness/state_manager"
|
|
|
93
93
|
require_relative "aidp/harness/error_handler"
|
|
94
94
|
require_relative "aidp/harness/status_display"
|
|
95
95
|
require_relative "aidp/harness/runner"
|
|
96
|
+
require_relative "aidp/harness/filter_strategy"
|
|
97
|
+
require_relative "aidp/harness/generic_filter_strategy"
|
|
98
|
+
require_relative "aidp/harness/rspec_filter_strategy"
|
|
99
|
+
require_relative "aidp/harness/output_filter"
|
|
96
100
|
|
|
97
101
|
# UI components
|
|
98
102
|
require_relative "aidp/harness/ui/spinner_helper"
|
data/templates/aidp.yml.example
CHANGED
|
@@ -708,6 +708,56 @@ devcontainer:
|
|
|
708
708
|
# - "api.example.com"
|
|
709
709
|
# - "registry.example.com"
|
|
710
710
|
|
|
711
|
+
# Watch mode configuration
|
|
712
|
+
# Configures automated monitoring and processing of GitHub issues/PRs
|
|
713
|
+
watch:
|
|
714
|
+
# Label configuration for different triggers
|
|
715
|
+
labels:
|
|
716
|
+
plan_trigger: "aidp-plan" # Trigger plan generation
|
|
717
|
+
needs_input: "aidp-needs-input" # Request clarification
|
|
718
|
+
ready_to_build: "aidp-ready" # Ready for implementation
|
|
719
|
+
build_trigger: "aidp-build" # Trigger implementation
|
|
720
|
+
review_trigger: "aidp-review" # Trigger code review
|
|
721
|
+
ci_fix_trigger: "aidp-fix-ci" # Trigger CI fix
|
|
722
|
+
change_request_trigger: "aidp-request-changes" # Trigger PR change requests
|
|
723
|
+
|
|
724
|
+
# Safety configuration
|
|
725
|
+
safety:
|
|
726
|
+
# Allow watch mode on public repositories (default: false for safety)
|
|
727
|
+
allow_public_repos: false
|
|
728
|
+
|
|
729
|
+
# List of GitHub usernames allowed to trigger automated actions
|
|
730
|
+
# Empty list means all authenticated users (for private repos)
|
|
731
|
+
author_allowlist: []
|
|
732
|
+
# - "user1"
|
|
733
|
+
# - "user2"
|
|
734
|
+
|
|
735
|
+
# Require running in a container environment (additional safety)
|
|
736
|
+
require_container: false
|
|
737
|
+
|
|
738
|
+
# PR change request configuration
|
|
739
|
+
pr_change_requests:
|
|
740
|
+
# Enable/disable PR change request feature
|
|
741
|
+
enabled: true
|
|
742
|
+
|
|
743
|
+
# Allow changes across multiple files in a single request
|
|
744
|
+
allow_multi_file_edits: true
|
|
745
|
+
|
|
746
|
+
# Run tests and linters before pushing changes
|
|
747
|
+
# If tests fail, changes are committed locally but not pushed
|
|
748
|
+
run_tests_before_push: true
|
|
749
|
+
|
|
750
|
+
# Prefix for commit messages created by change request processor
|
|
751
|
+
commit_message_prefix: "aidp: pr-change"
|
|
752
|
+
|
|
753
|
+
# Require at least one comment before processing change request
|
|
754
|
+
# This ensures there's context for what changes are requested
|
|
755
|
+
require_comment_reference: true
|
|
756
|
+
|
|
757
|
+
# Maximum PR diff size (in lines) to process
|
|
758
|
+
# Large PRs are skipped to avoid overwhelming the AI analysis
|
|
759
|
+
max_diff_size: 2000
|
|
760
|
+
|
|
711
761
|
# Configuration tips:
|
|
712
762
|
# - Set max_tokens based on your API plan limits
|
|
713
763
|
# - Use default_flags to customize provider behavior
|
|
@@ -724,3 +774,6 @@ devcontainer:
|
|
|
724
774
|
# - Use time-based configs for different usage patterns
|
|
725
775
|
# - Use step-specific configs for different workflow steps
|
|
726
776
|
# - Use user-specific configs for personalized experiences
|
|
777
|
+
# - Configure watch mode safety settings before using on public repositories
|
|
778
|
+
# - Set author_allowlist to restrict who can trigger automated actions
|
|
779
|
+
# - Adjust max_diff_size based on your typical PR sizes and AI capabilities
|
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.26.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bart Agapinan
|
|
@@ -305,12 +305,16 @@ files:
|
|
|
305
305
|
- lib/aidp/harness/configuration.rb
|
|
306
306
|
- lib/aidp/harness/enhanced_runner.rb
|
|
307
307
|
- lib/aidp/harness/error_handler.rb
|
|
308
|
+
- lib/aidp/harness/filter_strategy.rb
|
|
309
|
+
- lib/aidp/harness/generic_filter_strategy.rb
|
|
310
|
+
- lib/aidp/harness/output_filter.rb
|
|
308
311
|
- lib/aidp/harness/provider_config.rb
|
|
309
312
|
- lib/aidp/harness/provider_factory.rb
|
|
310
313
|
- lib/aidp/harness/provider_info.rb
|
|
311
314
|
- lib/aidp/harness/provider_manager.rb
|
|
312
315
|
- lib/aidp/harness/provider_metrics.rb
|
|
313
316
|
- lib/aidp/harness/provider_type_checker.rb
|
|
317
|
+
- lib/aidp/harness/rspec_filter_strategy.rb
|
|
314
318
|
- lib/aidp/harness/runner.rb
|
|
315
319
|
- lib/aidp/harness/simple_user_interface.rb
|
|
316
320
|
- lib/aidp/harness/state/errors.rb
|
|
@@ -400,6 +404,7 @@ files:
|
|
|
400
404
|
- lib/aidp/version.rb
|
|
401
405
|
- lib/aidp/watch.rb
|
|
402
406
|
- lib/aidp/watch/build_processor.rb
|
|
407
|
+
- lib/aidp/watch/change_request_processor.rb
|
|
403
408
|
- lib/aidp/watch/ci_fix_processor.rb
|
|
404
409
|
- lib/aidp/watch/plan_generator.rb
|
|
405
410
|
- lib/aidp/watch/plan_processor.rb
|