aidp 0.23.0 → 0.25.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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +27 -1
  3. data/lib/aidp/auto_update/bundler_adapter.rb +66 -0
  4. data/lib/aidp/auto_update/checkpoint.rb +178 -0
  5. data/lib/aidp/auto_update/checkpoint_store.rb +182 -0
  6. data/lib/aidp/auto_update/coordinator.rb +204 -0
  7. data/lib/aidp/auto_update/errors.rb +17 -0
  8. data/lib/aidp/auto_update/failure_tracker.rb +162 -0
  9. data/lib/aidp/auto_update/rubygems_api_adapter.rb +95 -0
  10. data/lib/aidp/auto_update/update_check.rb +106 -0
  11. data/lib/aidp/auto_update/update_logger.rb +143 -0
  12. data/lib/aidp/auto_update/update_policy.rb +109 -0
  13. data/lib/aidp/auto_update/version_detector.rb +144 -0
  14. data/lib/aidp/auto_update.rb +52 -0
  15. data/lib/aidp/cli.rb +168 -1
  16. data/lib/aidp/execute/work_loop_runner.rb +252 -45
  17. data/lib/aidp/execute/work_loop_unit_scheduler.rb +27 -2
  18. data/lib/aidp/harness/condition_detector.rb +42 -8
  19. data/lib/aidp/harness/config_manager.rb +7 -0
  20. data/lib/aidp/harness/config_schema.rb +75 -0
  21. data/lib/aidp/harness/configuration.rb +69 -6
  22. data/lib/aidp/harness/error_handler.rb +117 -44
  23. data/lib/aidp/harness/provider_factory.rb +2 -0
  24. data/lib/aidp/harness/provider_manager.rb +64 -0
  25. data/lib/aidp/harness/provider_metrics.rb +138 -0
  26. data/lib/aidp/harness/runner.rb +90 -29
  27. data/lib/aidp/harness/simple_user_interface.rb +4 -0
  28. data/lib/aidp/harness/state/ui_state.rb +0 -10
  29. data/lib/aidp/harness/state_manager.rb +1 -15
  30. data/lib/aidp/harness/test_runner.rb +39 -2
  31. data/lib/aidp/logger.rb +34 -4
  32. data/lib/aidp/message_display.rb +10 -2
  33. data/lib/aidp/prompt_optimization/style_guide_indexer.rb +3 -1
  34. data/lib/aidp/provider_manager.rb +2 -0
  35. data/lib/aidp/providers/adapter.rb +241 -0
  36. data/lib/aidp/providers/anthropic.rb +75 -7
  37. data/lib/aidp/providers/base.rb +29 -1
  38. data/lib/aidp/providers/capability_registry.rb +205 -0
  39. data/lib/aidp/providers/codex.rb +14 -0
  40. data/lib/aidp/providers/error_taxonomy.rb +195 -0
  41. data/lib/aidp/providers/gemini.rb +3 -2
  42. data/lib/aidp/providers/kilocode.rb +202 -0
  43. data/lib/aidp/setup/provider_registry.rb +122 -0
  44. data/lib/aidp/setup/wizard.rb +125 -33
  45. data/lib/aidp/skills/composer.rb +4 -0
  46. data/lib/aidp/skills/loader.rb +3 -1
  47. data/lib/aidp/version.rb +1 -1
  48. data/lib/aidp/watch/build_processor.rb +323 -33
  49. data/lib/aidp/watch/ci_fix_processor.rb +448 -0
  50. data/lib/aidp/watch/plan_processor.rb +12 -2
  51. data/lib/aidp/watch/repository_client.rb +384 -4
  52. data/lib/aidp/watch/review_processor.rb +266 -0
  53. data/lib/aidp/watch/reviewers/base_reviewer.rb +164 -0
  54. data/lib/aidp/watch/reviewers/performance_reviewer.rb +65 -0
  55. data/lib/aidp/watch/reviewers/security_reviewer.rb +65 -0
  56. data/lib/aidp/watch/reviewers/senior_dev_reviewer.rb +33 -0
  57. data/lib/aidp/watch/runner.rb +222 -5
  58. data/lib/aidp/watch/state_store.rb +53 -0
  59. data/lib/aidp/workflows/guided_agent.rb +53 -0
  60. data/lib/aidp/worktree.rb +67 -10
  61. data/lib/aidp.rb +1 -0
  62. data/templates/work_loop/decide_whats_next.md +21 -0
  63. data/templates/work_loop/diagnose_failures.md +21 -0
  64. metadata +29 -3
  65. /data/{bin → exe}/aidp +0 -0
@@ -53,6 +53,49 @@ module Aidp
53
53
  save!
54
54
  end
55
55
 
56
+ # Review tracking methods
57
+ def review_processed?(pr_number)
58
+ reviews.key?(pr_number.to_s)
59
+ end
60
+
61
+ def review_data(pr_number)
62
+ reviews[pr_number.to_s]
63
+ end
64
+
65
+ def record_review(pr_number, data)
66
+ payload = {
67
+ "timestamp" => data[:timestamp] || Time.now.utc.iso8601,
68
+ "reviewers" => data[:reviewers],
69
+ "total_findings" => data[:total_findings]
70
+ }.compact
71
+
72
+ reviews[pr_number.to_s] = payload
73
+ save!
74
+ end
75
+
76
+ # CI fix tracking methods
77
+ def ci_fix_completed?(pr_number)
78
+ fix_data = ci_fixes[pr_number.to_s]
79
+ fix_data && fix_data["status"] == "completed"
80
+ end
81
+
82
+ def ci_fix_data(pr_number)
83
+ ci_fixes[pr_number.to_s]
84
+ end
85
+
86
+ def record_ci_fix(pr_number, data)
87
+ payload = {
88
+ "status" => data[:status],
89
+ "timestamp" => data[:timestamp] || Time.now.utc.iso8601,
90
+ "reason" => data[:reason],
91
+ "root_causes" => data[:root_causes],
92
+ "fixes_count" => data[:fixes_count]
93
+ }.compact
94
+
95
+ ci_fixes[pr_number.to_s] = payload
96
+ save!
97
+ end
98
+
56
99
  private
57
100
 
58
101
  def ensure_directory
@@ -81,6 +124,8 @@ module Aidp
81
124
  base = load_state
82
125
  base["plans"] ||= {}
83
126
  base["builds"] ||= {}
127
+ base["reviews"] ||= {}
128
+ base["ci_fixes"] ||= {}
84
129
  base
85
130
  end
86
131
  end
@@ -93,6 +138,14 @@ module Aidp
93
138
  state["builds"]
94
139
  end
95
140
 
141
+ def reviews
142
+ state["reviews"]
143
+ end
144
+
145
+ def ci_fixes
146
+ state["ci_fixes"]
147
+ end
148
+
96
149
  def stringify_keys(hash)
97
150
  return {} unless hash
98
151
 
@@ -229,16 +229,34 @@ module Aidp
229
229
  end
230
230
 
231
231
  combined_prompt = "#{system_prompt}\n\n#{user_prompt}"
232
+
233
+ # Record timing for metrics
234
+ start_time = Time.now
232
235
  result = provider.send_message(prompt: combined_prompt)
236
+ duration = Time.now - start_time
233
237
 
234
238
  if result.nil? || result.empty?
239
+ # Record failed metric
240
+ @provider_manager.record_metrics(provider_name, success: false, duration: duration, error: StandardError.new("Empty response")) if @provider_manager.respond_to?(:record_metrics)
235
241
  raise ConversationError, "Provider request failed: empty response"
236
242
  end
237
243
 
244
+ # Record successful metric
245
+ @provider_manager.record_metrics(provider_name, success: true, duration: duration) if @provider_manager.respond_to?(:record_metrics)
246
+
238
247
  result
239
248
  rescue => e
240
249
  message = e.message.to_s
241
250
 
251
+ # Record failed metric (if we haven't already from empty response check)
252
+ if defined?(start_time) && defined?(provider_name) && defined?(duration).nil?
253
+ duration = Time.now - start_time
254
+ @provider_manager.record_metrics(provider_name, success: false, duration: duration, error: e) if @provider_manager.respond_to?(:record_metrics)
255
+ end
256
+
257
+ # Extract rate limit reset time from error message (especially Claude)
258
+ extract_and_save_rate_limit_info(provider_name, message) if defined?(provider_name)
259
+
242
260
  # Classify error type for better handling
243
261
  classified = if message =~ /resource[_ ]exhausted/i || message =~ /\[resource_exhausted\]/i
244
262
  "resource_exhausted"
@@ -319,6 +337,41 @@ module Aidp
319
337
  Aidp.logger.warn("guided_agent", "Failed verbose iteration emit", error: e.message)
320
338
  end
321
339
 
340
+ # Extract rate limit reset time from error message and save to provider manager
341
+ def extract_and_save_rate_limit_info(provider_name, error_message)
342
+ return unless @provider_manager.respond_to?(:mark_rate_limited)
343
+ return unless error_message
344
+
345
+ # Claude error format: "please try again in 1m23s" or "retry after X seconds/minutes"
346
+ reset_time = nil
347
+
348
+ if error_message =~ /try again in (\d+)m(\d+)s/i
349
+ minutes = $1.to_i
350
+ seconds = $2.to_i
351
+ reset_time = Time.now + (minutes * 60) + seconds
352
+ elsif error_message =~ /try again in (\d+)s/i
353
+ seconds = $1.to_i
354
+ reset_time = Time.now + seconds
355
+ elsif error_message =~ /try again in (\d+)m/i
356
+ minutes = $1.to_i
357
+ reset_time = Time.now + (minutes * 60)
358
+ elsif error_message =~ /retry after (\d+) seconds?/i
359
+ seconds = $1.to_i
360
+ reset_time = Time.now + seconds
361
+ elsif error_message =~ /retry after (\d+) minutes?/i
362
+ minutes = $1.to_i
363
+ reset_time = Time.now + (minutes * 60)
364
+ end
365
+
366
+ # Mark provider as rate limited with extracted reset time
367
+ if reset_time
368
+ @provider_manager.mark_rate_limited(provider_name, reset_time)
369
+ Aidp.logger.info("guided_agent", "Extracted rate limit reset time", provider: provider_name, reset_at: reset_time.iso8601)
370
+ end
371
+ rescue => e
372
+ Aidp.logger.warn("guided_agent", "Failed to extract rate limit info", error: e.message)
373
+ end
374
+
322
375
  def validate_provider_configuration!
323
376
  configured = @provider_manager.configured_providers
324
377
  if configured.nil? || configured.empty?
data/lib/aidp/worktree.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "fileutils"
4
4
  require "json"
5
+ require "open3"
5
6
  require_relative "workstream_state"
6
7
 
7
8
  module Aidp
@@ -32,16 +33,14 @@ module Aidp
32
33
  raise WorktreeExists, "Worktree already exists at #{worktree_path}"
33
34
  end
34
35
 
35
- # Create the worktree
36
- cmd = ["git", "worktree", "add", "-b", branch, worktree_path]
37
- cmd << base_branch if base_branch
38
-
39
- Dir.chdir(project_dir) do
40
- success = system(*cmd, out: File::NULL, err: File::NULL)
41
- unless success
42
- raise Error, "Failed to create worktree: #{$?.exitstatus}"
43
- end
44
- end
36
+ branch_exists = branch_exists?(project_dir, branch)
37
+ run_worktree_add!(
38
+ project_dir: project_dir,
39
+ branch: branch,
40
+ branch_exists: branch_exists,
41
+ worktree_path: worktree_path,
42
+ base_branch: base_branch
43
+ )
45
44
 
46
45
  # Initialize .aidp directory in the worktree
47
46
  ensure_aidp_dir(worktree_path)
@@ -203,6 +202,64 @@ module Aidp
203
202
  def registry_file_path(project_dir)
204
203
  File.join(project_dir, ".aidp", "worktrees.json")
205
204
  end
205
+
206
+ def branch_exists?(project_dir, branch)
207
+ Dir.chdir(project_dir) do
208
+ system("git", "show-ref", "--verify", "--quiet", "refs/heads/#{branch}")
209
+ end
210
+ end
211
+
212
+ def run_worktree_add!(project_dir:, branch:, branch_exists:, worktree_path:, base_branch:)
213
+ prune_attempted = false
214
+
215
+ loop do
216
+ cmd = build_worktree_command(branch_exists: branch_exists, branch: branch, worktree_path: worktree_path, base_branch: base_branch)
217
+ stdout, stderr, status = Dir.chdir(project_dir) { Open3.capture3(*cmd) }
218
+
219
+ return if status.success?
220
+
221
+ error_output = stderr.strip.empty? ? stdout.strip : stderr.strip
222
+
223
+ if !branch_exists && branch_already_exists?(error_output, branch)
224
+ Aidp.log_debug("worktree", "branch_exists_retry", branch: branch)
225
+ branch_exists = true
226
+ next
227
+ end
228
+
229
+ if !prune_attempted && missing_registered_worktree?(error_output)
230
+ Aidp.log_debug("worktree", "prune_missing_worktree", branch: branch, path: worktree_path)
231
+ Dir.chdir(project_dir) { Open3.capture3("git", "worktree", "prune") }
232
+ prune_attempted = true
233
+ next
234
+ end
235
+
236
+ raise Error, "Failed to create worktree (status=#{status.exitstatus}): #{error_output}"
237
+ end
238
+ end
239
+
240
+ def build_worktree_command(branch_exists:, branch:, worktree_path:, base_branch:)
241
+ if branch_exists
242
+ ["git", "worktree", "add", worktree_path, branch]
243
+ else
244
+ cmd = ["git", "worktree", "add", "-b", branch, worktree_path]
245
+ cmd << base_branch if base_branch
246
+ cmd
247
+ end
248
+ end
249
+
250
+ def branch_already_exists?(error_output, branch)
251
+ return false if error_output.nil? || error_output.empty?
252
+
253
+ normalized = error_output.downcase
254
+ normalized.include?("branch '#{branch.downcase}' already exists") ||
255
+ normalized.include?("a branch named '#{branch.downcase}' already exists")
256
+ end
257
+
258
+ def missing_registered_worktree?(error_output)
259
+ return false if error_output.nil? || error_output.empty?
260
+
261
+ error_output.downcase.include?("missing but already registered worktree")
262
+ end
206
263
  end
207
264
  end
208
265
  end
data/lib/aidp.rb CHANGED
@@ -26,6 +26,7 @@ require_relative "aidp/providers/base"
26
26
  require_relative "aidp/providers/cursor"
27
27
  require_relative "aidp/providers/anthropic"
28
28
  require_relative "aidp/providers/gemini"
29
+ require_relative "aidp/providers/kilocode"
29
30
  # Supervised providers removed - using direct execution model
30
31
  require_relative "aidp/provider_manager"
31
32
 
@@ -0,0 +1,21 @@
1
+ # Decide the Next Work Loop Unit
2
+
3
+ You are operating inside the Aidp hybrid work loop. Review the recent deterministic outputs, consider the latest agent summary, and choose the next unit by emitting `NEXT_UNIT: <unit_name>` on its own line.
4
+
5
+ ## Deterministic Outputs
6
+
7
+ {{DETERMINISTIC_OUTPUTS}}
8
+
9
+ ## Previous Agent Summary
10
+
11
+ {{PREVIOUS_AGENT_SUMMARY}}
12
+
13
+ ## Guidance
14
+
15
+ - Pick whichever unit will unblock progress the fastest. Options include deterministic unit names (`run_full_tests`, `run_lint`, etc.), `agentic` to resume coding, or `wait_for_github` if the system must await an external event.
16
+ - Cite concrete evidence from the outputs above when selecting a unit so future readers understand the decision.
17
+ - Keep the rationale tight—two or three sentences max.
18
+
19
+ ## Rationale
20
+
21
+ Provide the reasoning for your `NEXT_UNIT` decision here.
@@ -0,0 +1,21 @@
1
+ # Diagnose Recent Failures
2
+
3
+ You are operating inside the Aidp work loop. Analyze the most recent deterministic failures and decide how to unblock progress. Finish by emitting `NEXT_UNIT: <unit_name>` on its own line.
4
+
5
+ ## Recent Deterministic Outputs
6
+
7
+ {{DETERMINISTIC_OUTPUTS}}
8
+
9
+ ## Previous Agent Summary
10
+
11
+ {{PREVIOUS_AGENT_SUMMARY}}
12
+
13
+ ## Instructions
14
+
15
+ - Identify the concrete failures and hypothesize root causes.
16
+ - Propose the next best action (run another deterministic unit, resume agentic editing, or wait for GitHub updates).
17
+ - Keep the analysis crisp—focus on evidence and required follow-up work.
18
+
19
+ ## Analysis
20
+
21
+ Summarize key findings here, then state your `NEXT_UNIT` decision.
metadata CHANGED
@@ -1,11 +1,11 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aidp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.23.0
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
8
- bindir: bin
8
+ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
@@ -231,7 +231,7 @@ extra_rdoc_files: []
231
231
  files:
232
232
  - LICENSE
233
233
  - README.md
234
- - bin/aidp
234
+ - exe/aidp
235
235
  - lib/aidp.rb
236
236
  - lib/aidp/analyze/error_handler.rb
237
237
  - lib/aidp/analyze/feature_analyzer.rb
@@ -244,6 +244,18 @@ files:
244
244
  - lib/aidp/analyze/steps.rb
245
245
  - lib/aidp/analyze/tree_sitter_grammar_loader.rb
246
246
  - lib/aidp/analyze/tree_sitter_scan.rb
247
+ - lib/aidp/auto_update.rb
248
+ - lib/aidp/auto_update/bundler_adapter.rb
249
+ - lib/aidp/auto_update/checkpoint.rb
250
+ - lib/aidp/auto_update/checkpoint_store.rb
251
+ - lib/aidp/auto_update/coordinator.rb
252
+ - lib/aidp/auto_update/errors.rb
253
+ - lib/aidp/auto_update/failure_tracker.rb
254
+ - lib/aidp/auto_update/rubygems_api_adapter.rb
255
+ - lib/aidp/auto_update/update_check.rb
256
+ - lib/aidp/auto_update/update_logger.rb
257
+ - lib/aidp/auto_update/update_policy.rb
258
+ - lib/aidp/auto_update/version_detector.rb
247
259
  - lib/aidp/cli.rb
248
260
  - lib/aidp/cli/devcontainer_commands.rb
249
261
  - lib/aidp/cli/enhanced_input.rb
@@ -297,6 +309,7 @@ files:
297
309
  - lib/aidp/harness/provider_factory.rb
298
310
  - lib/aidp/harness/provider_info.rb
299
311
  - lib/aidp/harness/provider_manager.rb
312
+ - lib/aidp/harness/provider_metrics.rb
300
313
  - lib/aidp/harness/provider_type_checker.rb
301
314
  - lib/aidp/harness/runner.rb
302
315
  - lib/aidp/harness/simple_user_interface.rb
@@ -347,12 +360,16 @@ files:
347
360
  - lib/aidp/prompt_optimization/style_guide_indexer.rb
348
361
  - lib/aidp/prompt_optimization/template_indexer.rb
349
362
  - lib/aidp/provider_manager.rb
363
+ - lib/aidp/providers/adapter.rb
350
364
  - lib/aidp/providers/anthropic.rb
351
365
  - lib/aidp/providers/base.rb
366
+ - lib/aidp/providers/capability_registry.rb
352
367
  - lib/aidp/providers/codex.rb
353
368
  - lib/aidp/providers/cursor.rb
369
+ - lib/aidp/providers/error_taxonomy.rb
354
370
  - lib/aidp/providers/gemini.rb
355
371
  - lib/aidp/providers/github_copilot.rb
372
+ - lib/aidp/providers/kilocode.rb
356
373
  - lib/aidp/providers/opencode.rb
357
374
  - lib/aidp/rescue_logging.rb
358
375
  - lib/aidp/safe_directory.rb
@@ -360,6 +377,7 @@ files:
360
377
  - lib/aidp/setup/devcontainer/generator.rb
361
378
  - lib/aidp/setup/devcontainer/parser.rb
362
379
  - lib/aidp/setup/devcontainer/port_manager.rb
380
+ - lib/aidp/setup/provider_registry.rb
363
381
  - lib/aidp/setup/wizard.rb
364
382
  - lib/aidp/skills.rb
365
383
  - lib/aidp/skills/composer.rb
@@ -382,10 +400,16 @@ files:
382
400
  - lib/aidp/version.rb
383
401
  - lib/aidp/watch.rb
384
402
  - lib/aidp/watch/build_processor.rb
403
+ - lib/aidp/watch/ci_fix_processor.rb
385
404
  - lib/aidp/watch/plan_generator.rb
386
405
  - lib/aidp/watch/plan_processor.rb
387
406
  - lib/aidp/watch/repository_client.rb
388
407
  - lib/aidp/watch/repository_safety_checker.rb
408
+ - lib/aidp/watch/review_processor.rb
409
+ - lib/aidp/watch/reviewers/base_reviewer.rb
410
+ - lib/aidp/watch/reviewers/performance_reviewer.rb
411
+ - lib/aidp/watch/reviewers/security_reviewer.rb
412
+ - lib/aidp/watch/reviewers/senior_dev_reviewer.rb
389
413
  - lib/aidp/watch/runner.rb
390
414
  - lib/aidp/watch/state_store.rb
391
415
  - lib/aidp/workflows/definitions.rb
@@ -438,6 +462,8 @@ files:
438
462
  - templates/skills/product_strategist/SKILL.md
439
463
  - templates/skills/repository_analyst/SKILL.md
440
464
  - templates/skills/test_analyzer/SKILL.md
465
+ - templates/work_loop/decide_whats_next.md
466
+ - templates/work_loop/diagnose_failures.md
441
467
  homepage: https://github.com/viamin/aidp
442
468
  licenses:
443
469
  - MIT
/data/{bin → exe}/aidp RENAMED
File without changes