aidp 0.32.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/feature_analyzer.rb +322 -320
- data/lib/aidp/analyze/tree_sitter_scan.rb +3 -0
- data/lib/aidp/auto_update/coordinator.rb +97 -7
- data/lib/aidp/auto_update.rb +0 -12
- data/lib/aidp/cli/devcontainer_commands.rb +0 -5
- 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 +172 -4
- data/lib/aidp/comment_consolidator.rb +78 -0
- data/lib/aidp/concurrency/exec.rb +3 -0
- data/lib/aidp/concurrency.rb +0 -3
- data/lib/aidp/config.rb +113 -1
- data/lib/aidp/config_paths.rb +91 -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 +719 -58
- 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/ai_filter_factory.rb +285 -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/config_schema.rb +97 -1
- data/lib/aidp/harness/config_validator.rb +1 -1
- data/lib/aidp/harness/configuration.rb +61 -5
- data/lib/aidp/harness/enhanced_runner.rb +14 -11
- data/lib/aidp/harness/error_handler.rb +3 -0
- data/lib/aidp/harness/filter_definition.rb +212 -0
- data/lib/aidp/harness/generated_filter_strategy.rb +197 -0
- data/lib/aidp/harness/output_filter.rb +50 -25
- data/lib/aidp/harness/output_filter_config.rb +129 -0
- data/lib/aidp/harness/provider_factory.rb +3 -0
- data/lib/aidp/harness/provider_manager.rb +96 -2
- data/lib/aidp/harness/runner.rb +5 -12
- 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/test_runner.rb +179 -41
- data/lib/aidp/harness/thinking_depth_manager.rb +44 -28
- 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 -2
- 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 +195 -0
- data/lib/aidp/logger.rb +3 -0
- data/lib/aidp/message_display.rb +31 -0
- data/lib/aidp/metadata/compiler.rb +29 -17
- data/lib/aidp/metadata/query.rb +1 -1
- data/lib/aidp/metadata/scanner.rb +8 -1
- data/lib/aidp/metadata/tool_metadata.rb +13 -13
- data/lib/aidp/metadata/validator.rb +10 -0
- data/lib/aidp/metadata.rb +16 -0
- data/lib/aidp/pr_worktree_manager.rb +20 -8
- data/lib/aidp/provider_manager.rb +4 -7
- 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 +283 -11
- data/lib/aidp/skills.rb +0 -5
- data/lib/aidp/storage/csv_storage.rb +3 -0
- data/lib/aidp/style_guide/selector.rb +360 -0
- data/lib/aidp/tooling_detector.rb +283 -16
- 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 +682 -150
- 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 +871 -22
- data/lib/aidp/watch/review_processor.rb +33 -6
- data/lib/aidp/watch/runner.rb +80 -29
- data/lib/aidp/watch/state_store.rb +233 -0
- data/lib/aidp/watch/sub_issue_creator.rb +221 -0
- data/lib/aidp/watch.rb +5 -7
- data/lib/aidp/workflows/guided_agent.rb +4 -0
- data/lib/aidp/workstream_cleanup.rb +0 -2
- data/lib/aidp/workstream_executor.rb +3 -4
- data/lib/aidp/worktree.rb +61 -12
- data/lib/aidp/worktree_branch_manager.rb +347 -101
- data/lib/aidp.rb +21 -106
- data/templates/implementation/iterative_implementation.md +46 -3
- metadata +91 -36
- data/lib/aidp/config/paths.rb +0 -131
|
@@ -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,21 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "tty-prompt"
|
|
4
|
-
|
|
5
|
-
require_relative "../message_display"
|
|
6
|
-
require_relative "repository_client"
|
|
7
|
-
require_relative "repository_safety_checker"
|
|
8
|
-
require_relative "state_store"
|
|
4
|
+
require_relative "feedback_collector"
|
|
9
5
|
require_relative "github_state_extractor"
|
|
10
|
-
require_relative "plan_generator"
|
|
11
|
-
require_relative "plan_processor"
|
|
12
|
-
require_relative "build_processor"
|
|
13
|
-
require_relative "../auto_update"
|
|
14
|
-
require_relative "review_processor"
|
|
15
|
-
require_relative "ci_fix_processor"
|
|
16
|
-
require_relative "change_request_processor"
|
|
17
|
-
require_relative "auto_processor"
|
|
18
|
-
require_relative "auto_pr_processor"
|
|
19
6
|
|
|
20
7
|
module Aidp
|
|
21
8
|
module Watch
|
|
@@ -26,13 +13,18 @@ module Aidp
|
|
|
26
13
|
|
|
27
14
|
DEFAULT_INTERVAL = 30
|
|
28
15
|
|
|
29
|
-
|
|
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)
|
|
30
21
|
@prompt = prompt
|
|
31
22
|
@interval = interval
|
|
32
23
|
@once = once
|
|
33
24
|
@project_dir = project_dir
|
|
34
25
|
@force = force
|
|
35
26
|
@verbose = verbose
|
|
27
|
+
@quiet = quiet
|
|
36
28
|
@provider_name = provider_name
|
|
37
29
|
@safety_config = safety_config
|
|
38
30
|
|
|
@@ -117,6 +109,12 @@ module Aidp
|
|
|
117
109
|
safety_config: safety_config[:safety] || safety_config["safety"] || {},
|
|
118
110
|
verbose: verbose
|
|
119
111
|
)
|
|
112
|
+
|
|
113
|
+
@feedback_collector = FeedbackCollector.new(
|
|
114
|
+
repository_client: @repository_client,
|
|
115
|
+
state_store: @state_store,
|
|
116
|
+
project_dir: project_dir
|
|
117
|
+
)
|
|
120
118
|
end
|
|
121
119
|
|
|
122
120
|
def start
|
|
@@ -144,6 +142,7 @@ module Aidp
|
|
|
144
142
|
process_cycle
|
|
145
143
|
Aidp.log_debug("watch_runner", "poll_cycle.complete", once: @once, next_poll_in: @once ? nil : @interval)
|
|
146
144
|
break if @once
|
|
145
|
+
|
|
147
146
|
Aidp.log_debug("watch_runner", "poll_cycle.sleep", seconds: @interval)
|
|
148
147
|
sleep @interval
|
|
149
148
|
end
|
|
@@ -165,6 +164,7 @@ module Aidp
|
|
|
165
164
|
process_ci_fix_triggers
|
|
166
165
|
process_auto_pr_triggers
|
|
167
166
|
process_change_request_triggers
|
|
167
|
+
collect_feedback
|
|
168
168
|
end
|
|
169
169
|
|
|
170
170
|
def process_plan_triggers
|
|
@@ -197,7 +197,8 @@ module Aidp
|
|
|
197
197
|
|
|
198
198
|
# Check author authorization before processing
|
|
199
199
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
200
|
-
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])
|
|
201
202
|
next
|
|
202
203
|
end
|
|
203
204
|
|
|
@@ -253,7 +254,8 @@ module Aidp
|
|
|
253
254
|
|
|
254
255
|
# Check author authorization before processing
|
|
255
256
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
256
|
-
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])
|
|
257
259
|
next
|
|
258
260
|
end
|
|
259
261
|
|
|
@@ -276,7 +278,8 @@ module Aidp
|
|
|
276
278
|
begin
|
|
277
279
|
@repository_client.remove_labels(detailed[:number], GitHubStateExtractor::IN_PROGRESS_LABEL)
|
|
278
280
|
rescue => e
|
|
279
|
-
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)
|
|
280
283
|
end
|
|
281
284
|
end
|
|
282
285
|
end
|
|
@@ -296,7 +299,8 @@ module Aidp
|
|
|
296
299
|
|
|
297
300
|
issues.each do |issue|
|
|
298
301
|
unless issue_has_label?(issue, auto_label)
|
|
299
|
-
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])
|
|
300
304
|
next
|
|
301
305
|
end
|
|
302
306
|
|
|
@@ -315,7 +319,8 @@ module Aidp
|
|
|
315
319
|
|
|
316
320
|
# Check author authorization before processing
|
|
317
321
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
318
|
-
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])
|
|
319
324
|
next
|
|
320
325
|
end
|
|
321
326
|
|
|
@@ -361,7 +366,8 @@ module Aidp
|
|
|
361
366
|
|
|
362
367
|
# Check author authorization before processing
|
|
363
368
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
364
|
-
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])
|
|
365
371
|
next
|
|
366
372
|
end
|
|
367
373
|
|
|
@@ -398,7 +404,8 @@ module Aidp
|
|
|
398
404
|
|
|
399
405
|
# Check author authorization before processing
|
|
400
406
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
401
|
-
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])
|
|
402
409
|
next
|
|
403
410
|
end
|
|
404
411
|
|
|
@@ -434,7 +441,8 @@ module Aidp
|
|
|
434
441
|
|
|
435
442
|
# Check author authorization before processing
|
|
436
443
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
437
|
-
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])
|
|
438
446
|
next
|
|
439
447
|
end
|
|
440
448
|
|
|
@@ -470,7 +478,8 @@ module Aidp
|
|
|
470
478
|
|
|
471
479
|
# Check author authorization before processing
|
|
472
480
|
unless @safety_checker.should_process_issue?(detailed, enforce: false)
|
|
473
|
-
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])
|
|
474
483
|
next
|
|
475
484
|
end
|
|
476
485
|
|
|
@@ -522,14 +531,22 @@ module Aidp
|
|
|
522
531
|
return unless @auto_update_coordinator.policy.enabled
|
|
523
532
|
return unless time_for_update_check?
|
|
524
533
|
|
|
534
|
+
@last_update_check = Time.now
|
|
525
535
|
update_check = @auto_update_coordinator.check_for_update
|
|
526
536
|
|
|
527
537
|
if update_check.should_update?
|
|
528
|
-
display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}",
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
538
|
+
display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}",
|
|
539
|
+
type: :highlight)
|
|
540
|
+
|
|
541
|
+
# Prefer hot reloading if available (Zeitwerk enabled with reloading)
|
|
542
|
+
if @auto_update_coordinator.hot_reload_available?
|
|
543
|
+
perform_hot_reload(update_check)
|
|
544
|
+
else
|
|
545
|
+
# Fall back to checkpoint + exit approach
|
|
546
|
+
display_message(" Saving checkpoint and initiating update...", type: :muted)
|
|
547
|
+
initiate_update(update_check)
|
|
548
|
+
# Never returns - exits with code 75
|
|
549
|
+
end
|
|
533
550
|
end
|
|
534
551
|
rescue Aidp::AutoUpdate::UpdateLoopError => e
|
|
535
552
|
# Restart loop detected - disable auto-update
|
|
@@ -540,6 +557,25 @@ module Aidp
|
|
|
540
557
|
Aidp.log_error("watch_runner", "update_check_failed", error: e.message)
|
|
541
558
|
end
|
|
542
559
|
|
|
560
|
+
# Perform hot code reload without restarting
|
|
561
|
+
# @param update_check [Aidp::AutoUpdate::UpdateCheck] Update check result
|
|
562
|
+
def perform_hot_reload(update_check)
|
|
563
|
+
display_message(" Performing hot code reload (no restart needed)...", type: :muted)
|
|
564
|
+
|
|
565
|
+
if @auto_update_coordinator.hot_reload_update(update_check)
|
|
566
|
+
display_message("✨ Hot reload complete! Now running #{Aidp::VERSION}", type: :success)
|
|
567
|
+
Aidp.log_info("watch_runner", "hot_reload_success",
|
|
568
|
+
version: Aidp::VERSION)
|
|
569
|
+
else
|
|
570
|
+
display_message("⚠️ Hot reload skipped (no update needed)", type: :muted)
|
|
571
|
+
end
|
|
572
|
+
rescue Aidp::AutoUpdate::UpdateError => e
|
|
573
|
+
display_message("⚠️ Hot reload failed: #{e.message}", type: :warning)
|
|
574
|
+
display_message(" Falling back to checkpoint + restart...", type: :muted)
|
|
575
|
+
# Fall back to cold restart
|
|
576
|
+
initiate_update(update_check)
|
|
577
|
+
end
|
|
578
|
+
|
|
543
579
|
# Determine if it's time to check for updates
|
|
544
580
|
# @return [Boolean]
|
|
545
581
|
def time_for_update_check?
|
|
@@ -633,6 +669,21 @@ module Aidp
|
|
|
633
669
|
name.casecmp(label).zero?
|
|
634
670
|
end
|
|
635
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
|
|
636
687
|
end
|
|
637
688
|
end
|
|
638
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
|
|