aidp 0.34.0 → 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 (133) 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/ui.rb +11 -0
  61. data/lib/aidp/harness/usage_limit.rb +179 -0
  62. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  63. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  64. data/lib/aidp/harness/usage_period.rb +234 -0
  65. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  66. data/lib/aidp/init/runner.rb +25 -4
  67. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  68. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  69. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  70. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  71. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  72. data/lib/aidp/logger.rb +25 -7
  73. data/lib/aidp/orchestration_adapter.rb +279 -0
  74. data/lib/aidp/pr_worktree_manager.rb +4 -15
  75. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  76. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  77. data/lib/aidp/prompts/template_evolver.rb +305 -0
  78. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  79. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  80. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  81. data/lib/aidp/setup/wizard.rb +616 -176
  82. data/lib/aidp/shell_executor.rb +58 -0
  83. data/lib/aidp/style_guide/selector.rb +56 -1
  84. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  85. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  86. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  87. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  88. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  89. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  90. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  91. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  92. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  93. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  94. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  95. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  96. data/lib/aidp/temporal/configuration.rb +169 -0
  97. data/lib/aidp/temporal/connection.rb +93 -0
  98. data/lib/aidp/temporal/worker.rb +119 -0
  99. data/lib/aidp/temporal/workflow_client.rb +171 -0
  100. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  101. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  102. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  103. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  104. data/lib/aidp/temporal.rb +120 -0
  105. data/lib/aidp/version.rb +1 -1
  106. data/lib/aidp/watch/base_processor.rb +35 -0
  107. data/lib/aidp/watch/build_processor.rb +132 -0
  108. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  109. data/lib/aidp/watch/rebase_processor.rb +245 -0
  110. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  111. data/lib/aidp/watch/runner.rb +366 -285
  112. data/lib/aidp/watch/state_store.rb +80 -0
  113. data/lib/aidp/watch/work_item.rb +96 -0
  114. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  115. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  116. data/lib/aidp/workflows/guided_agent.rb +1 -1
  117. data/lib/aidp/worktree.rb +62 -0
  118. data/templates/aidp.yml.example +70 -0
  119. metadata +118 -16
  120. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  121. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  122. data/lib/aidp/providers/adapter.rb +0 -239
  123. data/lib/aidp/providers/aider.rb +0 -264
  124. data/lib/aidp/providers/anthropic.rb +0 -614
  125. data/lib/aidp/providers/base.rb +0 -617
  126. data/lib/aidp/providers/capability_registry.rb +0 -204
  127. data/lib/aidp/providers/codex.rb +0 -267
  128. data/lib/aidp/providers/cursor.rb +0 -304
  129. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  130. data/lib/aidp/providers/gemini.rb +0 -110
  131. data/lib/aidp/providers/github_copilot.rb +0 -227
  132. data/lib/aidp/providers/kilocode.rb +0 -157
  133. data/lib/aidp/providers/opencode.rb +0 -139
@@ -4,18 +4,82 @@ require "open3"
4
4
  require_relative "../tooling_detector"
5
5
  require_relative "output_filter"
6
6
  require_relative "output_filter_config"
7
+ require_relative "rspec_command_optimizer"
7
8
 
8
9
  module Aidp
9
10
  module Harness
10
11
  # Executes test and linter commands configured in aidp.yml
11
12
  # Returns results with exit status and output
12
13
  # Applies intelligent output filtering to reduce token consumption
14
+ #
15
+ # Supports both legacy category-specific commands (test_commands, lint_commands, etc.)
16
+ # and the new generic commands array with run_after phases.
13
17
  class TestRunner
14
18
  def initialize(project_dir, config)
15
19
  @project_dir = project_dir
16
20
  @config = config
17
21
  @iteration_count = 0
18
22
  @filter_stats = {total_input_bytes: 0, total_output_bytes: 0}
23
+ @previous_iteration_had_test_failures = false
24
+ @rspec_optimizer = RSpecCommandOptimizer.new(project_dir)
25
+ end
26
+
27
+ # Run all commands configured for a specific execution phase
28
+ # @param phase [Symbol] :each_unit, :full_loop, or :on_completion
29
+ # @return [Hash] Aggregated results with success status and outputs by command
30
+ def run_commands_for_phase(phase)
31
+ @iteration_count += 1
32
+ commands = @config.commands_for_phase(phase)
33
+
34
+ Aidp.log_debug("test_runner", "running_commands_for_phase",
35
+ phase: phase,
36
+ command_count: commands.length,
37
+ iteration: @iteration_count)
38
+
39
+ return empty_results if commands.empty?
40
+
41
+ results_by_command = {}
42
+ all_failures = []
43
+ required_failures = []
44
+
45
+ commands.each do |cmd_config|
46
+ result = execute_command(cmd_config[:command], cmd_config[:category].to_s)
47
+ result[:name] = cmd_config[:name]
48
+ result[:category] = cmd_config[:category]
49
+ result[:required] = cmd_config[:required]
50
+
51
+ results_by_command[cmd_config[:name]] = result
52
+
53
+ unless result[:success]
54
+ all_failures << result
55
+ required_failures << result if cmd_config[:required]
56
+ end
57
+ end
58
+
59
+ success = required_failures.empty?
60
+ output = if all_failures.empty?
61
+ "All #{commands.length} commands passed"
62
+ else
63
+ format_phase_failures(all_failures, phase)
64
+ end
65
+
66
+ {
67
+ success: success,
68
+ output: output,
69
+ failures: all_failures,
70
+ required_failures: required_failures,
71
+ results_by_command: results_by_command
72
+ }
73
+ end
74
+
75
+ # Get all commands organized by phase
76
+ # @return [Hash] Commands grouped by run_after phase
77
+ def commands_by_phase
78
+ {
79
+ each_unit: @config.commands_for_phase(:each_unit),
80
+ full_loop: @config.commands_for_phase(:full_loop),
81
+ on_completion: @config.commands_for_phase(:on_completion)
82
+ }
19
83
  end
20
84
 
21
85
  # Run all configured tests
@@ -68,13 +132,44 @@ module Aidp
68
132
  # Get filtering statistics
69
133
  attr_reader :filter_stats
70
134
 
135
+ # Get RSpec command optimizer (for inspection/testing)
136
+ attr_reader :rspec_optimizer
137
+
138
+ # Track if previous iteration had test failures (for --only-failures optimization)
139
+ attr_reader :previous_iteration_had_test_failures
140
+
71
141
  # Reset iteration counter (useful for testing)
72
142
  def reset_iteration_count
73
143
  @iteration_count = 0
144
+ @previous_iteration_had_test_failures = false
145
+ @rspec_optimizer.reset_caches!
74
146
  end
75
147
 
76
148
  private
77
149
 
150
+ # Return empty results structure
151
+ def empty_results
152
+ {success: true, output: "", failures: [], required_failures: [], results_by_command: {}}
153
+ end
154
+
155
+ # Format failures for phase-based execution
156
+ def format_phase_failures(failures, phase)
157
+ output = ["#{phase.to_s.tr("_", " ").capitalize} Phase Failures:", ""]
158
+
159
+ failures.each do |failure|
160
+ required_label = failure[:required] ? "[REQUIRED]" : "[OPTIONAL]"
161
+ output << "#{required_label} #{failure[:name]} (#{failure[:category]})"
162
+ output << "Command: #{failure[:command]}"
163
+ output << "Exit Code: #{failure[:exit_code]}"
164
+ output << "--- Output ---"
165
+ output << failure[:stdout] unless failure[:stdout].to_s.strip.empty?
166
+ output << failure[:stderr] unless failure[:stderr].to_s.strip.empty?
167
+ output << ""
168
+ end
169
+
170
+ output.join("\n")
171
+ end
172
+
78
173
  # Run commands for a specific category (test, lint, formatter, build, documentation)
79
174
  def run_command_category(category, display_name)
80
175
  commands = resolved_commands(category)
@@ -85,54 +180,115 @@ module Aidp
85
180
  # Determine output mode based on category
86
181
  mode = determine_output_mode(category)
87
182
 
183
+ # Enable RSpec optimization for test commands
184
+ optimize_rspec = (category == :test)
185
+
88
186
  Aidp.log_debug("test_runner", "running_category",
89
187
  category: category,
90
188
  command_count: commands.length,
91
189
  iteration: @iteration_count,
92
- output_mode: mode)
190
+ output_mode: mode,
191
+ optimize_rspec: optimize_rspec,
192
+ previous_had_failures: @previous_iteration_had_test_failures)
93
193
 
94
194
  # Execute all commands
95
195
  results = commands.map do |cmd_config|
96
196
  # Handle both string commands (legacy) and hash format (new)
97
197
  if cmd_config.is_a?(String)
98
- result = execute_command(cmd_config, category.to_s)
198
+ result = execute_command(cmd_config, category.to_s, optimize_rspec: optimize_rspec)
99
199
  result.merge(required: true)
100
200
  else
101
- result = execute_command(cmd_config[:command], category.to_s)
201
+ result = execute_command(cmd_config[:command], category.to_s, optimize_rspec: optimize_rspec)
102
202
  result.merge(required: cmd_config[:required])
103
203
  end
104
204
  end
105
205
 
106
- aggregate_results(results, display_name, mode: mode)
206
+ aggregated = aggregate_results(results, display_name, mode: mode)
207
+
208
+ # Track test failures for next iteration's --only-failures optimization
209
+ if category == :test
210
+ @previous_iteration_had_test_failures = !aggregated[:success]
211
+ @rspec_optimizer.reset_caches! if aggregated[:success]
212
+
213
+ Aidp.log_debug("test_runner", "test_failure_tracking",
214
+ iteration: @iteration_count,
215
+ had_failures: @previous_iteration_had_test_failures,
216
+ failure_count: aggregated[:failures]&.length || 0)
217
+ end
218
+
219
+ aggregated
107
220
  rescue NameError
108
221
  # Logging not available
109
222
  commands = resolved_commands(category)
110
223
  return {success: true, output: "", failures: [], required_failures: []} if commands.empty?
111
224
 
225
+ optimize_rspec = (category == :test)
112
226
  mode = determine_output_mode(category)
113
227
  results = commands.map do |cmd_config|
114
228
  if cmd_config.is_a?(String)
115
- result = execute_command(cmd_config, category.to_s)
229
+ result = execute_command(cmd_config, category.to_s, optimize_rspec: optimize_rspec)
116
230
  result.merge(required: true)
117
231
  else
118
- result = execute_command(cmd_config[:command], category.to_s)
232
+ result = execute_command(cmd_config[:command], category.to_s, optimize_rspec: optimize_rspec)
119
233
  result.merge(required: cmd_config[:required])
120
234
  end
121
235
  end
122
236
 
123
- aggregate_results(results, display_name, mode: mode)
237
+ aggregated = aggregate_results(results, display_name, mode: mode)
238
+
239
+ # Track test failures for next iteration (without logging)
240
+ if category == :test
241
+ @previous_iteration_had_test_failures = !aggregated[:success]
242
+ @rspec_optimizer.reset_caches! if aggregated[:success]
243
+ end
244
+
245
+ aggregated
124
246
  end
125
247
 
126
- def execute_command(command, type)
127
- stdout, stderr, status = Open3.capture3(command, chdir: @project_dir)
248
+ def execute_command(command, type, optimize_rspec: false)
249
+ actual_command = command
250
+ optimization_info = nil
251
+
252
+ # Apply RSpec --only-failures optimization for test commands on subsequent iterations
253
+ if optimize_rspec && type == "test"
254
+ optimization_info = @rspec_optimizer.optimize_command(
255
+ command,
256
+ iteration: @iteration_count,
257
+ had_failures: @previous_iteration_had_test_failures
258
+ )
259
+ actual_command = optimization_info[:command]
260
+
261
+ if optimization_info[:optimized]
262
+ Aidp.log_info("test_runner", "rspec_optimized",
263
+ original: command,
264
+ optimized: actual_command,
265
+ iteration: @iteration_count,
266
+ reason: optimization_info[:reason])
267
+ end
268
+ end
269
+
270
+ stdout, stderr, status = Open3.capture3(actual_command, chdir: @project_dir)
271
+
272
+ # Handle special case: --only-failures with no failures to rerun
273
+ # RSpec exits with 0 and says "All examples were filtered out"
274
+ if optimization_info&.dig(:optimized) && status.success? && stdout.include?("All examples were filtered out")
275
+ Aidp.log_info("test_runner", "rspec_only_failures_empty",
276
+ message: "No failures to rerun, running full suite")
277
+
278
+ # Fall back to full RSpec run
279
+ stdout, stderr, status = Open3.capture3(command, chdir: @project_dir)
280
+ actual_command = command
281
+ end
128
282
 
129
283
  {
130
- command: command,
284
+ command: actual_command,
285
+ original_command: command,
131
286
  type: type,
132
287
  success: status.success?,
133
288
  exit_code: status.exitstatus,
134
289
  stdout: stdout,
135
- stderr: stderr
290
+ stderr: stderr,
291
+ rspec_optimized: optimization_info&.dig(:optimized) || false
136
292
  }
137
293
  end
138
294