aidp 0.33.0 → 0.34.1
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/ui.rb +11 -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 +21 -1
|
@@ -10,6 +10,7 @@ require_relative "implementation_verifier"
|
|
|
10
10
|
require_relative "reviewers/senior_dev_reviewer"
|
|
11
11
|
require_relative "reviewers/security_reviewer"
|
|
12
12
|
require_relative "reviewers/performance_reviewer"
|
|
13
|
+
require_relative "feedback_collector"
|
|
13
14
|
|
|
14
15
|
module Aidp
|
|
15
16
|
module Watch
|
|
@@ -81,13 +82,16 @@ module Aidp
|
|
|
81
82
|
review_results: review_results,
|
|
82
83
|
verification_result: verification_result
|
|
83
84
|
)
|
|
84
|
-
|
|
85
|
+
comment_body_with_feedback = FeedbackCollector.append_feedback_prompt(comment_body)
|
|
86
|
+
result = @repository_client.post_comment(number, comment_body_with_feedback)
|
|
87
|
+
comment_id = result[:id] if result.is_a?(Hash)
|
|
85
88
|
|
|
86
89
|
display_message("💬 Posted review comment for PR ##{number}", type: :success)
|
|
87
90
|
@state_store.record_review(number, {
|
|
88
91
|
timestamp: Time.now.utc.iso8601,
|
|
89
92
|
reviewers: review_results.map { |r| r[:persona] },
|
|
90
|
-
total_findings: review_results.sum { |r| r[:findings].length }
|
|
93
|
+
total_findings: review_results.sum { |r| r[:findings].length },
|
|
94
|
+
comment_id: comment_id
|
|
91
95
|
})
|
|
92
96
|
|
|
93
97
|
# Remove review label after processing
|
|
@@ -200,12 +204,35 @@ module Aidp
|
|
|
200
204
|
else
|
|
201
205
|
parts << "### ⚠️ Implementation Incomplete"
|
|
202
206
|
parts << ""
|
|
203
|
-
parts << "**This PR appears to be incomplete based on the linked issue requirements
|
|
207
|
+
parts << "**This PR appears to be incomplete based on the linked issue requirements.**"
|
|
204
208
|
parts << ""
|
|
205
|
-
|
|
206
|
-
|
|
209
|
+
|
|
210
|
+
# Show the verification reasoning
|
|
211
|
+
if verification_result[:reason]
|
|
212
|
+
parts << "**Summary:** #{verification_result[:reason]}"
|
|
213
|
+
parts << ""
|
|
207
214
|
end
|
|
208
|
-
|
|
215
|
+
|
|
216
|
+
# Show missing requirements for implementers to address
|
|
217
|
+
if verification_result[:missing_items]&.any?
|
|
218
|
+
parts << "**Missing Requirements:**"
|
|
219
|
+
parts << ""
|
|
220
|
+
verification_result[:missing_items].each do |item|
|
|
221
|
+
parts << "- #{item}"
|
|
222
|
+
end
|
|
223
|
+
parts << ""
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Show additional work needed for implementers
|
|
227
|
+
if verification_result[:additional_work]&.any?
|
|
228
|
+
parts << "**Additional Work Needed:**"
|
|
229
|
+
parts << ""
|
|
230
|
+
verification_result[:additional_work].each do |work|
|
|
231
|
+
parts << "- #{work}"
|
|
232
|
+
end
|
|
233
|
+
parts << ""
|
|
234
|
+
end
|
|
235
|
+
|
|
209
236
|
parts << "**Suggested Action:** Add the `aidp-request-changes` label if you'd like AIDP to help complete the implementation."
|
|
210
237
|
end
|
|
211
238
|
parts << ""
|
data/lib/aidp/watch/runner.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "tty-prompt"
|
|
4
|
+
require_relative "feedback_collector"
|
|
5
|
+
require_relative "github_state_extractor"
|
|
4
6
|
|
|
5
7
|
module Aidp
|
|
6
8
|
module Watch
|
|
@@ -11,13 +13,18 @@ module Aidp
|
|
|
11
13
|
|
|
12
14
|
DEFAULT_INTERVAL = 30
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
# Expose for testability
|
|
17
|
+
attr_reader :post_detection_comments
|
|
18
|
+
attr_writer :last_update_check
|
|
19
|
+
|
|
20
|
+
def initialize(issues_url:, interval: DEFAULT_INTERVAL, provider_name: nil, gh_available: nil, project_dir: Dir.pwd, once: false, use_workstreams: true, prompt: TTY::Prompt.new, safety_config: {}, force: false, verbose: false, quiet: false)
|
|
15
21
|
@prompt = prompt
|
|
16
22
|
@interval = interval
|
|
17
23
|
@once = once
|
|
18
24
|
@project_dir = project_dir
|
|
19
25
|
@force = force
|
|
20
26
|
@verbose = verbose
|
|
27
|
+
@quiet = quiet
|
|
21
28
|
@provider_name = provider_name
|
|
22
29
|
@safety_config = safety_config
|
|
23
30
|
|
|
@@ -102,6 +109,12 @@ module Aidp
|
|
|
102
109
|
safety_config: safety_config[:safety] || safety_config["safety"] || {},
|
|
103
110
|
verbose: verbose
|
|
104
111
|
)
|
|
112
|
+
|
|
113
|
+
@feedback_collector = FeedbackCollector.new(
|
|
114
|
+
repository_client: @repository_client,
|
|
115
|
+
state_store: @state_store,
|
|
116
|
+
project_dir: project_dir
|
|
117
|
+
)
|
|
105
118
|
end
|
|
106
119
|
|
|
107
120
|
def start
|
|
@@ -129,6 +142,7 @@ module Aidp
|
|
|
129
142
|
process_cycle
|
|
130
143
|
Aidp.log_debug("watch_runner", "poll_cycle.complete", once: @once, next_poll_in: @once ? nil : @interval)
|
|
131
144
|
break if @once
|
|
145
|
+
|
|
132
146
|
Aidp.log_debug("watch_runner", "poll_cycle.sleep", seconds: @interval)
|
|
133
147
|
sleep @interval
|
|
134
148
|
end
|
|
@@ -150,6 +164,7 @@ module Aidp
|
|
|
150
164
|
process_ci_fix_triggers
|
|
151
165
|
process_auto_pr_triggers
|
|
152
166
|
process_change_request_triggers
|
|
167
|
+
collect_feedback
|
|
153
168
|
end
|
|
154
169
|
|
|
155
170
|
def process_plan_triggers
|
|
@@ -182,7 +197,8 @@ module Aidp
|
|
|
182
197
|
|
|
183
198
|
# Check author authorization before processing
|
|
184
199
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
185
|
-
Aidp.log_debug("watch_runner", "plan_skip_unauthorized_author", issue: detailed[:number],
|
|
200
|
+
Aidp.log_debug("watch_runner", "plan_skip_unauthorized_author", issue: detailed[:number],
|
|
201
|
+
author: detailed[:author])
|
|
186
202
|
next
|
|
187
203
|
end
|
|
188
204
|
|
|
@@ -238,7 +254,8 @@ module Aidp
|
|
|
238
254
|
|
|
239
255
|
# Check author authorization before processing
|
|
240
256
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
241
|
-
Aidp.log_debug("watch_runner", "build_skip_unauthorized_author", issue: detailed[:number],
|
|
257
|
+
Aidp.log_debug("watch_runner", "build_skip_unauthorized_author", issue: detailed[:number],
|
|
258
|
+
author: detailed[:author])
|
|
242
259
|
next
|
|
243
260
|
end
|
|
244
261
|
|
|
@@ -261,7 +278,8 @@ module Aidp
|
|
|
261
278
|
begin
|
|
262
279
|
@repository_client.remove_labels(detailed[:number], GitHubStateExtractor::IN_PROGRESS_LABEL)
|
|
263
280
|
rescue => e
|
|
264
|
-
Aidp.log_warn("watch_runner", "failed_to_remove_in_progress_label", issue: detailed[:number],
|
|
281
|
+
Aidp.log_warn("watch_runner", "failed_to_remove_in_progress_label", issue: detailed[:number],
|
|
282
|
+
error: e.message)
|
|
265
283
|
end
|
|
266
284
|
end
|
|
267
285
|
end
|
|
@@ -281,7 +299,8 @@ module Aidp
|
|
|
281
299
|
|
|
282
300
|
issues.each do |issue|
|
|
283
301
|
unless issue_has_label?(issue, auto_label)
|
|
284
|
-
Aidp.log_debug("watch_runner", "auto_issue_skip_label_mismatch", issue: issue[:number],
|
|
302
|
+
Aidp.log_debug("watch_runner", "auto_issue_skip_label_mismatch", issue: issue[:number],
|
|
303
|
+
labels: issue[:labels])
|
|
285
304
|
next
|
|
286
305
|
end
|
|
287
306
|
|
|
@@ -300,7 +319,8 @@ module Aidp
|
|
|
300
319
|
|
|
301
320
|
# Check author authorization before processing
|
|
302
321
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
303
|
-
Aidp.log_debug("watch_runner", "auto_issue_skip_unauthorized_author", issue: detailed[:number],
|
|
322
|
+
Aidp.log_debug("watch_runner", "auto_issue_skip_unauthorized_author", issue: detailed[:number],
|
|
323
|
+
author: detailed[:author])
|
|
304
324
|
next
|
|
305
325
|
end
|
|
306
326
|
|
|
@@ -346,7 +366,8 @@ module Aidp
|
|
|
346
366
|
|
|
347
367
|
# Check author authorization before processing
|
|
348
368
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
349
|
-
Aidp.log_debug("watch_runner", "review_skip_unauthorized_author", pr: detailed[:number],
|
|
369
|
+
Aidp.log_debug("watch_runner", "review_skip_unauthorized_author", pr: detailed[:number],
|
|
370
|
+
author: detailed[:author])
|
|
350
371
|
next
|
|
351
372
|
end
|
|
352
373
|
|
|
@@ -383,7 +404,8 @@ module Aidp
|
|
|
383
404
|
|
|
384
405
|
# Check author authorization before processing
|
|
385
406
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
386
|
-
Aidp.log_debug("watch_runner", "auto_pr_skip_unauthorized_author", pr: detailed[:number],
|
|
407
|
+
Aidp.log_debug("watch_runner", "auto_pr_skip_unauthorized_author", pr: detailed[:number],
|
|
408
|
+
author: detailed[:author])
|
|
387
409
|
next
|
|
388
410
|
end
|
|
389
411
|
|
|
@@ -419,7 +441,8 @@ module Aidp
|
|
|
419
441
|
|
|
420
442
|
# Check author authorization before processing
|
|
421
443
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
422
|
-
Aidp.log_debug("watch_runner", "ci_fix_skip_unauthorized_author", pr: detailed[:number],
|
|
444
|
+
Aidp.log_debug("watch_runner", "ci_fix_skip_unauthorized_author", pr: detailed[:number],
|
|
445
|
+
author: detailed[:author])
|
|
423
446
|
next
|
|
424
447
|
end
|
|
425
448
|
|
|
@@ -455,7 +478,8 @@ module Aidp
|
|
|
455
478
|
|
|
456
479
|
# Check author authorization before processing
|
|
457
480
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
458
|
-
Aidp.log_debug("watch_runner", "change_request_skip_unauthorized_author", pr: detailed[:number],
|
|
481
|
+
Aidp.log_debug("watch_runner", "change_request_skip_unauthorized_author", pr: detailed[:number],
|
|
482
|
+
author: detailed[:author])
|
|
459
483
|
next
|
|
460
484
|
end
|
|
461
485
|
|
|
@@ -511,7 +535,8 @@ module Aidp
|
|
|
511
535
|
update_check = @auto_update_coordinator.check_for_update
|
|
512
536
|
|
|
513
537
|
if update_check.should_update?
|
|
514
|
-
display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}",
|
|
538
|
+
display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}",
|
|
539
|
+
type: :highlight)
|
|
515
540
|
|
|
516
541
|
# Prefer hot reloading if available (Zeitwerk enabled with reloading)
|
|
517
542
|
if @auto_update_coordinator.hot_reload_available?
|
|
@@ -644,6 +669,21 @@ module Aidp
|
|
|
644
669
|
name.casecmp(label).zero?
|
|
645
670
|
end
|
|
646
671
|
end
|
|
672
|
+
|
|
673
|
+
# Collect feedback from reactions on tracked comments
|
|
674
|
+
def collect_feedback
|
|
675
|
+
new_evaluations = @feedback_collector.collect_feedback
|
|
676
|
+
return if new_evaluations.empty?
|
|
677
|
+
|
|
678
|
+
Aidp.log_info("watch_runner", "feedback_collected",
|
|
679
|
+
count: new_evaluations.size,
|
|
680
|
+
evaluations: new_evaluations.map { |e| {id: e[:id], rating: e[:rating]} })
|
|
681
|
+
|
|
682
|
+
display_message("📊 Collected #{new_evaluations.size} new feedback evaluation(s)", type: :info) if @verbose
|
|
683
|
+
rescue => e
|
|
684
|
+
Aidp.log_error("watch_runner", "feedback_collection_failed", error: e.message)
|
|
685
|
+
display_message("⚠️ Feedback collection failed: #{e.message}", type: :warn) if @verbose
|
|
686
|
+
end
|
|
647
687
|
end
|
|
648
688
|
end
|
|
649
689
|
end
|
|
@@ -189,6 +189,214 @@ module Aidp
|
|
|
189
189
|
save!
|
|
190
190
|
end
|
|
191
191
|
|
|
192
|
+
# Feedback tracking methods - track comments for reaction-based evaluations
|
|
193
|
+
|
|
194
|
+
# Get all tracked comments with their metadata for feedback collection
|
|
195
|
+
# @return [Array<Hash>] List of comment info hashes
|
|
196
|
+
def tracked_comments
|
|
197
|
+
comments = []
|
|
198
|
+
|
|
199
|
+
# Collect from plans
|
|
200
|
+
plans.each do |issue_number, data|
|
|
201
|
+
next unless data["comment_id"]
|
|
202
|
+
comments << {
|
|
203
|
+
comment_id: data["comment_id"],
|
|
204
|
+
processor_type: "plan",
|
|
205
|
+
number: issue_number.to_i,
|
|
206
|
+
posted_at: data["posted_at"]
|
|
207
|
+
}
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Collect from reviews (if they store comment_id)
|
|
211
|
+
reviews.each do |pr_number, data|
|
|
212
|
+
next unless data["comment_id"]
|
|
213
|
+
comments << {
|
|
214
|
+
comment_id: data["comment_id"],
|
|
215
|
+
processor_type: "review",
|
|
216
|
+
number: pr_number.to_i,
|
|
217
|
+
posted_at: data["timestamp"]
|
|
218
|
+
}
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Collect from builds (if they store comment_id)
|
|
222
|
+
builds.each do |issue_number, data|
|
|
223
|
+
next unless data["comment_id"]
|
|
224
|
+
comments << {
|
|
225
|
+
comment_id: data["comment_id"],
|
|
226
|
+
processor_type: "build",
|
|
227
|
+
number: issue_number.to_i,
|
|
228
|
+
posted_at: data["updated_at"]
|
|
229
|
+
}
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Collect from feedback_comments (explicitly tracked)
|
|
233
|
+
feedback_comments.each do |key, data|
|
|
234
|
+
comments << {
|
|
235
|
+
comment_id: data["comment_id"],
|
|
236
|
+
processor_type: data["processor_type"],
|
|
237
|
+
number: data["number"].to_i,
|
|
238
|
+
posted_at: data["posted_at"]
|
|
239
|
+
}
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
comments
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Track a comment for feedback collection
|
|
246
|
+
# @param comment_id [Integer, String] GitHub comment ID
|
|
247
|
+
# @param processor_type [String] Type of processor (plan, review, build, etc.)
|
|
248
|
+
# @param number [Integer] Issue or PR number
|
|
249
|
+
def track_comment_for_feedback(comment_id:, processor_type:, number:)
|
|
250
|
+
key = "#{processor_type}_#{number}"
|
|
251
|
+
feedback_comments[key] = {
|
|
252
|
+
"comment_id" => comment_id.to_s,
|
|
253
|
+
"processor_type" => processor_type,
|
|
254
|
+
"number" => number,
|
|
255
|
+
"posted_at" => Time.now.utc.iso8601
|
|
256
|
+
}
|
|
257
|
+
save!
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Get IDs of reactions already processed for a comment
|
|
261
|
+
# @param comment_id [Integer, String] GitHub comment ID
|
|
262
|
+
# @return [Array<Integer>] List of processed reaction IDs
|
|
263
|
+
def processed_reaction_ids(comment_id)
|
|
264
|
+
data = processed_reactions[comment_id.to_s]
|
|
265
|
+
return [] unless data
|
|
266
|
+
data["reaction_ids"] || []
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Mark a reaction as processed
|
|
270
|
+
# @param comment_id [Integer, String] GitHub comment ID
|
|
271
|
+
# @param reaction_id [Integer] GitHub reaction ID
|
|
272
|
+
def mark_reaction_processed(comment_id, reaction_id)
|
|
273
|
+
key = comment_id.to_s
|
|
274
|
+
processed_reactions[key] ||= {"reaction_ids" => [], "last_checked" => nil}
|
|
275
|
+
processed_reactions[key]["reaction_ids"] << reaction_id unless processed_reactions[key]["reaction_ids"].include?(reaction_id)
|
|
276
|
+
processed_reactions[key]["last_checked"] = Time.now.utc.iso8601
|
|
277
|
+
save!
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Auto PR tracking methods - for aidp-auto label on PRs
|
|
281
|
+
# Tracks iteration counts to enforce iteration cap
|
|
282
|
+
|
|
283
|
+
# Get the current iteration count for an auto PR
|
|
284
|
+
# @param pr_number [Integer] PR number
|
|
285
|
+
# @return [Integer] Current iteration count (0 if not tracked)
|
|
286
|
+
def auto_pr_iteration_count(pr_number)
|
|
287
|
+
data = auto_prs[pr_number.to_s]
|
|
288
|
+
return 0 unless data
|
|
289
|
+
data["iteration"] || 0
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Get full auto PR data
|
|
293
|
+
# @param pr_number [Integer] PR number
|
|
294
|
+
# @return [Hash, nil] Auto PR tracking data
|
|
295
|
+
def auto_pr_data(pr_number)
|
|
296
|
+
auto_prs[pr_number.to_s]
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Record an auto PR iteration
|
|
300
|
+
# @param pr_number [Integer] PR number
|
|
301
|
+
# @param data [Hash] Additional data to store
|
|
302
|
+
# @return [Integer] New iteration count
|
|
303
|
+
def record_auto_pr_iteration(pr_number, data = {})
|
|
304
|
+
key = pr_number.to_s
|
|
305
|
+
existing = auto_prs[key] || {}
|
|
306
|
+
iteration = (existing["iteration"] || 0) + 1
|
|
307
|
+
|
|
308
|
+
auto_prs[key] = {
|
|
309
|
+
"iteration" => iteration,
|
|
310
|
+
"last_processed_at" => Time.now.utc.iso8601,
|
|
311
|
+
"status" => data[:status] || "in_progress",
|
|
312
|
+
"metadata" => stringify_keys(data[:metadata] || {})
|
|
313
|
+
}.merge(stringify_keys(data.except(:status, :metadata)))
|
|
314
|
+
|
|
315
|
+
save!
|
|
316
|
+
iteration
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Mark an auto PR as completed (ready for human review)
|
|
320
|
+
# @param pr_number [Integer] PR number
|
|
321
|
+
# @param data [Hash] Additional completion data
|
|
322
|
+
def complete_auto_pr(pr_number, data = {})
|
|
323
|
+
key = pr_number.to_s
|
|
324
|
+
existing = auto_prs[key] || {}
|
|
325
|
+
|
|
326
|
+
auto_prs[key] = existing.merge({
|
|
327
|
+
"status" => "completed",
|
|
328
|
+
"completed_at" => Time.now.utc.iso8601
|
|
329
|
+
}).merge(stringify_keys(data))
|
|
330
|
+
|
|
331
|
+
save!
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Check if an auto PR has reached the iteration cap
|
|
335
|
+
# @param pr_number [Integer] PR number
|
|
336
|
+
# @param cap [Integer] Maximum iterations allowed
|
|
337
|
+
# @return [Boolean] True if cap reached
|
|
338
|
+
def auto_pr_cap_reached?(pr_number, cap:)
|
|
339
|
+
auto_pr_iteration_count(pr_number) >= cap
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# Project tracking methods
|
|
343
|
+
def project_item_id(issue_number)
|
|
344
|
+
projects[issue_number.to_s]&.dig("item_id")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def record_project_item_id(issue_number, item_id)
|
|
348
|
+
projects[issue_number.to_s] ||= {}
|
|
349
|
+
projects[issue_number.to_s]["item_id"] = item_id
|
|
350
|
+
projects[issue_number.to_s]["synced_at"] = Time.now.utc.iso8601
|
|
351
|
+
save!
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def project_sync_data(issue_number)
|
|
355
|
+
projects[issue_number.to_s] || {}
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def record_project_sync(issue_number, data)
|
|
359
|
+
projects[issue_number.to_s] ||= {}
|
|
360
|
+
projects[issue_number.to_s].merge!(stringify_keys(data))
|
|
361
|
+
projects[issue_number.to_s]["synced_at"] = Time.now.utc.iso8601
|
|
362
|
+
save!
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Sub-issue tracking methods
|
|
366
|
+
def sub_issues(parent_number)
|
|
367
|
+
hierarchies[parent_number.to_s]&.dig("sub_issues") || []
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def parent_issue(sub_issue_number)
|
|
371
|
+
hierarchies[sub_issue_number.to_s]&.dig("parent")
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def record_sub_issues(parent_number, sub_issue_numbers)
|
|
375
|
+
hierarchies[parent_number.to_s] ||= {}
|
|
376
|
+
hierarchies[parent_number.to_s]["sub_issues"] = Array(sub_issue_numbers)
|
|
377
|
+
hierarchies[parent_number.to_s]["created_at"] = Time.now.utc.iso8601
|
|
378
|
+
|
|
379
|
+
# Also record reverse mapping
|
|
380
|
+
sub_issue_numbers.each do |sub_number|
|
|
381
|
+
hierarchies[sub_number.to_s] ||= {}
|
|
382
|
+
hierarchies[sub_number.to_s]["parent"] = parent_number
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
save!
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
def blocking_status(issue_number)
|
|
389
|
+
# Check if this issue is blocked by any open sub-issues
|
|
390
|
+
sub_issue_numbers = sub_issues(issue_number)
|
|
391
|
+
return {blocked: false, blockers: []} if sub_issue_numbers.empty?
|
|
392
|
+
|
|
393
|
+
{
|
|
394
|
+
blocked: true,
|
|
395
|
+
blockers: sub_issue_numbers,
|
|
396
|
+
blocker_count: sub_issue_numbers.size
|
|
397
|
+
}
|
|
398
|
+
end
|
|
399
|
+
|
|
192
400
|
private
|
|
193
401
|
|
|
194
402
|
def ensure_directory
|
|
@@ -221,6 +429,11 @@ module Aidp
|
|
|
221
429
|
base["ci_fixes"] ||= {}
|
|
222
430
|
base["change_requests"] ||= {}
|
|
223
431
|
base["detection_comments"] ||= {}
|
|
432
|
+
base["feedback_comments"] ||= {}
|
|
433
|
+
base["processed_reactions"] ||= {}
|
|
434
|
+
base["auto_prs"] ||= {}
|
|
435
|
+
base["projects"] ||= {}
|
|
436
|
+
base["hierarchies"] ||= {}
|
|
224
437
|
base
|
|
225
438
|
end
|
|
226
439
|
end
|
|
@@ -249,6 +462,26 @@ module Aidp
|
|
|
249
462
|
state["detection_comments"]
|
|
250
463
|
end
|
|
251
464
|
|
|
465
|
+
def feedback_comments
|
|
466
|
+
state["feedback_comments"]
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
def processed_reactions
|
|
470
|
+
state["processed_reactions"]
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def auto_prs
|
|
474
|
+
state["auto_prs"]
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
def projects
|
|
478
|
+
state["projects"]
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
def hierarchies
|
|
482
|
+
state["hierarchies"]
|
|
483
|
+
end
|
|
484
|
+
|
|
252
485
|
def stringify_keys(hash)
|
|
253
486
|
return {} unless hash
|
|
254
487
|
|