aidp 0.34.1 → 0.39.3

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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +14 -14
  3. data/lib/aidp/agent_harness_adapter.rb +309 -0
  4. data/lib/aidp/cli/issue_importer.rb +20 -6
  5. data/lib/aidp/cli/jobs_command.rb +1 -1
  6. data/lib/aidp/cli/models_command.rb +9 -16
  7. data/lib/aidp/cli/prompts_command.rb +253 -0
  8. data/lib/aidp/cli/security_command.rb +4 -10
  9. data/lib/aidp/cli/storage_command.rb +277 -0
  10. data/lib/aidp/cli/temporal_command.rb +460 -0
  11. data/lib/aidp/cli.rb +24 -2
  12. data/lib/aidp/config.rb +15 -0
  13. data/lib/aidp/config_paths.rb +12 -2
  14. data/lib/aidp/database/migrations.rb +114 -0
  15. data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
  16. data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
  17. data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
  18. data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
  19. data/lib/aidp/database/repositories/job_repository.rb +278 -0
  20. data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
  21. data/lib/aidp/database/repositories/progress_repository.rb +210 -0
  22. data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
  23. data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
  24. data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
  25. data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
  26. data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
  27. data/lib/aidp/database/repositories/task_repository.rb +231 -0
  28. data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
  29. data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
  30. data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
  31. data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
  32. data/lib/aidp/database/repository.rb +166 -0
  33. data/lib/aidp/database/schema.rb +347 -0
  34. data/lib/aidp/database/storage_migrator.rb +693 -0
  35. data/lib/aidp/database.rb +139 -0
  36. data/lib/aidp/debug_mixin.rb +2 -2
  37. data/lib/aidp/execute/checkpoint.rb +26 -0
  38. data/lib/aidp/execute/checkpoint_display.rb +16 -0
  39. data/lib/aidp/execute/prompt_evaluator.rb +30 -57
  40. data/lib/aidp/execute/work_loop_runner.rb +294 -46
  41. data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
  42. data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
  43. data/lib/aidp/harness/ai_decision_engine.rb +78 -296
  44. data/lib/aidp/harness/config_schema.rb +59 -0
  45. data/lib/aidp/harness/configuration.rb +229 -10
  46. data/lib/aidp/harness/deprecation_cache.rb +53 -11
  47. data/lib/aidp/harness/enhanced_runner.rb +1 -1
  48. data/lib/aidp/harness/error_handler.rb +11 -5
  49. data/lib/aidp/harness/provider_config.rb +36 -0
  50. data/lib/aidp/harness/provider_factory.rb +26 -24
  51. data/lib/aidp/harness/provider_info.rb +9 -5
  52. data/lib/aidp/harness/provider_manager.rb +2 -2
  53. data/lib/aidp/harness/provider_metrics.rb +103 -1
  54. data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
  55. data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
  56. data/lib/aidp/harness/runner.rb +7 -2
  57. data/lib/aidp/harness/state_manager.rb +1 -1
  58. data/lib/aidp/harness/test_runner.rb +167 -11
  59. data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
  60. data/lib/aidp/harness/usage_limit.rb +179 -0
  61. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  62. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  63. data/lib/aidp/harness/usage_period.rb +234 -0
  64. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  65. data/lib/aidp/init/runner.rb +25 -4
  66. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  67. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  68. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  69. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  70. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  71. data/lib/aidp/logger.rb +25 -7
  72. data/lib/aidp/orchestration_adapter.rb +279 -0
  73. data/lib/aidp/pr_worktree_manager.rb +4 -15
  74. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  75. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  76. data/lib/aidp/prompts/template_evolver.rb +305 -0
  77. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  78. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  79. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  80. data/lib/aidp/setup/wizard.rb +616 -176
  81. data/lib/aidp/shell_executor.rb +58 -0
  82. data/lib/aidp/style_guide/selector.rb +56 -1
  83. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  84. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  85. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  86. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  87. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  88. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  89. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  90. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  91. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  92. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  93. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  94. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  95. data/lib/aidp/temporal/configuration.rb +169 -0
  96. data/lib/aidp/temporal/connection.rb +93 -0
  97. data/lib/aidp/temporal/worker.rb +119 -0
  98. data/lib/aidp/temporal/workflow_client.rb +171 -0
  99. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  100. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  101. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  102. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  103. data/lib/aidp/temporal.rb +120 -0
  104. data/lib/aidp/version.rb +1 -1
  105. data/lib/aidp/watch/base_processor.rb +35 -0
  106. data/lib/aidp/watch/build_processor.rb +132 -0
  107. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  108. data/lib/aidp/watch/rebase_processor.rb +245 -0
  109. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  110. data/lib/aidp/watch/runner.rb +366 -285
  111. data/lib/aidp/watch/state_store.rb +80 -0
  112. data/lib/aidp/watch/work_item.rb +96 -0
  113. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  114. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  115. data/lib/aidp/workflows/guided_agent.rb +1 -1
  116. data/lib/aidp/worktree.rb +62 -0
  117. data/templates/aidp.yml.example +70 -0
  118. metadata +117 -16
  119. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  120. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  121. data/lib/aidp/providers/adapter.rb +0 -239
  122. data/lib/aidp/providers/aider.rb +0 -264
  123. data/lib/aidp/providers/anthropic.rb +0 -614
  124. data/lib/aidp/providers/base.rb +0 -617
  125. data/lib/aidp/providers/capability_registry.rb +0 -204
  126. data/lib/aidp/providers/codex.rb +0 -267
  127. data/lib/aidp/providers/cursor.rb +0 -304
  128. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  129. data/lib/aidp/providers/gemini.rb +0 -110
  130. data/lib/aidp/providers/github_copilot.rb +0 -227
  131. data/lib/aidp/providers/kilocode.rb +0 -157
  132. data/lib/aidp/providers/opencode.rb +0 -139
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_activity"
4
+
5
+ module Aidp
6
+ module Temporal
7
+ module Activities
8
+ # Activity that records a checkpoint for progress tracking
9
+ # Persists state for recovery and observability
10
+ class RecordCheckpointActivity < BaseActivity
11
+ def execute(input)
12
+ with_activity_context do
13
+ project_dir = input[:project_dir]
14
+ step_name = input[:step_name]
15
+ iteration = input[:iteration]
16
+ state = input[:state]
17
+ test_results = input[:test_results]
18
+
19
+ log_activity("recording_checkpoint",
20
+ project_dir: project_dir,
21
+ step_name: step_name,
22
+ iteration: iteration)
23
+
24
+ # Create checkpoint recorder
25
+ checkpoint = Aidp::Execute::Checkpoint.new(project_dir)
26
+
27
+ # Build metrics from test results
28
+ metrics = build_metrics(test_results)
29
+
30
+ # Record the checkpoint
31
+ checkpoint.record_checkpoint(
32
+ step_name,
33
+ iteration,
34
+ {
35
+ state: state,
36
+ test_results_summary: summarize_test_results(test_results),
37
+ metrics: metrics,
38
+ workflow_type: "temporal"
39
+ }
40
+ )
41
+
42
+ success_result(
43
+ step_name: step_name,
44
+ iteration: iteration,
45
+ timestamp: Time.now.iso8601
46
+ )
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def build_metrics(test_results)
53
+ return {} unless test_results
54
+
55
+ results = test_results[:results] || {}
56
+ passing = results.count { |_, r| r[:success] }
57
+ total = results.count
58
+
59
+ {
60
+ checks_passing: passing,
61
+ checks_total: total,
62
+ pass_rate: (total > 0) ? (passing.to_f / total * 100).round(1) : 0
63
+ }
64
+ end
65
+
66
+ def summarize_test_results(test_results)
67
+ return {} unless test_results
68
+
69
+ results = test_results[:results] || {}
70
+
71
+ results.transform_values do |result|
72
+ {
73
+ success: result[:success],
74
+ duration: result[:duration]
75
+ }
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_activity"
4
+
5
+ module Aidp
6
+ module Temporal
7
+ module Activities
8
+ # Activity that executes an AI agent iteration
9
+ # Wraps the existing AIDP agent execution with Temporal durability
10
+ class RunAgentActivity < BaseActivity
11
+ def execute(input)
12
+ with_activity_context do
13
+ project_dir = input[:project_dir]
14
+ step_name = input[:step_name]
15
+ iteration = input[:iteration]
16
+ injected_instructions = input[:injected_instructions] || []
17
+ escalate = input[:escalate] || false
18
+
19
+ log_activity("executing_agent",
20
+ project_dir: project_dir,
21
+ step_name: step_name,
22
+ iteration: iteration,
23
+ instructions_count: injected_instructions.length,
24
+ escalate: escalate)
25
+
26
+ # Load configuration
27
+ config = load_config(project_dir)
28
+ provider_manager = create_provider_manager(project_dir, config)
29
+
30
+ # Get prompt content
31
+ prompt_manager = Aidp::Execute::PromptManager.new(project_dir, config: config)
32
+ prompt_content = prompt_manager.read
33
+
34
+ unless prompt_content
35
+ return error_result("No PROMPT.md found")
36
+ end
37
+
38
+ # Inject any queued instructions
39
+ if injected_instructions.any?
40
+ prompt_content = inject_instructions(prompt_content, injected_instructions)
41
+ prompt_manager.write(prompt_content)
42
+ end
43
+
44
+ # Select model based on escalation status
45
+ model_selector = create_model_selector(config, escalate: escalate)
46
+ provider, model = model_selector.select
47
+
48
+ log_activity("agent_model_selected",
49
+ provider: provider,
50
+ model: model,
51
+ escalate: escalate)
52
+
53
+ # Periodic heartbeat during agent execution
54
+ heartbeat_thread = start_heartbeat_thread(iteration: iteration)
55
+
56
+ begin
57
+ # Execute agent
58
+ result = execute_agent(
59
+ project_dir: project_dir,
60
+ provider_manager: provider_manager,
61
+ provider: provider,
62
+ model: model,
63
+ prompt_content: prompt_content
64
+ )
65
+
66
+ check_cancellation!
67
+
68
+ if result[:success]
69
+ success_result(
70
+ result: result[:output],
71
+ provider: provider,
72
+ model: model,
73
+ iteration: iteration
74
+ )
75
+ else
76
+ error_result(result[:error] || "Agent execution failed",
77
+ provider: provider,
78
+ model: model,
79
+ iteration: iteration)
80
+ end
81
+ ensure
82
+ heartbeat_thread&.kill
83
+ end
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def inject_instructions(prompt_content, instructions)
90
+ injection_text = instructions.map { |i| i[:content] || i }.join("\n\n")
91
+
92
+ <<~PROMPT
93
+ #{prompt_content}
94
+
95
+ ---
96
+ ## Additional Instructions (Injected)
97
+
98
+ #{injection_text}
99
+ PROMPT
100
+ end
101
+
102
+ def create_model_selector(config, escalate:)
103
+ require_relative "../../harness/thinking_depth_manager"
104
+
105
+ manager = Aidp::Harness::ThinkingDepthManager.new(config)
106
+ manager.escalate if escalate
107
+ manager
108
+ end
109
+
110
+ def start_heartbeat_thread(iteration:)
111
+ Thread.new do
112
+ loop do
113
+ sleep 30
114
+ heartbeat(iteration: iteration, status: "running")
115
+ end
116
+ end
117
+ end
118
+
119
+ def execute_agent(project_dir:, provider_manager:, provider:, model:, prompt_content:)
120
+ # Use the provider manager to execute
121
+ prompt_path = File.join(project_dir, ".aidp", "PROMPT.md")
122
+
123
+ result = provider_manager.execute_with_provider(
124
+ provider,
125
+ model: model,
126
+ prompt_path: prompt_path
127
+ )
128
+
129
+ {success: result[:success], output: result[:output], error: result[:error]}
130
+ rescue => e
131
+ Aidp.log_error("run_agent_activity", "agent_execution_failed",
132
+ error: e.message,
133
+ error_class: e.class.name)
134
+ {success: false, error: e.message}
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_activity"
4
+
5
+ module Aidp
6
+ module Temporal
7
+ module Activities
8
+ # Activity that runs tests and linters
9
+ # Wraps AIDP test runner with Temporal durability
10
+ class RunTestsActivity < BaseActivity
11
+ def execute(input)
12
+ with_activity_context do
13
+ project_dir = input[:project_dir]
14
+ iteration = input[:iteration]
15
+ phases = input[:phases] || [:test, :lint]
16
+
17
+ log_activity("running_tests",
18
+ project_dir: project_dir,
19
+ iteration: iteration,
20
+ phases: phases)
21
+
22
+ # Load configuration
23
+ config = load_config(project_dir)
24
+
25
+ # Create test runner
26
+ test_runner = Aidp::Harness::TestRunner.new(project_dir, config)
27
+
28
+ # Run tests with periodic heartbeat
29
+ results = {}
30
+ all_passing = true
31
+ partial_pass = false
32
+
33
+ phases.each do |phase|
34
+ heartbeat(phase: phase, iteration: iteration)
35
+ check_cancellation!
36
+
37
+ result = run_phase(test_runner, phase)
38
+ results[phase] = result
39
+
40
+ if result[:success]
41
+ partial_pass = true
42
+ else
43
+ all_passing = false
44
+ end
45
+
46
+ log_activity("phase_complete",
47
+ phase: phase,
48
+ success: result[:success],
49
+ duration: result[:duration])
50
+ end
51
+
52
+ success_result(
53
+ all_passing: all_passing,
54
+ partial_pass: partial_pass,
55
+ results: results,
56
+ iteration: iteration,
57
+ summary: build_summary(results)
58
+ )
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def run_phase(test_runner, phase)
65
+ start_time = Time.now
66
+
67
+ result = case phase
68
+ when :test, "test"
69
+ test_runner.run_tests
70
+ when :lint, "lint"
71
+ test_runner.run_lint
72
+ when :typecheck, "typecheck"
73
+ test_runner.run_typecheck
74
+ when :build, "build"
75
+ test_runner.run_build
76
+ else
77
+ {success: false, output: "Unknown phase: #{phase}"}
78
+ end
79
+
80
+ duration = Time.now - start_time
81
+
82
+ {
83
+ success: result[:success],
84
+ output: result[:output],
85
+ exit_code: result[:exit_code],
86
+ duration: duration.round(2)
87
+ }
88
+ rescue => e
89
+ Aidp.log_error("run_tests_activity", "phase_failed",
90
+ phase: phase,
91
+ error: e.message)
92
+
93
+ {
94
+ success: false,
95
+ output: e.message,
96
+ exit_code: -1,
97
+ duration: (Time.now - start_time).round(2)
98
+ }
99
+ end
100
+
101
+ def build_summary(results)
102
+ passing = results.count { |_, r| r[:success] }
103
+ total = results.count
104
+
105
+ {
106
+ passing: passing,
107
+ total: total,
108
+ failed_phases: results.reject { |_, r| r[:success] }.keys
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_activity"
4
+
5
+ module Aidp
6
+ module Temporal
7
+ module Activities
8
+ # Activity that runs a single work loop iteration
9
+ # Combines agent execution and testing in one activity
10
+ # Used by IssueToPrWorkflow for simplified orchestration
11
+ class RunWorkLoopIterationActivity < BaseActivity
12
+ def execute(input)
13
+ with_activity_context do
14
+ project_dir = input[:project_dir]
15
+ issue_number = input[:issue_number]
16
+ plan = input[:plan]
17
+ iteration = input[:iteration]
18
+ injected_instructions = input[:injected_instructions] || []
19
+
20
+ log_activity("running_work_loop_iteration",
21
+ project_dir: project_dir,
22
+ issue_number: issue_number,
23
+ iteration: iteration)
24
+
25
+ # Start heartbeat thread
26
+ heartbeat_thread = start_heartbeat_thread(iteration: iteration)
27
+
28
+ begin
29
+ # Phase 1: Run agent
30
+ heartbeat(phase: "agent", iteration: iteration)
31
+ agent_result = run_agent(
32
+ project_dir: project_dir,
33
+ issue_number: issue_number,
34
+ plan: plan,
35
+ iteration: iteration,
36
+ injected_instructions: injected_instructions
37
+ )
38
+
39
+ check_cancellation!
40
+
41
+ unless agent_result[:success]
42
+ return error_result("Agent failed: #{agent_result[:error]}",
43
+ iteration: iteration,
44
+ tests_passing: false)
45
+ end
46
+
47
+ # Phase 2: Run tests
48
+ heartbeat(phase: "tests", iteration: iteration)
49
+ test_result = run_tests(project_dir: project_dir)
50
+
51
+ check_cancellation!
52
+
53
+ # Phase 3: Handle result
54
+ if test_result[:all_passing]
55
+ success_result(
56
+ result: {
57
+ agent_output: agent_result[:output],
58
+ test_results: test_result
59
+ },
60
+ iteration: iteration,
61
+ tests_passing: true
62
+ )
63
+ else
64
+ # Update prompt with failures for next iteration
65
+ update_prompt_with_failures(project_dir, test_result)
66
+
67
+ success_result(
68
+ result: {
69
+ agent_output: agent_result[:output],
70
+ test_results: test_result
71
+ },
72
+ iteration: iteration,
73
+ tests_passing: false
74
+ )
75
+ end
76
+ ensure
77
+ heartbeat_thread&.kill
78
+ end
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ def start_heartbeat_thread(iteration:)
85
+ Thread.new do
86
+ loop do
87
+ sleep 30
88
+ heartbeat(iteration: iteration, status: "running")
89
+ end
90
+ end
91
+ end
92
+
93
+ def run_agent(project_dir:, issue_number:, plan:, iteration:, injected_instructions:)
94
+ config = load_config(project_dir)
95
+ provider_manager = create_provider_manager(project_dir, config)
96
+
97
+ # Prepare prompt with plan context
98
+ prompt_manager = Aidp::Execute::PromptManager.new(project_dir, config: config)
99
+ current_prompt = prompt_manager.read
100
+
101
+ unless current_prompt
102
+ # Create initial prompt from plan
103
+ current_prompt = build_initial_prompt(plan, issue_number)
104
+ prompt_manager.write(current_prompt)
105
+ end
106
+
107
+ # Inject instructions if any
108
+ if injected_instructions.any?
109
+ current_prompt = inject_instructions(current_prompt, injected_instructions)
110
+ prompt_manager.write(current_prompt)
111
+ end
112
+
113
+ # Select model
114
+ require_relative "../../harness/thinking_depth_manager"
115
+ model_selector = Aidp::Harness::ThinkingDepthManager.new(config)
116
+ provider, model = model_selector.select
117
+
118
+ log_activity("agent_executing",
119
+ provider: provider,
120
+ model: model,
121
+ iteration: iteration)
122
+
123
+ # Execute
124
+ result = provider_manager.execute_with_provider(
125
+ provider,
126
+ model: model,
127
+ prompt_path: prompt_manager.prompt_path
128
+ )
129
+
130
+ {success: result[:success], output: result[:output], error: result[:error]}
131
+ rescue => e
132
+ Aidp.log_error("run_work_loop_iteration_activity", "agent_failed",
133
+ error: e.message,
134
+ iteration: iteration)
135
+ {success: false, error: e.message}
136
+ end
137
+
138
+ def run_tests(project_dir:)
139
+ config = load_config(project_dir)
140
+ test_runner = Aidp::Harness::TestRunner.new(project_dir, config)
141
+
142
+ results = {}
143
+
144
+ # Run tests
145
+ test_result = test_runner.run_tests
146
+ results[:test] = test_result
147
+
148
+ # Run lint
149
+ lint_result = test_runner.run_lint
150
+ results[:lint] = lint_result
151
+
152
+ all_passing = results.values.all? { |r| r[:success] }
153
+
154
+ {
155
+ all_passing: all_passing,
156
+ results: results
157
+ }
158
+ rescue => e
159
+ Aidp.log_error("run_work_loop_iteration_activity", "tests_failed",
160
+ error: e.message)
161
+ {all_passing: false, results: {}, error: e.message}
162
+ end
163
+
164
+ def build_initial_prompt(plan, issue_number)
165
+ <<~PROMPT
166
+ # Implementation Task: Issue ##{issue_number}
167
+
168
+ ## Objective
169
+
170
+ #{plan[:title]}
171
+
172
+ ## Requirements
173
+
174
+ #{plan[:requirements]&.map { |r| "- #{r}" }&.join("\n")}
175
+
176
+ ## Implementation Steps
177
+
178
+ #{plan[:steps]&.map { |s| "- #{s[:description]}" }&.join("\n")}
179
+
180
+ ## Instructions
181
+
182
+ 1. Review the existing codebase
183
+ 2. Implement the required changes
184
+ 3. Ensure tests pass
185
+ 4. Follow the project's style guide
186
+ PROMPT
187
+ end
188
+
189
+ def inject_instructions(prompt, instructions)
190
+ instruction_text = instructions.map { |i| i[:content] || i }.join("\n\n")
191
+
192
+ <<~PROMPT
193
+ #{prompt}
194
+
195
+ ---
196
+ ## Additional Instructions
197
+
198
+ #{instruction_text}
199
+ PROMPT
200
+ end
201
+
202
+ def update_prompt_with_failures(project_dir, test_result)
203
+ prompt_manager = Aidp::Execute::PromptManager.new(project_dir)
204
+ current_prompt = prompt_manager.read
205
+
206
+ return unless current_prompt
207
+
208
+ failures = extract_failures(test_result)
209
+
210
+ updated_prompt = <<~PROMPT
211
+ #{current_prompt}
212
+
213
+ ---
214
+ ## Failures to Fix
215
+
216
+ #{failures}
217
+
218
+ Please address the above failures.
219
+ PROMPT
220
+
221
+ prompt_manager.write(updated_prompt)
222
+ end
223
+
224
+ def extract_failures(test_result)
225
+ results = test_result[:results] || {}
226
+ sections = []
227
+
228
+ results.each do |phase, result|
229
+ next if result[:success]
230
+
231
+ sections << "### #{phase.to_s.capitalize} Failures"
232
+ sections << ""
233
+ sections << "```"
234
+ sections << truncate(result[:output], 1500)
235
+ sections << "```"
236
+ sections << ""
237
+ end
238
+
239
+ sections.join("\n")
240
+ end
241
+
242
+ def truncate(text, max_length)
243
+ return "" unless text
244
+ (text.length > max_length) ? "#{text.slice(0, max_length)}\n...(truncated)" : text
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end