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,472 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../repository"
4
+
5
+ module Aidp
6
+ module Database
7
+ module Repositories
8
+ # Repository for watch_state table
9
+ # Replaces watch/*.yml files
10
+ # Stores all watch mode state in a single JSON blob per repository
11
+ class WatchStateRepository < Repository
12
+ def initialize(project_dir: Dir.pwd, repository: nil)
13
+ super(project_dir: project_dir, table_name: "watch_state")
14
+ @repository = repository
15
+ end
16
+
17
+ attr_reader :repository
18
+
19
+ # Get or create state for repository
20
+ #
21
+ # @return [Hash] State data
22
+ def state
23
+ @state ||= load_or_create_state
24
+ end
25
+
26
+ # Plan tracking methods
27
+
28
+ def plan_processed?(issue_number)
29
+ plans.key?(issue_number.to_s)
30
+ end
31
+
32
+ def plan_data(issue_number)
33
+ plans[issue_number.to_s]
34
+ end
35
+
36
+ def plan_iteration_count(issue_number)
37
+ plan = plans[issue_number.to_s]
38
+ return 0 unless plan
39
+ plan[:iteration] || 1
40
+ end
41
+
42
+ def record_plan(issue_number, data)
43
+ existing_plan = plans[issue_number.to_s]
44
+ iteration = existing_plan ? (existing_plan[:iteration] || 1) + 1 : 1
45
+
46
+ plans[issue_number.to_s] = {
47
+ summary: data[:summary],
48
+ tasks: data[:tasks],
49
+ questions: data[:questions],
50
+ comment_body: data[:comment_body],
51
+ comment_hint: data[:comment_hint],
52
+ comment_id: data[:comment_id],
53
+ posted_at: data[:posted_at] || current_timestamp,
54
+ iteration: iteration,
55
+ previous_iteration_at: existing_plan&.dig(:posted_at)
56
+ }.compact
57
+
58
+ save!
59
+ end
60
+
61
+ # Build tracking methods
62
+
63
+ def build_status(issue_number)
64
+ builds[issue_number.to_s] || {}
65
+ end
66
+
67
+ def record_build_status(issue_number, status:, details: {})
68
+ builds[issue_number.to_s] = {
69
+ status: status,
70
+ updated_at: current_timestamp
71
+ }.merge(symbolize_keys(details))
72
+
73
+ save!
74
+ end
75
+
76
+ def workstream_for_issue(issue_number)
77
+ data = build_status(issue_number)
78
+ return nil if data.nil? || data.empty?
79
+
80
+ {
81
+ issue_number: issue_number.to_i,
82
+ branch: data[:branch],
83
+ workstream: data[:workstream],
84
+ pr_url: data[:pr_url],
85
+ status: data[:status]
86
+ }
87
+ end
88
+
89
+ def find_build_by_pr(pr_number)
90
+ builds.each do |issue_number, data|
91
+ pr_url = data[:pr_url]
92
+ next unless pr_url
93
+
94
+ if pr_url.match?(%r{/pull/#{pr_number}\b})
95
+ return {
96
+ issue_number: issue_number.to_i,
97
+ branch: data[:branch],
98
+ workstream: data[:workstream],
99
+ pr_url: pr_url,
100
+ status: data[:status]
101
+ }
102
+ end
103
+ end
104
+
105
+ nil
106
+ end
107
+
108
+ # Review tracking methods
109
+
110
+ def review_processed?(pr_number)
111
+ reviews.key?(pr_number.to_s)
112
+ end
113
+
114
+ def review_data(pr_number)
115
+ reviews[pr_number.to_s]
116
+ end
117
+
118
+ def record_review(pr_number, data)
119
+ reviews[pr_number.to_s] = {
120
+ timestamp: data[:timestamp] || current_timestamp,
121
+ reviewers: data[:reviewers],
122
+ total_findings: data[:total_findings],
123
+ comment_id: data[:comment_id]
124
+ }.compact
125
+
126
+ save!
127
+ end
128
+
129
+ # CI fix tracking methods
130
+
131
+ def ci_fix_completed?(pr_number)
132
+ fix_data = ci_fixes[pr_number.to_s]
133
+ !!(fix_data && fix_data[:status] == "completed")
134
+ end
135
+
136
+ def ci_fix_data(pr_number)
137
+ ci_fixes[pr_number.to_s]
138
+ end
139
+
140
+ def record_ci_fix(pr_number, data)
141
+ ci_fixes[pr_number.to_s] = {
142
+ status: data[:status],
143
+ timestamp: data[:timestamp] || current_timestamp,
144
+ reason: data[:reason],
145
+ root_causes: data[:root_causes],
146
+ fixes_count: data[:fixes_count]
147
+ }.compact
148
+
149
+ save!
150
+ end
151
+
152
+ # Change request tracking methods
153
+
154
+ def change_request_processed?(pr_number)
155
+ change_requests.key?(pr_number.to_s)
156
+ end
157
+
158
+ def change_request_data(pr_number)
159
+ change_requests[pr_number.to_s]
160
+ end
161
+
162
+ def record_change_request(pr_number, data)
163
+ change_requests[pr_number.to_s] = {
164
+ status: data[:status],
165
+ timestamp: data[:timestamp] || current_timestamp,
166
+ changes_applied: data[:changes_applied],
167
+ commits: data[:commits],
168
+ reason: data[:reason],
169
+ clarification_count: data[:clarification_count],
170
+ verification_reasons: data[:verification_reasons],
171
+ missing_items: data[:missing_items],
172
+ additional_work: data[:additional_work]
173
+ }.compact
174
+
175
+ save!
176
+ end
177
+
178
+ def reset_change_request_state(pr_number)
179
+ change_requests.delete(pr_number.to_s)
180
+ save!
181
+ end
182
+
183
+ # Auto PR tracking methods
184
+
185
+ def auto_pr_iteration_count(pr_number)
186
+ data = auto_prs[pr_number.to_s]
187
+ return 0 unless data
188
+ data[:iteration] || 0
189
+ end
190
+
191
+ def auto_pr_data(pr_number)
192
+ auto_prs[pr_number.to_s]
193
+ end
194
+
195
+ def record_auto_pr_iteration(pr_number, data = {})
196
+ key = pr_number.to_s
197
+ existing = auto_prs[key] || {}
198
+ iteration = (existing[:iteration] || 0) + 1
199
+
200
+ auto_prs[key] = {
201
+ iteration: iteration,
202
+ last_processed_at: current_timestamp,
203
+ status: data[:status] || "in_progress",
204
+ metadata: symbolize_keys(data[:metadata] || {})
205
+ }.merge(symbolize_keys(data.except(:status, :metadata)))
206
+
207
+ save!
208
+ iteration
209
+ end
210
+
211
+ def complete_auto_pr(pr_number, data = {})
212
+ key = pr_number.to_s
213
+ existing = auto_prs[key] || {}
214
+
215
+ auto_prs[key] = existing.merge({
216
+ status: "completed",
217
+ completed_at: current_timestamp
218
+ }).merge(symbolize_keys(data))
219
+
220
+ save!
221
+ end
222
+
223
+ def auto_pr_cap_reached?(pr_number, cap:)
224
+ auto_pr_iteration_count(pr_number) >= cap
225
+ end
226
+
227
+ # Detection comment tracking
228
+
229
+ def detection_comment_posted?(detection_key)
230
+ detection_comments.key?(detection_key.to_s)
231
+ end
232
+
233
+ def record_detection_comment(detection_key, timestamp:)
234
+ detection_comments[detection_key.to_s] = {
235
+ timestamp: timestamp,
236
+ posted_at: current_timestamp
237
+ }
238
+ save!
239
+ end
240
+
241
+ # Feedback tracking
242
+
243
+ def tracked_comments
244
+ comments = []
245
+
246
+ plans.each do |issue_number, data|
247
+ next unless data[:comment_id]
248
+ comments << {
249
+ comment_id: data[:comment_id],
250
+ processor_type: "plan",
251
+ number: issue_number.to_i,
252
+ posted_at: data[:posted_at]
253
+ }
254
+ end
255
+
256
+ reviews.each do |pr_number, data|
257
+ next unless data[:comment_id]
258
+ comments << {
259
+ comment_id: data[:comment_id],
260
+ processor_type: "review",
261
+ number: pr_number.to_i,
262
+ posted_at: data[:timestamp]
263
+ }
264
+ end
265
+
266
+ builds.each do |issue_number, data|
267
+ next unless data[:comment_id]
268
+ comments << {
269
+ comment_id: data[:comment_id],
270
+ processor_type: "build",
271
+ number: issue_number.to_i,
272
+ posted_at: data[:updated_at]
273
+ }
274
+ end
275
+
276
+ feedback_comments.each do |_key, data|
277
+ comments << {
278
+ comment_id: data[:comment_id],
279
+ processor_type: data[:processor_type],
280
+ number: data[:number].to_i,
281
+ posted_at: data[:posted_at]
282
+ }
283
+ end
284
+
285
+ comments
286
+ end
287
+
288
+ def track_comment_for_feedback(comment_id:, processor_type:, number:)
289
+ key = "#{processor_type}_#{number}"
290
+ feedback_comments[key] = {
291
+ comment_id: comment_id.to_s,
292
+ processor_type: processor_type,
293
+ number: number,
294
+ posted_at: current_timestamp
295
+ }
296
+ save!
297
+ end
298
+
299
+ def processed_reaction_ids(comment_id)
300
+ data = processed_reactions[comment_id.to_s]
301
+ return [] unless data
302
+ data[:reaction_ids] || []
303
+ end
304
+
305
+ def mark_reaction_processed(comment_id, reaction_id)
306
+ key = comment_id.to_s
307
+ processed_reactions[key] ||= {reaction_ids: [], last_checked: nil}
308
+ processed_reactions[key][:reaction_ids] << reaction_id unless processed_reactions[key][:reaction_ids].include?(reaction_id)
309
+ processed_reactions[key][:last_checked] = current_timestamp
310
+ save!
311
+ end
312
+
313
+ private
314
+
315
+ def load_or_create_state
316
+ row = query_one(
317
+ "SELECT * FROM watch_state WHERE project_dir = ? AND repository = ?",
318
+ [project_dir, repository]
319
+ )
320
+
321
+ if row
322
+ {
323
+ id: row["id"],
324
+ plans: deserialize_json(row["plans"]) || {},
325
+ builds: deserialize_json(row["builds"]) || {},
326
+ reviews: deserialize_json(row["reviews"]) || {},
327
+ ci_fixes: deserialize_json(row["ci_fixes"]) || {},
328
+ change_requests: deserialize_json(row["change_requests"]) || {},
329
+ detection_comments: deserialize_json(row["detection_comments"]) || {},
330
+ feedback_comments: deserialize_json(row["feedback_comments"]) || {},
331
+ processed_reactions: deserialize_json(row["processed_reactions"]) || {},
332
+ auto_prs: deserialize_json(row["auto_prs"]) || {},
333
+ metadata: deserialize_json(row["metadata"]) || {}
334
+ }
335
+ else
336
+ {
337
+ plans: {},
338
+ builds: {},
339
+ reviews: {},
340
+ ci_fixes: {},
341
+ change_requests: {},
342
+ detection_comments: {},
343
+ feedback_comments: {},
344
+ processed_reactions: {},
345
+ auto_prs: {},
346
+ metadata: {}
347
+ }
348
+ end
349
+ end
350
+
351
+ def save!
352
+ now = current_timestamp
353
+
354
+ existing = query_one(
355
+ "SELECT id FROM watch_state WHERE project_dir = ? AND repository = ?",
356
+ [project_dir, repository]
357
+ )
358
+
359
+ if existing
360
+ execute(
361
+ <<~SQL,
362
+ UPDATE watch_state SET
363
+ plans = ?,
364
+ builds = ?,
365
+ reviews = ?,
366
+ ci_fixes = ?,
367
+ change_requests = ?,
368
+ detection_comments = ?,
369
+ feedback_comments = ?,
370
+ processed_reactions = ?,
371
+ auto_prs = ?,
372
+ metadata = ?,
373
+ last_poll_at = ?,
374
+ updated_at = ?
375
+ WHERE project_dir = ? AND repository = ?
376
+ SQL
377
+ [
378
+ serialize_json(plans),
379
+ serialize_json(builds),
380
+ serialize_json(reviews),
381
+ serialize_json(ci_fixes),
382
+ serialize_json(change_requests),
383
+ serialize_json(detection_comments),
384
+ serialize_json(feedback_comments),
385
+ serialize_json(processed_reactions),
386
+ serialize_json(auto_prs),
387
+ serialize_json(state[:metadata]),
388
+ now,
389
+ now,
390
+ project_dir,
391
+ repository
392
+ ]
393
+ )
394
+ else
395
+ execute(
396
+ insert_sql([
397
+ :project_dir, :repository, :plans, :builds, :reviews,
398
+ :ci_fixes, :change_requests, :detection_comments,
399
+ :feedback_comments, :processed_reactions, :auto_prs,
400
+ :metadata, :last_poll_at, :created_at, :updated_at
401
+ ]),
402
+ [
403
+ project_dir,
404
+ repository,
405
+ serialize_json(plans),
406
+ serialize_json(builds),
407
+ serialize_json(reviews),
408
+ serialize_json(ci_fixes),
409
+ serialize_json(change_requests),
410
+ serialize_json(detection_comments),
411
+ serialize_json(feedback_comments),
412
+ serialize_json(processed_reactions),
413
+ serialize_json(auto_prs),
414
+ serialize_json(state[:metadata]),
415
+ now,
416
+ now,
417
+ now
418
+ ]
419
+ )
420
+ @state[:id] = last_insert_row_id
421
+ end
422
+
423
+ Aidp.log_debug("watch_state_repository", "saved", repository: repository)
424
+ end
425
+
426
+ def plans
427
+ state[:plans]
428
+ end
429
+
430
+ def builds
431
+ state[:builds]
432
+ end
433
+
434
+ def reviews
435
+ state[:reviews]
436
+ end
437
+
438
+ def ci_fixes
439
+ state[:ci_fixes]
440
+ end
441
+
442
+ def change_requests
443
+ state[:change_requests]
444
+ end
445
+
446
+ def detection_comments
447
+ state[:detection_comments]
448
+ end
449
+
450
+ def feedback_comments
451
+ state[:feedback_comments]
452
+ end
453
+
454
+ def processed_reactions
455
+ state[:processed_reactions]
456
+ end
457
+
458
+ def auto_prs
459
+ state[:auto_prs]
460
+ end
461
+
462
+ def symbolize_keys(hash)
463
+ return {} unless hash
464
+
465
+ hash.each_with_object({}) do |(key, value), memo|
466
+ memo[key.to_sym] = value
467
+ end
468
+ end
469
+ end
470
+ end
471
+ end
472
+ end