aidp 0.22.0 → 0.24.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +145 -31
  3. data/lib/aidp/cli.rb +19 -2
  4. data/lib/aidp/execute/work_loop_runner.rb +252 -45
  5. data/lib/aidp/execute/work_loop_unit_scheduler.rb +27 -2
  6. data/lib/aidp/harness/condition_detector.rb +42 -8
  7. data/lib/aidp/harness/config_manager.rb +7 -0
  8. data/lib/aidp/harness/config_schema.rb +25 -0
  9. data/lib/aidp/harness/configuration.rb +69 -6
  10. data/lib/aidp/harness/error_handler.rb +117 -44
  11. data/lib/aidp/harness/provider_manager.rb +64 -0
  12. data/lib/aidp/harness/provider_metrics.rb +138 -0
  13. data/lib/aidp/harness/runner.rb +110 -35
  14. data/lib/aidp/harness/simple_user_interface.rb +4 -0
  15. data/lib/aidp/harness/state/ui_state.rb +0 -10
  16. data/lib/aidp/harness/state_manager.rb +1 -15
  17. data/lib/aidp/harness/test_runner.rb +39 -2
  18. data/lib/aidp/logger.rb +34 -4
  19. data/lib/aidp/providers/adapter.rb +241 -0
  20. data/lib/aidp/providers/anthropic.rb +75 -7
  21. data/lib/aidp/providers/base.rb +29 -1
  22. data/lib/aidp/providers/capability_registry.rb +205 -0
  23. data/lib/aidp/providers/codex.rb +14 -0
  24. data/lib/aidp/providers/error_taxonomy.rb +195 -0
  25. data/lib/aidp/providers/gemini.rb +3 -2
  26. data/lib/aidp/setup/devcontainer/backup_manager.rb +11 -4
  27. data/lib/aidp/setup/provider_registry.rb +107 -0
  28. data/lib/aidp/setup/wizard.rb +189 -31
  29. data/lib/aidp/version.rb +1 -1
  30. data/lib/aidp/watch/build_processor.rb +357 -27
  31. data/lib/aidp/watch/plan_generator.rb +16 -1
  32. data/lib/aidp/watch/plan_processor.rb +54 -3
  33. data/lib/aidp/watch/repository_client.rb +78 -4
  34. data/lib/aidp/watch/repository_safety_checker.rb +12 -3
  35. data/lib/aidp/watch/runner.rb +52 -10
  36. data/lib/aidp/workflows/guided_agent.rb +53 -0
  37. data/lib/aidp/worktree.rb +67 -10
  38. data/templates/work_loop/decide_whats_next.md +21 -0
  39. data/templates/work_loop/diagnose_failures.md +21 -0
  40. metadata +10 -3
  41. /data/{bin → exe}/aidp +0 -0
@@ -61,8 +61,22 @@ module Aidp
61
61
  gh_available? ? post_comment_via_gh(number, body) : post_comment_via_api(number, body)
62
62
  end
63
63
 
64
- def create_pull_request(title:, body:, head:, base:, issue_number:)
65
- gh_available? ? create_pull_request_via_gh(title: title, body: body, head: head, base: base, issue_number: issue_number) : raise("GitHub CLI not available - cannot create PR")
64
+ def create_pull_request(title:, body:, head:, base:, issue_number:, draft: false, assignee: nil)
65
+ gh_available? ? create_pull_request_via_gh(title: title, body: body, head: head, base: base, issue_number: issue_number, draft: draft, assignee: assignee) : raise("GitHub CLI not available - cannot create PR")
66
+ end
67
+
68
+ def add_labels(number, *labels)
69
+ gh_available? ? add_labels_via_gh(number, labels.flatten) : add_labels_via_api(number, labels.flatten)
70
+ end
71
+
72
+ def remove_labels(number, *labels)
73
+ gh_available? ? remove_labels_via_gh(number, labels.flatten) : remove_labels_via_api(number, labels.flatten)
74
+ end
75
+
76
+ def replace_labels(number, old_labels:, new_labels:)
77
+ # Remove old labels and add new ones atomically where possible
78
+ remove_labels(number, *old_labels) unless old_labels.empty?
79
+ add_labels(number, *new_labels) unless new_labels.empty?
66
80
  end
67
81
 
68
82
  private
@@ -162,7 +176,7 @@ module Aidp
162
176
  response.body
163
177
  end
164
178
 
165
- def create_pull_request_via_gh(title:, body:, head:, base:, issue_number:, draft: false)
179
+ def create_pull_request_via_gh(title:, body:, head:, base:, issue_number:, draft: false, assignee: nil)
166
180
  cmd = [
167
181
  "gh", "pr", "create",
168
182
  "--repo", full_repo,
@@ -171,8 +185,8 @@ module Aidp
171
185
  "--head", head,
172
186
  "--base", base
173
187
  ]
174
- cmd += ["--issue", issue_number.to_s] if issue_number
175
188
  cmd += ["--draft"] if draft
189
+ cmd += ["--assignee", assignee] if assignee
176
190
 
177
191
  stdout, stderr, status = Open3.capture3(*cmd)
178
192
  raise "Failed to create PR via gh: #{stderr.strip}" unless status.success?
@@ -180,6 +194,66 @@ module Aidp
180
194
  stdout.strip
181
195
  end
182
196
 
197
+ def add_labels_via_gh(number, labels)
198
+ return if labels.empty?
199
+
200
+ cmd = ["gh", "issue", "edit", number.to_s, "--repo", full_repo]
201
+ labels.each { |label| cmd += ["--add-label", label] }
202
+
203
+ stdout, stderr, status = Open3.capture3(*cmd)
204
+ raise "Failed to add labels via gh: #{stderr.strip}" unless status.success?
205
+
206
+ stdout.strip
207
+ end
208
+
209
+ def add_labels_via_api(number, labels)
210
+ return if labels.empty?
211
+
212
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/labels")
213
+ request = Net::HTTP::Post.new(uri)
214
+ request["Content-Type"] = "application/json"
215
+ request.body = JSON.dump({labels: labels})
216
+
217
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
218
+ http.request(request)
219
+ end
220
+
221
+ raise "Failed to add labels via API (#{response.code})" unless response.code.start_with?("2")
222
+ response.body
223
+ end
224
+
225
+ def remove_labels_via_gh(number, labels)
226
+ return if labels.empty?
227
+
228
+ cmd = ["gh", "issue", "edit", number.to_s, "--repo", full_repo]
229
+ labels.each { |label| cmd += ["--remove-label", label] }
230
+
231
+ stdout, stderr, status = Open3.capture3(*cmd)
232
+ raise "Failed to remove labels via gh: #{stderr.strip}" unless status.success?
233
+
234
+ stdout.strip
235
+ end
236
+
237
+ def remove_labels_via_api(number, labels)
238
+ return if labels.empty?
239
+
240
+ labels.each do |label|
241
+ # URL encode the label name
242
+ encoded_label = URI.encode_www_form_component(label)
243
+ uri = URI("https://api.github.com/repos/#{full_repo}/issues/#{number}/labels/#{encoded_label}")
244
+ request = Net::HTTP::Delete.new(uri)
245
+
246
+ response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
247
+ http.request(request)
248
+ end
249
+
250
+ # 404 is OK - label didn't exist
251
+ unless response.code.start_with?("2") || response.code == "404"
252
+ raise "Failed to remove label '#{label}' via API (#{response.code})"
253
+ end
254
+ end
255
+ end
256
+
183
257
  def normalize_issue(raw)
184
258
  {
185
259
  number: raw["number"],
@@ -163,16 +163,25 @@ module Aidp
163
163
  end
164
164
 
165
165
  def public_repos_allowed?
166
- @config.dig(:safety, :allow_public_repos) == true
166
+ # Support both string and symbol keys
167
+ safety_config = @config[:safety] || @config["safety"] || {}
168
+ (safety_config[:allow_public_repos] || safety_config["allow_public_repos"]) == true
167
169
  end
168
170
 
169
171
  def author_allowlist
170
- @author_allowlist ||= Array(@config.dig(:safety, :author_allowlist)).compact.map(&:to_s)
172
+ # Support both string and symbol keys
173
+ safety_config = @config[:safety] || @config["safety"] || {}
174
+ @author_allowlist ||= Array(
175
+ safety_config[:author_allowlist] || safety_config["author_allowlist"]
176
+ ).compact.map(&:to_s)
171
177
  end
172
178
 
173
179
  def safe_environment?
174
180
  # Check if running in a container
175
- in_container? || @config.dig(:safety, :require_container) == false
181
+ # Support both string and symbol keys
182
+ safety_config = @config[:safety] || @config["safety"] || {}
183
+ require_container = safety_config[:require_container] || safety_config["require_container"]
184
+ in_container? || require_container == false
176
185
  end
177
186
 
178
187
  def in_container?
@@ -19,12 +19,13 @@ module Aidp
19
19
 
20
20
  DEFAULT_INTERVAL = 30
21
21
 
22
- 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)
22
+ 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)
23
23
  @prompt = prompt
24
24
  @interval = interval
25
25
  @once = once
26
26
  @project_dir = project_dir
27
27
  @force = force
28
+ @verbose = verbose
28
29
 
29
30
  owner, repo = RepositoryClient.parse_issues_url(issues_url)
30
31
  @repository_client = RepositoryClient.new(owner: owner, repo: repo, gh_available: gh_available)
@@ -33,16 +34,23 @@ module Aidp
33
34
  config: safety_config
34
35
  )
35
36
  @state_store = StateStore.new(project_dir: project_dir, repository: "#{owner}/#{repo}")
37
+
38
+ # Extract label configuration from safety_config (it's actually the full watch config)
39
+ label_config = safety_config[:labels] || safety_config["labels"] || {}
40
+
36
41
  @plan_processor = PlanProcessor.new(
37
42
  repository_client: @repository_client,
38
43
  state_store: @state_store,
39
- plan_generator: PlanGenerator.new(provider_name: provider_name)
44
+ plan_generator: PlanGenerator.new(provider_name: provider_name, verbose: verbose),
45
+ label_config: label_config
40
46
  )
41
47
  @build_processor = BuildProcessor.new(
42
48
  repository_client: @repository_client,
43
49
  state_store: @state_store,
44
50
  project_dir: project_dir,
45
- use_workstreams: use_workstreams
51
+ use_workstreams: use_workstreams,
52
+ verbose: verbose,
53
+ label_config: label_config
46
54
  )
47
55
  end
48
56
 
@@ -50,12 +58,25 @@ module Aidp
50
58
  # Validate safety requirements before starting
51
59
  @safety_checker.validate_watch_mode_safety!(force: @force)
52
60
 
61
+ Aidp.log_info(
62
+ "watch_runner",
63
+ "watch_mode_started",
64
+ repo: @repository_client.full_repo,
65
+ interval: @interval,
66
+ once: @once,
67
+ use_workstreams: @use_workstreams,
68
+ verbose: @verbose
69
+ )
70
+
53
71
  display_message("👀 Watch mode enabled for #{@repository_client.full_repo}", type: :highlight)
54
72
  display_message("Polling every #{@interval} seconds. Press Ctrl+C to stop.", type: :muted)
55
73
 
56
74
  loop do
75
+ Aidp.log_debug("watch_runner", "poll_cycle.begin", repo: @repository_client.full_repo, interval: @interval)
57
76
  process_cycle
77
+ Aidp.log_debug("watch_runner", "poll_cycle.complete", once: @once, next_poll_in: @once ? nil : @interval)
58
78
  break if @once
79
+ Aidp.log_debug("watch_runner", "poll_cycle.sleep", seconds: @interval)
59
80
  sleep @interval
60
81
  end
61
82
  rescue Interrupt
@@ -73,15 +94,24 @@ module Aidp
73
94
  end
74
95
 
75
96
  def process_plan_triggers
76
- issues = @repository_client.list_issues(labels: [PlanProcessor::PLAN_LABEL], state: "open")
97
+ plan_label = @plan_processor.plan_label
98
+ issues = @repository_client.list_issues(labels: [plan_label], state: "open")
99
+ Aidp.log_debug("watch_runner", "plan_poll", label: plan_label, total: issues.size)
77
100
  issues.each do |issue|
78
- next unless issue_has_label?(issue, PlanProcessor::PLAN_LABEL)
101
+ unless issue_has_label?(issue, plan_label)
102
+ Aidp.log_debug("watch_runner", "plan_skip_label_mismatch", issue: issue[:number], labels: issue[:labels])
103
+ next
104
+ end
79
105
 
80
106
  detailed = @repository_client.fetch_issue(issue[:number])
81
107
 
82
108
  # Check author authorization before processing
83
- next unless @safety_checker.should_process_issue?(detailed, enforce: false)
109
+ unless @safety_checker.should_process_issue?(detailed, enforce: false)
110
+ Aidp.log_debug("watch_runner", "plan_skip_unauthorized_author", issue: detailed[:number], author: detailed[:author])
111
+ next
112
+ end
84
113
 
114
+ Aidp.log_debug("watch_runner", "plan_process", issue: detailed[:number])
85
115
  @plan_processor.process(detailed)
86
116
  rescue RepositorySafetyChecker::UnauthorizedAuthorError => e
87
117
  Aidp.log_warn("watch_runner", "unauthorized issue author", issue: issue[:number], error: e.message)
@@ -89,18 +119,30 @@ module Aidp
89
119
  end
90
120
 
91
121
  def process_build_triggers
92
- issues = @repository_client.list_issues(labels: [BuildProcessor::BUILD_LABEL], state: "open")
122
+ build_label = @build_processor.build_label
123
+ issues = @repository_client.list_issues(labels: [build_label], state: "open")
124
+ Aidp.log_debug("watch_runner", "build_poll", label: build_label, total: issues.size)
93
125
  issues.each do |issue|
94
- next unless issue_has_label?(issue, BuildProcessor::BUILD_LABEL)
126
+ unless issue_has_label?(issue, build_label)
127
+ Aidp.log_debug("watch_runner", "build_skip_label_mismatch", issue: issue[:number], labels: issue[:labels])
128
+ next
129
+ end
95
130
 
96
131
  status = @state_store.build_status(issue[:number])
97
- next if status["status"] == "completed"
132
+ if status["status"] == "completed"
133
+ Aidp.log_debug("watch_runner", "build_skip_completed", issue: issue[:number])
134
+ next
135
+ end
98
136
 
99
137
  detailed = @repository_client.fetch_issue(issue[:number])
100
138
 
101
139
  # Check author authorization before processing
102
- next unless @safety_checker.should_process_issue?(detailed, enforce: false)
140
+ unless @safety_checker.should_process_issue?(detailed, enforce: false)
141
+ Aidp.log_debug("watch_runner", "build_skip_unauthorized_author", issue: detailed[:number], author: detailed[:author])
142
+ next
143
+ end
103
144
 
145
+ Aidp.log_debug("watch_runner", "build_process", issue: detailed[:number])
104
146
  @build_processor.process(detailed)
105
147
  rescue RepositorySafetyChecker::UnauthorizedAuthorError => e
106
148
  Aidp.log_warn("watch_runner", "unauthorized issue author", issue: issue[:number], error: e.message)
@@ -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
@@ -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.22.0
4
+ version: 0.24.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
@@ -297,6 +297,7 @@ files:
297
297
  - lib/aidp/harness/provider_factory.rb
298
298
  - lib/aidp/harness/provider_info.rb
299
299
  - lib/aidp/harness/provider_manager.rb
300
+ - lib/aidp/harness/provider_metrics.rb
300
301
  - lib/aidp/harness/provider_type_checker.rb
301
302
  - lib/aidp/harness/runner.rb
302
303
  - lib/aidp/harness/simple_user_interface.rb
@@ -347,10 +348,13 @@ files:
347
348
  - lib/aidp/prompt_optimization/style_guide_indexer.rb
348
349
  - lib/aidp/prompt_optimization/template_indexer.rb
349
350
  - lib/aidp/provider_manager.rb
351
+ - lib/aidp/providers/adapter.rb
350
352
  - lib/aidp/providers/anthropic.rb
351
353
  - lib/aidp/providers/base.rb
354
+ - lib/aidp/providers/capability_registry.rb
352
355
  - lib/aidp/providers/codex.rb
353
356
  - lib/aidp/providers/cursor.rb
357
+ - lib/aidp/providers/error_taxonomy.rb
354
358
  - lib/aidp/providers/gemini.rb
355
359
  - lib/aidp/providers/github_copilot.rb
356
360
  - lib/aidp/providers/opencode.rb
@@ -360,6 +364,7 @@ files:
360
364
  - lib/aidp/setup/devcontainer/generator.rb
361
365
  - lib/aidp/setup/devcontainer/parser.rb
362
366
  - lib/aidp/setup/devcontainer/port_manager.rb
367
+ - lib/aidp/setup/provider_registry.rb
363
368
  - lib/aidp/setup/wizard.rb
364
369
  - lib/aidp/skills.rb
365
370
  - lib/aidp/skills/composer.rb
@@ -438,6 +443,8 @@ files:
438
443
  - templates/skills/product_strategist/SKILL.md
439
444
  - templates/skills/repository_analyst/SKILL.md
440
445
  - templates/skills/test_analyzer/SKILL.md
446
+ - templates/work_loop/decide_whats_next.md
447
+ - templates/work_loop/diagnose_failures.md
441
448
  homepage: https://github.com/viamin/aidp
442
449
  licenses:
443
450
  - MIT
/data/{bin → exe}/aidp RENAMED
File without changes