aidp 0.27.0 → 0.28.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 +89 -0
- data/lib/aidp/cli/models_command.rb +5 -6
- data/lib/aidp/cli.rb +10 -8
- data/lib/aidp/config.rb +54 -0
- data/lib/aidp/debug_mixin.rb +23 -1
- data/lib/aidp/execute/agent_signal_parser.rb +22 -0
- data/lib/aidp/execute/repl_macros.rb +2 -2
- data/lib/aidp/execute/steps.rb +94 -1
- data/lib/aidp/execute/work_loop_runner.rb +209 -17
- data/lib/aidp/execute/workflow_selector.rb +2 -25
- data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
- data/lib/aidp/harness/ai_decision_engine.rb +35 -2
- data/lib/aidp/harness/config_manager.rb +0 -5
- data/lib/aidp/harness/config_schema.rb +8 -0
- data/lib/aidp/harness/configuration.rb +27 -19
- data/lib/aidp/harness/enhanced_runner.rb +1 -4
- data/lib/aidp/harness/error_handler.rb +1 -72
- data/lib/aidp/harness/provider_factory.rb +11 -2
- data/lib/aidp/harness/state_manager.rb +0 -7
- data/lib/aidp/harness/thinking_depth_manager.rb +47 -68
- data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
- data/lib/aidp/harness/ui/progress_display.rb +6 -2
- data/lib/aidp/harness/user_interface.rb +0 -58
- data/lib/aidp/init/runner.rb +7 -2
- data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
- data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
- data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
- data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
- data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
- data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
- data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
- data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
- data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
- data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
- data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
- data/lib/aidp/planning/parsers/document_parser.rb +141 -0
- data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
- data/lib/aidp/provider_manager.rb +8 -32
- data/lib/aidp/providers/aider.rb +264 -0
- data/lib/aidp/providers/anthropic.rb +74 -2
- data/lib/aidp/providers/base.rb +25 -1
- data/lib/aidp/providers/codex.rb +26 -3
- data/lib/aidp/providers/cursor.rb +16 -0
- data/lib/aidp/providers/gemini.rb +13 -0
- data/lib/aidp/providers/github_copilot.rb +17 -0
- data/lib/aidp/providers/kilocode.rb +11 -0
- data/lib/aidp/providers/opencode.rb +11 -0
- data/lib/aidp/setup/wizard.rb +249 -39
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +211 -30
- data/lib/aidp/watch/change_request_processor.rb +128 -14
- data/lib/aidp/watch/ci_fix_processor.rb +103 -37
- data/lib/aidp/watch/ci_log_extractor.rb +258 -0
- data/lib/aidp/watch/github_state_extractor.rb +177 -0
- data/lib/aidp/watch/implementation_verifier.rb +284 -0
- data/lib/aidp/watch/plan_generator.rb +7 -43
- data/lib/aidp/watch/plan_processor.rb +7 -6
- data/lib/aidp/watch/repository_client.rb +245 -17
- data/lib/aidp/watch/review_processor.rb +98 -17
- data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
- data/lib/aidp/watch/runner.rb +181 -29
- data/lib/aidp/watch/state_store.rb +22 -1
- data/lib/aidp/workflows/definitions.rb +147 -0
- data/lib/aidp/workstream_cleanup.rb +245 -0
- data/lib/aidp/worktree.rb +19 -0
- data/templates/aidp.yml.example +57 -0
- data/templates/implementation/generate_tdd_specs.md +213 -0
- data/templates/implementation/iterative_implementation.md +122 -0
- data/templates/planning/agile/analyze_feedback.md +183 -0
- data/templates/planning/agile/generate_iteration_plan.md +179 -0
- data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
- data/templates/planning/agile/generate_marketing_report.md +162 -0
- data/templates/planning/agile/generate_mvp_scope.md +127 -0
- data/templates/planning/agile/generate_user_test_plan.md +143 -0
- data/templates/planning/agile/ingest_feedback.md +174 -0
- data/templates/planning/assemble_project_plan.md +113 -0
- data/templates/planning/assign_personas.md +108 -0
- data/templates/planning/create_tasks.md +52 -6
- data/templates/planning/generate_gantt.md +86 -0
- data/templates/planning/generate_wbs.md +85 -0
- data/templates/planning/initialize_planning_mode.md +70 -0
- data/templates/skills/README.md +2 -2
- data/templates/skills/marketing_strategist/SKILL.md +279 -0
- data/templates/skills/product_manager/SKILL.md +177 -0
- data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
- data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
- data/templates/skills/ux_researcher/SKILL.md +222 -0
- metadata +39 -1
|
@@ -12,6 +12,9 @@ require_relative "../execute/prompt_manager"
|
|
|
12
12
|
require_relative "../harness/runner"
|
|
13
13
|
require_relative "../harness/state_manager"
|
|
14
14
|
require_relative "../harness/test_runner"
|
|
15
|
+
require_relative "../worktree"
|
|
16
|
+
require_relative "github_state_extractor"
|
|
17
|
+
require_relative "implementation_verifier"
|
|
15
18
|
|
|
16
19
|
module Aidp
|
|
17
20
|
module Watch
|
|
@@ -32,10 +35,17 @@ module Aidp
|
|
|
32
35
|
def initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, change_request_config: {}, safety_config: {}, verbose: false)
|
|
33
36
|
@repository_client = repository_client
|
|
34
37
|
@state_store = state_store
|
|
38
|
+
@state_extractor = GitHubStateExtractor.new(repository_client: repository_client)
|
|
35
39
|
@provider_name = provider_name
|
|
36
40
|
@project_dir = project_dir
|
|
37
41
|
@verbose = verbose
|
|
38
42
|
|
|
43
|
+
# Initialize verifier
|
|
44
|
+
@verifier = ImplementationVerifier.new(
|
|
45
|
+
repository_client: repository_client,
|
|
46
|
+
project_dir: project_dir
|
|
47
|
+
)
|
|
48
|
+
|
|
39
49
|
# Load label configuration
|
|
40
50
|
@change_request_label = label_config[:change_request_trigger] || label_config["change_request_trigger"] || DEFAULT_CHANGE_REQUEST_LABEL
|
|
41
51
|
@needs_input_label = label_config[:needs_input] || label_config["needs_input"] || DEFAULT_NEEDS_INPUT_LABEL
|
|
@@ -109,19 +119,14 @@ module Aidp
|
|
|
109
119
|
display_message("❌ Change request processing failed: #{e.message}", type: :error)
|
|
110
120
|
Aidp.log_error("change_request_processor", "Change request failed", pr: pr[:number], error: e.message, backtrace: e.backtrace&.first(10))
|
|
111
121
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
begin
|
|
121
|
-
@repository_client.post_comment(pr[:number], error_comment)
|
|
122
|
-
rescue
|
|
123
|
-
nil
|
|
124
|
-
end
|
|
122
|
+
# Record failure state internally but DON'T post error to GitHub
|
|
123
|
+
# (per issue #280 - error messages should never appear on issues)
|
|
124
|
+
@state_store.record_change_request(pr[:number], {
|
|
125
|
+
status: "error",
|
|
126
|
+
error: e.message,
|
|
127
|
+
error_class: e.class.name,
|
|
128
|
+
timestamp: Time.now.utc.iso8601
|
|
129
|
+
})
|
|
125
130
|
end
|
|
126
131
|
|
|
127
132
|
private
|
|
@@ -143,7 +148,7 @@ module Aidp
|
|
|
143
148
|
|
|
144
149
|
def analyze_change_requests(pr_data:, comments:, diff:)
|
|
145
150
|
provider_name = @provider_name || detect_default_provider
|
|
146
|
-
provider = Aidp::ProviderManager.get_provider(provider_name
|
|
151
|
+
provider = Aidp::ProviderManager.get_provider(provider_name)
|
|
147
152
|
|
|
148
153
|
user_prompt = build_analysis_prompt(pr_data: pr_data, comments: comments, diff: diff)
|
|
149
154
|
full_prompt = "#{change_request_system_prompt}\n\n#{user_prompt}"
|
|
@@ -269,6 +274,28 @@ module Aidp
|
|
|
269
274
|
end
|
|
270
275
|
end
|
|
271
276
|
|
|
277
|
+
# Check if PR is linked to an issue - if so, verify implementation completeness
|
|
278
|
+
issue_number = @state_extractor.extract_linked_issue(pr[:body])
|
|
279
|
+
if issue_number
|
|
280
|
+
display_message("🔗 Found linked issue ##{issue_number} - verifying implementation...", type: :info)
|
|
281
|
+
|
|
282
|
+
begin
|
|
283
|
+
issue = @repository_client.fetch_issue(issue_number)
|
|
284
|
+
verification_result = @verifier.verify(issue: issue, working_dir: @project_dir)
|
|
285
|
+
|
|
286
|
+
unless verification_result[:verified]
|
|
287
|
+
handle_incomplete_implementation(pr: pr, analysis: analysis, verification_result: verification_result)
|
|
288
|
+
return
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
display_message("✅ Implementation verified complete", type: :success)
|
|
292
|
+
rescue => e
|
|
293
|
+
display_message("⚠️ Verification check failed: #{e.message}", type: :warn)
|
|
294
|
+
Aidp.log_error("change_request_processor", "Verification failed", pr: pr[:number], error: e.message)
|
|
295
|
+
# Continue with commit/push even if verification fails
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
272
299
|
# Commit and push
|
|
273
300
|
if commit_and_push(pr, analysis)
|
|
274
301
|
handle_success(pr: pr, analysis: analysis)
|
|
@@ -279,7 +306,29 @@ module Aidp
|
|
|
279
306
|
|
|
280
307
|
def checkout_pr_branch(pr_data)
|
|
281
308
|
head_ref = pr_data[:head_ref]
|
|
309
|
+
pr_number = pr_data[:number]
|
|
310
|
+
|
|
311
|
+
# Check if a worktree already exists for this branch
|
|
312
|
+
existing = Aidp::Worktree.find_by_branch(branch: head_ref, project_dir: @project_dir)
|
|
313
|
+
|
|
314
|
+
if existing && existing[:active]
|
|
315
|
+
display_message("🔄 Using existing worktree for branch: #{head_ref}", type: :info)
|
|
316
|
+
Aidp.log_debug("change_request_processor", "worktree_reused", pr_number: pr_number, branch: head_ref, path: existing[:path])
|
|
282
317
|
|
|
318
|
+
# Update @project_dir to point to the worktree
|
|
319
|
+
@project_dir = existing[:path]
|
|
320
|
+
|
|
321
|
+
# Pull latest changes in the worktree
|
|
322
|
+
Dir.chdir(@project_dir) do
|
|
323
|
+
run_git(%w[fetch origin], allow_failure: true)
|
|
324
|
+
run_git(["checkout", head_ref])
|
|
325
|
+
run_git(%w[pull --ff-only], allow_failure: true)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
return
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Otherwise, use the main worktree
|
|
283
332
|
Dir.chdir(@project_dir) do
|
|
284
333
|
# Fetch latest
|
|
285
334
|
run_git(%w[fetch origin])
|
|
@@ -448,6 +497,71 @@ module Aidp
|
|
|
448
497
|
end
|
|
449
498
|
end
|
|
450
499
|
|
|
500
|
+
def handle_incomplete_implementation(pr:, analysis:, verification_result:)
|
|
501
|
+
display_message("⚠️ Implementation incomplete; creating follow-up tasks.", type: :warn)
|
|
502
|
+
|
|
503
|
+
# Create tasks for missing requirements
|
|
504
|
+
if verification_result[:additional_work] && !verification_result[:additional_work].empty?
|
|
505
|
+
create_follow_up_tasks(@project_dir, verification_result[:additional_work])
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Record state but do not post a separate comment
|
|
509
|
+
# (verification details will be included in the next summary comment)
|
|
510
|
+
@state_store.record_change_request(pr[:number], {
|
|
511
|
+
status: "incomplete_implementation",
|
|
512
|
+
timestamp: Time.now.utc.iso8601,
|
|
513
|
+
verification_reasons: verification_result[:reasons],
|
|
514
|
+
missing_items: verification_result[:missing_items],
|
|
515
|
+
additional_work: verification_result[:additional_work]
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
display_message("📝 Recorded incomplete implementation status for PR ##{pr[:number]}", type: :info)
|
|
519
|
+
|
|
520
|
+
# Keep the label so the work loop continues (do NOT remove it)
|
|
521
|
+
Aidp.log_info(
|
|
522
|
+
"change_request_processor",
|
|
523
|
+
"incomplete_implementation",
|
|
524
|
+
pr: pr[:number],
|
|
525
|
+
missing_items: verification_result[:missing_items],
|
|
526
|
+
additional_work: verification_result[:additional_work]
|
|
527
|
+
)
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def create_follow_up_tasks(working_dir, additional_work)
|
|
531
|
+
return if additional_work.nil? || additional_work.empty?
|
|
532
|
+
|
|
533
|
+
tasklist_file = File.join(working_dir, ".aidp", "tasklist.jsonl")
|
|
534
|
+
FileUtils.mkdir_p(File.dirname(tasklist_file))
|
|
535
|
+
|
|
536
|
+
require_relative "../execute/persistent_tasklist"
|
|
537
|
+
tasklist = Aidp::Execute::PersistentTasklist.new(working_dir)
|
|
538
|
+
|
|
539
|
+
additional_work.each do |task_description|
|
|
540
|
+
tasklist.create(
|
|
541
|
+
description: task_description,
|
|
542
|
+
priority: :high,
|
|
543
|
+
source: "verification"
|
|
544
|
+
)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
display_message("📝 Created #{additional_work.length} follow-up task(s) for continued work", type: :info)
|
|
548
|
+
|
|
549
|
+
Aidp.log_info(
|
|
550
|
+
"change_request_processor",
|
|
551
|
+
"created_follow_up_tasks",
|
|
552
|
+
task_count: additional_work.length,
|
|
553
|
+
working_dir: working_dir
|
|
554
|
+
)
|
|
555
|
+
rescue => e
|
|
556
|
+
display_message("⚠️ Failed to create follow-up tasks: #{e.message}", type: :warn)
|
|
557
|
+
Aidp.log_error(
|
|
558
|
+
"change_request_processor",
|
|
559
|
+
"failed_to_create_follow_up_tasks",
|
|
560
|
+
error: e.message,
|
|
561
|
+
backtrace: e.backtrace&.first(5)
|
|
562
|
+
)
|
|
563
|
+
end
|
|
564
|
+
|
|
451
565
|
def handle_clarification_needed(pr:, analysis:)
|
|
452
566
|
existing_data = @state_store.change_request_data(pr[:number])
|
|
453
567
|
clarification_count = (existing_data&.dig("clarification_count") || 0) + 1
|
|
@@ -11,6 +11,9 @@ require_relative "../harness/config_manager"
|
|
|
11
11
|
require_relative "../execute/prompt_manager"
|
|
12
12
|
require_relative "../harness/runner"
|
|
13
13
|
require_relative "../harness/state_manager"
|
|
14
|
+
require_relative "../worktree"
|
|
15
|
+
require_relative "github_state_extractor"
|
|
16
|
+
require_relative "ci_log_extractor"
|
|
14
17
|
|
|
15
18
|
module Aidp
|
|
16
19
|
module Watch
|
|
@@ -30,6 +33,7 @@ module Aidp
|
|
|
30
33
|
def initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, verbose: false)
|
|
31
34
|
@repository_client = repository_client
|
|
32
35
|
@state_store = state_store
|
|
36
|
+
@state_extractor = GitHubStateExtractor.new(repository_client: repository_client)
|
|
33
37
|
@provider_name = provider_name
|
|
34
38
|
@project_dir = project_dir
|
|
35
39
|
@verbose = verbose
|
|
@@ -41,9 +45,12 @@ module Aidp
|
|
|
41
45
|
def process(pr)
|
|
42
46
|
number = pr[:number]
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
Aidp.log_debug("ci_fix_processor", "process_started", pr_number: number, pr_title: pr[:title])
|
|
49
|
+
|
|
50
|
+
# Check if already processed successfully via GitHub comments
|
|
51
|
+
if @state_extractor.ci_fix_completed?(pr)
|
|
46
52
|
display_message("ℹ️ CI fix for PR ##{number} already completed. Skipping.", type: :muted)
|
|
53
|
+
Aidp.log_debug("ci_fix_processor", "already_completed", pr_number: number)
|
|
47
54
|
return
|
|
48
55
|
end
|
|
49
56
|
|
|
@@ -53,9 +60,16 @@ module Aidp
|
|
|
53
60
|
pr_data = @repository_client.fetch_pull_request(number)
|
|
54
61
|
ci_status = @repository_client.fetch_ci_status(number)
|
|
55
62
|
|
|
63
|
+
Aidp.log_debug("ci_fix_processor", "ci_status_fetched",
|
|
64
|
+
pr_number: number,
|
|
65
|
+
ci_state: ci_status[:state],
|
|
66
|
+
check_count: ci_status[:checks]&.length || 0,
|
|
67
|
+
checks: ci_status[:checks]&.map { |c| {name: c[:name], status: c[:status], conclusion: c[:conclusion]} })
|
|
68
|
+
|
|
56
69
|
# Check if there are failures
|
|
57
70
|
if ci_status[:state] == "success"
|
|
58
71
|
display_message("✅ CI is passing for PR ##{number}. No fixes needed.", type: :success)
|
|
72
|
+
Aidp.log_debug("ci_fix_processor", "ci_passing", pr_number: number)
|
|
59
73
|
post_success_comment(pr_data)
|
|
60
74
|
@state_store.record_ci_fix(number, {status: "no_failures", timestamp: Time.now.utc.iso8601})
|
|
61
75
|
begin
|
|
@@ -68,14 +82,25 @@ module Aidp
|
|
|
68
82
|
|
|
69
83
|
if ci_status[:state] == "pending"
|
|
70
84
|
display_message("⏳ CI is still running for PR ##{number}. Skipping for now.", type: :muted)
|
|
85
|
+
Aidp.log_debug("ci_fix_processor", "ci_pending", pr_number: number)
|
|
71
86
|
return
|
|
72
87
|
end
|
|
73
88
|
|
|
74
89
|
# Get failed checks
|
|
75
90
|
failed_checks = ci_status[:checks].select { |check| check[:conclusion] == "failure" }
|
|
76
91
|
|
|
92
|
+
Aidp.log_debug("ci_fix_processor", "failed_checks_filtered",
|
|
93
|
+
pr_number: number,
|
|
94
|
+
total_checks: ci_status[:checks]&.length || 0,
|
|
95
|
+
failed_count: failed_checks.length,
|
|
96
|
+
failed_checks: failed_checks.map { |c| c[:name] })
|
|
97
|
+
|
|
77
98
|
if failed_checks.empty?
|
|
78
99
|
display_message("⚠️ No specific failed checks found for PR ##{number}.", type: :warn)
|
|
100
|
+
Aidp.log_debug("ci_fix_processor", "no_failed_checks",
|
|
101
|
+
pr_number: number,
|
|
102
|
+
ci_state: ci_status[:state],
|
|
103
|
+
all_checks: ci_status[:checks]&.map { |c| {name: c[:name], conclusion: c[:conclusion]} })
|
|
79
104
|
return
|
|
80
105
|
end
|
|
81
106
|
|
|
@@ -99,31 +124,36 @@ module Aidp
|
|
|
99
124
|
display_message("❌ CI fix failed: #{e.message}", type: :error)
|
|
100
125
|
Aidp.log_error("ci_fix_processor", "CI fix failed", pr: pr[:number], error: e.message, backtrace: e.backtrace&.first(10))
|
|
101
126
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
begin
|
|
111
|
-
@repository_client.post_comment(pr[:number], error_comment)
|
|
112
|
-
rescue
|
|
113
|
-
nil
|
|
114
|
-
end
|
|
127
|
+
# Record failure state internally but DON'T post error to GitHub
|
|
128
|
+
# (per issue #280 - error messages should never appear on issues)
|
|
129
|
+
@state_store.record_ci_fix(pr[:number], {
|
|
130
|
+
status: "error",
|
|
131
|
+
error: e.message,
|
|
132
|
+
error_class: e.class.name,
|
|
133
|
+
timestamp: Time.now.utc.iso8601
|
|
134
|
+
})
|
|
115
135
|
end
|
|
116
136
|
|
|
117
137
|
private
|
|
118
138
|
|
|
119
139
|
def analyze_and_fix(pr_data:, ci_status:, failed_checks:)
|
|
120
|
-
#
|
|
140
|
+
# Extract concise failure information to reduce token usage
|
|
141
|
+
provider = detect_default_provider
|
|
142
|
+
provider_manager = Aidp::ProviderManager.get_provider(provider)
|
|
143
|
+
log_extractor = CiLogExtractor.new(provider_manager: provider_manager)
|
|
144
|
+
|
|
121
145
|
failure_details = failed_checks.map do |check|
|
|
146
|
+
Aidp.log_debug("ci_fix_processor", "extracting_logs", check_name: check[:name])
|
|
147
|
+
extracted = log_extractor.extract_failure_info(
|
|
148
|
+
check: check,
|
|
149
|
+
check_run_url: check[:details_url]
|
|
150
|
+
)
|
|
151
|
+
|
|
122
152
|
{
|
|
123
153
|
name: check[:name],
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
154
|
+
summary: extracted[:summary],
|
|
155
|
+
details: extracted[:details],
|
|
156
|
+
extraction_method: extracted[:extraction_method]
|
|
127
157
|
}
|
|
128
158
|
end
|
|
129
159
|
|
|
@@ -131,14 +161,14 @@ module Aidp
|
|
|
131
161
|
analysis = analyze_failures_with_ai(pr_data: pr_data, failures: failure_details)
|
|
132
162
|
|
|
133
163
|
if analysis[:can_fix]
|
|
134
|
-
#
|
|
135
|
-
|
|
164
|
+
# Setup worktree for the PR branch
|
|
165
|
+
working_dir = setup_pr_worktree(pr_data)
|
|
136
166
|
|
|
137
167
|
# Apply the proposed fixes
|
|
138
|
-
apply_fixes(analysis[:fixes])
|
|
168
|
+
apply_fixes(analysis[:fixes], working_dir: working_dir)
|
|
139
169
|
|
|
140
170
|
# Commit and push
|
|
141
|
-
if commit_and_push(pr_data, analysis)
|
|
171
|
+
if commit_and_push(pr_data, analysis, working_dir: working_dir)
|
|
142
172
|
{success: true, analysis: analysis, commit_created: true}
|
|
143
173
|
else
|
|
144
174
|
{success: false, analysis: analysis, reason: "No changes to commit"}
|
|
@@ -152,7 +182,7 @@ module Aidp
|
|
|
152
182
|
|
|
153
183
|
def analyze_failures_with_ai(pr_data:, failures:)
|
|
154
184
|
provider_name = @provider_name || detect_default_provider
|
|
155
|
-
provider = Aidp::ProviderManager.get_provider(provider_name
|
|
185
|
+
provider = Aidp::ProviderManager.get_provider(provider_name)
|
|
156
186
|
|
|
157
187
|
user_prompt = build_ci_analysis_prompt(pr_data: pr_data, failures: failures)
|
|
158
188
|
full_prompt = "#{ci_fix_system_prompt}\n\n#{user_prompt}"
|
|
@@ -222,12 +252,20 @@ module Aidp
|
|
|
222
252
|
#{pr_data[:body]}
|
|
223
253
|
|
|
224
254
|
**Failed Checks:**
|
|
225
|
-
#{failures.map { |f|
|
|
255
|
+
#{failures.map { |f| format_failure_for_prompt(f) }.join("\n\n")}
|
|
226
256
|
|
|
227
257
|
Please analyze these failures and propose fixes if possible.
|
|
228
258
|
PROMPT
|
|
229
259
|
end
|
|
230
260
|
|
|
261
|
+
def format_failure_for_prompt(failure)
|
|
262
|
+
output = "**Check: #{failure[:name]}**\n"
|
|
263
|
+
output += "Summary: #{failure[:summary]}\n" if failure[:summary]
|
|
264
|
+
output += "\nDetails:\n```\n#{failure[:details]}\n```" if failure[:details]
|
|
265
|
+
output += "\n(Logs extracted using: #{failure[:extraction_method]})" if failure[:extraction_method]
|
|
266
|
+
output
|
|
267
|
+
end
|
|
268
|
+
|
|
231
269
|
def detect_default_provider
|
|
232
270
|
config_manager = Aidp::Harness::ConfigManager.new(@project_dir)
|
|
233
271
|
config_manager.default_provider || "anthropic"
|
|
@@ -261,26 +299,54 @@ module Aidp
|
|
|
261
299
|
end
|
|
262
300
|
end
|
|
263
301
|
|
|
264
|
-
def
|
|
302
|
+
def setup_pr_worktree(pr_data)
|
|
265
303
|
head_ref = pr_data[:head_ref]
|
|
304
|
+
pr_number = pr_data[:number]
|
|
305
|
+
|
|
306
|
+
# Check if a worktree already exists for this branch
|
|
307
|
+
existing = Aidp::Worktree.find_by_branch(branch: head_ref, project_dir: @project_dir)
|
|
308
|
+
|
|
309
|
+
if existing && existing[:active]
|
|
310
|
+
display_message("🔄 Reusing existing worktree for branch: #{head_ref}", type: :info)
|
|
311
|
+
Aidp.log_debug("ci_fix_processor", "worktree_reused", pr_number: pr_number, branch: head_ref, path: existing[:path])
|
|
312
|
+
|
|
313
|
+
# Pull latest changes in the worktree
|
|
314
|
+
Dir.chdir(existing[:path]) do
|
|
315
|
+
run_git(%w[fetch origin], allow_failure: true)
|
|
316
|
+
run_git(["checkout", head_ref])
|
|
317
|
+
run_git(%w[pull --ff-only], allow_failure: true)
|
|
318
|
+
end
|
|
266
319
|
|
|
320
|
+
return existing[:path]
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Create a new worktree for this PR
|
|
324
|
+
slug = "pr-#{pr_number}-ci-fix"
|
|
325
|
+
display_message("🌿 Creating worktree for PR ##{pr_number}: #{head_ref}", type: :info)
|
|
326
|
+
|
|
327
|
+
# Fetch the branch first
|
|
267
328
|
Dir.chdir(@project_dir) do
|
|
268
|
-
# Fetch latest
|
|
269
329
|
run_git(%w[fetch origin])
|
|
330
|
+
end
|
|
270
331
|
|
|
271
|
-
|
|
272
|
-
|
|
332
|
+
# Create worktree
|
|
333
|
+
result = Aidp::Worktree.create(
|
|
334
|
+
slug: slug,
|
|
335
|
+
project_dir: @project_dir,
|
|
336
|
+
branch: head_ref,
|
|
337
|
+
base_branch: nil # Branch already exists, no base needed
|
|
338
|
+
)
|
|
273
339
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
340
|
+
worktree_path = result[:path]
|
|
341
|
+
Aidp.log_debug("ci_fix_processor", "worktree_created", pr_number: pr_number, branch: head_ref, path: worktree_path)
|
|
342
|
+
display_message("✅ Worktree created at #{worktree_path}", type: :success)
|
|
277
343
|
|
|
278
|
-
|
|
344
|
+
worktree_path
|
|
279
345
|
end
|
|
280
346
|
|
|
281
|
-
def apply_fixes(fixes)
|
|
347
|
+
def apply_fixes(fixes, working_dir:)
|
|
282
348
|
fixes.each do |fix|
|
|
283
|
-
file_path = File.join(
|
|
349
|
+
file_path = File.join(working_dir, fix["file"])
|
|
284
350
|
|
|
285
351
|
case fix["action"]
|
|
286
352
|
when "create", "edit"
|
|
@@ -296,8 +362,8 @@ module Aidp
|
|
|
296
362
|
end
|
|
297
363
|
end
|
|
298
364
|
|
|
299
|
-
def commit_and_push(pr_data, analysis)
|
|
300
|
-
Dir.chdir(
|
|
365
|
+
def commit_and_push(pr_data, analysis, working_dir:)
|
|
366
|
+
Dir.chdir(working_dir) do
|
|
301
367
|
# Check if there are changes
|
|
302
368
|
status_output = run_git(%w[status --porcelain])
|
|
303
369
|
if status_output.strip.empty?
|