aidp 0.24.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 (39) 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 +165 -1
  16. data/lib/aidp/harness/config_schema.rb +50 -0
  17. data/lib/aidp/harness/provider_factory.rb +2 -0
  18. data/lib/aidp/message_display.rb +10 -2
  19. data/lib/aidp/prompt_optimization/style_guide_indexer.rb +3 -1
  20. data/lib/aidp/provider_manager.rb +2 -0
  21. data/lib/aidp/providers/kilocode.rb +202 -0
  22. data/lib/aidp/setup/provider_registry.rb +15 -0
  23. data/lib/aidp/setup/wizard.rb +12 -4
  24. data/lib/aidp/skills/composer.rb +4 -0
  25. data/lib/aidp/skills/loader.rb +3 -1
  26. data/lib/aidp/version.rb +1 -1
  27. data/lib/aidp/watch/build_processor.rb +66 -16
  28. data/lib/aidp/watch/ci_fix_processor.rb +448 -0
  29. data/lib/aidp/watch/plan_processor.rb +12 -2
  30. data/lib/aidp/watch/repository_client.rb +380 -0
  31. data/lib/aidp/watch/review_processor.rb +266 -0
  32. data/lib/aidp/watch/reviewers/base_reviewer.rb +164 -0
  33. data/lib/aidp/watch/reviewers/performance_reviewer.rb +65 -0
  34. data/lib/aidp/watch/reviewers/security_reviewer.rb +65 -0
  35. data/lib/aidp/watch/reviewers/senior_dev_reviewer.rb +33 -0
  36. data/lib/aidp/watch/runner.rb +185 -0
  37. data/lib/aidp/watch/state_store.rb +53 -0
  38. data/lib/aidp.rb +1 -0
  39. metadata +20 -1
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../provider_manager"
4
+ require_relative "../../harness/config_manager"
5
+
6
+ module Aidp
7
+ module Watch
8
+ module Reviewers
9
+ # Base class for all PR reviewers
10
+ class BaseReviewer
11
+ attr_reader :provider_name, :persona_name, :focus_areas
12
+
13
+ def initialize(provider_name: nil)
14
+ @provider_name = provider_name
15
+ @persona_name = self.class::PERSONA_NAME
16
+ @focus_areas = self.class::FOCUS_AREAS
17
+ end
18
+
19
+ # Review the PR and return findings
20
+ # @param pr_data [Hash] PR metadata (number, title, body, etc.)
21
+ # @param files [Array<Hash>] Changed files with patches
22
+ # @param diff [String] Full diff content
23
+ # @return [Hash] Review findings with structure:
24
+ # {
25
+ # persona: String,
26
+ # findings: [
27
+ # {
28
+ # severity: "high|major|minor|nit",
29
+ # category: String,
30
+ # message: String,
31
+ # file: String (optional),
32
+ # line: Integer (optional),
33
+ # suggestion: String (optional)
34
+ # }
35
+ # ]
36
+ # }
37
+ def review(pr_data:, files:, diff:)
38
+ raise NotImplementedError, "Subclasses must implement #review"
39
+ end
40
+
41
+ protected
42
+
43
+ def provider
44
+ @provider ||= begin
45
+ provider_name = @provider_name || detect_default_provider
46
+ Aidp::ProviderManager.get_provider(provider_name, use_harness: false)
47
+ end
48
+ end
49
+
50
+ def detect_default_provider
51
+ config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
52
+ config_manager.default_provider || "anthropic"
53
+ rescue
54
+ "anthropic"
55
+ end
56
+
57
+ def system_prompt
58
+ <<~PROMPT
59
+ You are #{@persona_name}, reviewing a pull request.
60
+
61
+ Your focus areas are:
62
+ #{@focus_areas.map { |area| "- #{area}" }.join("\n")}
63
+
64
+ Review the code changes and provide structured feedback in the following JSON format:
65
+ {
66
+ "findings": [
67
+ {
68
+ "severity": "high|major|minor|nit",
69
+ "category": "Brief category (e.g., 'Security', 'Performance', 'Logic Error')",
70
+ "message": "Detailed explanation of the issue",
71
+ "file": "path/to/file.rb",
72
+ "line": 42,
73
+ "suggestion": "Optional: Suggested fix or improvement"
74
+ }
75
+ ]
76
+ }
77
+
78
+ Severity levels:
79
+ - high: Critical issues that must be fixed (security vulnerabilities, data loss, crashes)
80
+ - major: Significant problems that should be addressed (incorrect logic, performance issues)
81
+ - minor: Improvements that would be good to have (code quality, maintainability)
82
+ - nit: Stylistic or trivial suggestions (formatting, naming)
83
+
84
+ Focus on actionable, specific feedback. If no issues are found, return an empty findings array.
85
+ PROMPT
86
+ end
87
+
88
+ def analyze_with_provider(user_prompt)
89
+ full_prompt = "#{system_prompt}\n\n#{user_prompt}"
90
+ response = provider.send_message(prompt: full_prompt)
91
+ content = response.to_s.strip
92
+
93
+ # Extract JSON from response (handle code fences)
94
+ json_content = extract_json(content)
95
+
96
+ # Parse JSON response
97
+ parsed = JSON.parse(json_content)
98
+ parsed["findings"] || []
99
+ rescue JSON::ParserError => e
100
+ Aidp.log_error("reviewer", "Failed to parse provider response", persona: @persona_name, error: e.message, content: content)
101
+ []
102
+ rescue => e
103
+ Aidp.log_error("reviewer", "Review failed", persona: @persona_name, error: e.message)
104
+ []
105
+ end
106
+
107
+ def extract_json(text)
108
+ # Try to extract JSON from code fences or find JSON object
109
+ # Avoid regex to prevent ReDoS - use simple string operations
110
+ return text if text.start_with?("{") && text.end_with?("}")
111
+
112
+ # Extract from code fence using string operations
113
+ fence_start = text.index("```json")
114
+ if fence_start
115
+ json_start = text.index("{", fence_start)
116
+ fence_end = text.index("```", fence_start + 7)
117
+ if json_start && fence_end && json_start < fence_end
118
+ json_end = text.rindex("}", fence_end - 1)
119
+ return text[json_start..json_end] if json_end && json_end > json_start
120
+ end
121
+ end
122
+
123
+ # Find JSON object using string operations
124
+ first_brace = text.index("{")
125
+ last_brace = text.rindex("}")
126
+ if first_brace && last_brace && last_brace > first_brace
127
+ text[first_brace..last_brace]
128
+ else
129
+ text
130
+ end
131
+ end
132
+
133
+ def build_review_prompt(pr_data:, files:, diff:)
134
+ <<~PROMPT
135
+ Review this pull request from your expertise perspective:
136
+
137
+ **PR ##{pr_data[:number]}: #{pr_data[:title]}**
138
+
139
+ #{pr_data[:body]}
140
+
141
+ **Changed Files (#{files.length}):**
142
+ #{files.map { |f| "- #{f[:filename]} (+#{f[:additions]}/-#{f[:deletions]})" }.join("\n")}
143
+
144
+ **Diff:**
145
+ ```diff
146
+ #{truncate_diff(diff, max_lines: 500)}
147
+ ```
148
+
149
+ Please review the changes and provide your findings in JSON format.
150
+ PROMPT
151
+ end
152
+
153
+ def truncate_diff(diff, max_lines: 500)
154
+ lines = diff.lines
155
+ if lines.length > max_lines
156
+ lines.first(max_lines).join + "\n... (diff truncated, #{lines.length - max_lines} more lines)"
157
+ else
158
+ diff
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_reviewer"
4
+
5
+ module Aidp
6
+ module Watch
7
+ module Reviewers
8
+ # Performance Reviewer - focuses on performance issues and optimizations
9
+ class PerformanceReviewer < BaseReviewer
10
+ PERSONA_NAME = "Performance Analyst"
11
+ FOCUS_AREAS = [
12
+ "Algorithm complexity (O(n) vs O(n²), etc.)",
13
+ "Database query optimization (N+1 queries, missing indexes)",
14
+ "Memory allocation and garbage collection pressure",
15
+ "Blocking I/O operations",
16
+ "Inefficient data structures",
17
+ "Unnecessary computations or redundant work",
18
+ "Caching opportunities",
19
+ "Resource leaks (connections, file handles, etc.)",
20
+ "Concurrent access patterns",
21
+ "Network round-trips and latency"
22
+ ].freeze
23
+
24
+ def review(pr_data:, files:, diff:)
25
+ user_prompt = build_performance_prompt(pr_data: pr_data, files: files, diff: diff)
26
+ findings = analyze_with_provider(user_prompt)
27
+
28
+ {
29
+ persona: PERSONA_NAME,
30
+ findings: findings
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ def build_performance_prompt(pr_data:, files:, diff:)
37
+ base_prompt = build_review_prompt(pr_data: pr_data, files: files, diff: diff)
38
+
39
+ <<~PROMPT
40
+ #{base_prompt}
41
+
42
+ **Additional Performance Focus:**
43
+ Pay special attention to:
44
+ 1. Loop complexity and nested iterations
45
+ 2. Database queries (look for N+1 patterns, missing eager loading)
46
+ 3. Memory allocations in hot paths
47
+ 4. I/O operations (file, network, database) - are they batched?
48
+ 5. Synchronous operations that could be asynchronous
49
+ 6. Large data structures being copied or traversed repeatedly
50
+ 7. Missing caching opportunities
51
+ 8. Resource pooling and connection management
52
+ 9. Lazy loading vs eager loading trade-offs
53
+ 10. Potential bottlenecks under high load
54
+
55
+ Mark performance issues as:
56
+ - "high" if they could cause timeouts, out-of-memory errors, or severe degradation
57
+ - "major" if they significantly impact response time or resource usage
58
+ - "minor" for optimizations that would improve efficiency
59
+ - "nit" for micro-optimizations with negligible impact
60
+ PROMPT
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_reviewer"
4
+
5
+ module Aidp
6
+ module Watch
7
+ module Reviewers
8
+ # Security Reviewer - focuses on security vulnerabilities and risks
9
+ class SecurityReviewer < BaseReviewer
10
+ PERSONA_NAME = "Security Specialist"
11
+ FOCUS_AREAS = [
12
+ "Injection vulnerabilities (SQL, command, XSS, etc.)",
13
+ "Authentication and authorization flaws",
14
+ "Sensitive data exposure",
15
+ "Insecure deserialization",
16
+ "Security misconfiguration",
17
+ "Insufficient logging and monitoring",
18
+ "Insecure dependencies",
19
+ "Secrets and credentials in code",
20
+ "Input validation and sanitization",
21
+ "OWASP Top 10 vulnerabilities"
22
+ ].freeze
23
+
24
+ def review(pr_data:, files:, diff:)
25
+ user_prompt = build_security_prompt(pr_data: pr_data, files: files, diff: diff)
26
+ findings = analyze_with_provider(user_prompt)
27
+
28
+ {
29
+ persona: PERSONA_NAME,
30
+ findings: findings
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ def build_security_prompt(pr_data:, files:, diff:)
37
+ base_prompt = build_review_prompt(pr_data: pr_data, files: files, diff: diff)
38
+
39
+ <<~PROMPT
40
+ #{base_prompt}
41
+
42
+ **Additional Security Focus:**
43
+ Pay special attention to:
44
+ 1. User input handling and validation
45
+ 2. Database queries and potential SQL injection
46
+ 3. File operations and path traversal risks
47
+ 4. Authentication and session management
48
+ 5. Cryptographic operations and key management
49
+ 6. API endpoints and their access controls
50
+ 7. Third-party dependencies and their security posture
51
+ 8. Environment variables and configuration
52
+ 9. Logging of sensitive information
53
+ 10. Command execution and shell injection risks
54
+
55
+ Mark any security issues as "high" severity if they could lead to:
56
+ - Unauthorized access or privilege escalation
57
+ - Data breach or exposure of sensitive information
58
+ - Remote code execution
59
+ - Denial of service
60
+ PROMPT
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base_reviewer"
4
+
5
+ module Aidp
6
+ module Watch
7
+ module Reviewers
8
+ # Senior Developer Reviewer - focuses on correctness, architecture, and best practices
9
+ class SeniorDevReviewer < BaseReviewer
10
+ PERSONA_NAME = "Senior Developer"
11
+ FOCUS_AREAS = [
12
+ "Code correctness and logic errors",
13
+ "Architecture and design patterns",
14
+ "API design and consistency",
15
+ "Error handling and edge cases",
16
+ "Code maintainability and readability",
17
+ "Testing coverage and quality",
18
+ "Documentation completeness"
19
+ ].freeze
20
+
21
+ def review(pr_data:, files:, diff:)
22
+ user_prompt = build_review_prompt(pr_data: pr_data, files: files, diff: diff)
23
+ findings = analyze_with_provider(user_prompt)
24
+
25
+ {
26
+ persona: PERSONA_NAME,
27
+ findings: findings
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -9,6 +9,9 @@ require_relative "state_store"
9
9
  require_relative "plan_generator"
10
10
  require_relative "plan_processor"
11
11
  require_relative "build_processor"
12
+ require_relative "../auto_update"
13
+ require_relative "review_processor"
14
+ require_relative "ci_fix_processor"
12
15
 
13
16
  module Aidp
14
17
  module Watch
@@ -26,6 +29,8 @@ module Aidp
26
29
  @project_dir = project_dir
27
30
  @force = force
28
31
  @verbose = verbose
32
+ @provider_name = provider_name
33
+ @safety_config = safety_config
29
34
 
30
35
  owner, repo = RepositoryClient.parse_issues_url(issues_url)
31
36
  @repository_client = RepositoryClient.new(owner: owner, repo: repo, gh_available: gh_available)
@@ -52,9 +57,32 @@ module Aidp
52
57
  verbose: verbose,
53
58
  label_config: label_config
54
59
  )
60
+
61
+ # Initialize auto-update coordinator
62
+ @auto_update_coordinator = Aidp::AutoUpdate.coordinator(project_dir: project_dir)
63
+ @last_update_check = nil
64
+ @review_processor = ReviewProcessor.new(
65
+ repository_client: @repository_client,
66
+ state_store: @state_store,
67
+ provider_name: provider_name,
68
+ project_dir: project_dir,
69
+ label_config: label_config,
70
+ verbose: verbose
71
+ )
72
+ @ci_fix_processor = CiFixProcessor.new(
73
+ repository_client: @repository_client,
74
+ state_store: @state_store,
75
+ provider_name: provider_name,
76
+ project_dir: project_dir,
77
+ label_config: label_config,
78
+ verbose: verbose
79
+ )
55
80
  end
56
81
 
57
82
  def start
83
+ # Check for and restore from checkpoint (after auto-update)
84
+ restore_from_checkpoint_if_exists
85
+
58
86
  # Validate safety requirements before starting
59
87
  @safety_checker.validate_watch_mode_safety!(force: @force)
60
88
 
@@ -91,6 +119,9 @@ module Aidp
91
119
  def process_cycle
92
120
  process_plan_triggers
93
121
  process_build_triggers
122
+ check_for_updates_if_due
123
+ process_review_triggers
124
+ process_ci_fix_triggers
94
125
  end
95
126
 
96
127
  def process_plan_triggers
@@ -149,12 +180,166 @@ module Aidp
149
180
  end
150
181
  end
151
182
 
183
+ def process_review_triggers
184
+ review_label = @review_processor.review_label
185
+ prs = @repository_client.list_pull_requests(labels: [review_label], state: "open")
186
+ Aidp.log_debug("watch_runner", "review_poll", label: review_label, total: prs.size)
187
+ prs.each do |pr|
188
+ unless pr_has_label?(pr, review_label)
189
+ Aidp.log_debug("watch_runner", "review_skip_label_mismatch", pr: pr[:number], labels: pr[:labels])
190
+ next
191
+ end
192
+
193
+ detailed = @repository_client.fetch_pull_request(pr[:number])
194
+
195
+ # Check author authorization before processing
196
+ unless @safety_checker.should_process_issue?(detailed, enforce: false)
197
+ Aidp.log_debug("watch_runner", "review_skip_unauthorized_author", pr: detailed[:number], author: detailed[:author])
198
+ next
199
+ end
200
+
201
+ Aidp.log_debug("watch_runner", "review_process", pr: detailed[:number])
202
+ @review_processor.process(detailed)
203
+ rescue RepositorySafetyChecker::UnauthorizedAuthorError => e
204
+ Aidp.log_warn("watch_runner", "unauthorized PR author", pr: pr[:number], error: e.message)
205
+ end
206
+ end
207
+
208
+ def process_ci_fix_triggers
209
+ ci_fix_label = @ci_fix_processor.ci_fix_label
210
+ prs = @repository_client.list_pull_requests(labels: [ci_fix_label], state: "open")
211
+ Aidp.log_debug("watch_runner", "ci_fix_poll", label: ci_fix_label, total: prs.size)
212
+ prs.each do |pr|
213
+ unless pr_has_label?(pr, ci_fix_label)
214
+ Aidp.log_debug("watch_runner", "ci_fix_skip_label_mismatch", pr: pr[:number], labels: pr[:labels])
215
+ next
216
+ end
217
+
218
+ detailed = @repository_client.fetch_pull_request(pr[:number])
219
+
220
+ # Check author authorization before processing
221
+ unless @safety_checker.should_process_issue?(detailed, enforce: false)
222
+ Aidp.log_debug("watch_runner", "ci_fix_skip_unauthorized_author", pr: detailed[:number], author: detailed[:author])
223
+ next
224
+ end
225
+
226
+ Aidp.log_debug("watch_runner", "ci_fix_process", pr: detailed[:number])
227
+ @ci_fix_processor.process(detailed)
228
+ rescue RepositorySafetyChecker::UnauthorizedAuthorError => e
229
+ Aidp.log_warn("watch_runner", "unauthorized PR author", pr: pr[:number], error: e.message)
230
+ end
231
+ end
232
+
152
233
  def issue_has_label?(issue, label)
153
234
  Array(issue[:labels]).any? do |issue_label|
154
235
  name = issue_label.is_a?(Hash) ? issue_label["name"] : issue_label.to_s
155
236
  name.casecmp(label).zero?
156
237
  end
157
238
  end
239
+
240
+ # Restore from checkpoint if one exists (after auto-update)
241
+ def restore_from_checkpoint_if_exists
242
+ return unless @auto_update_coordinator.policy.enabled
243
+
244
+ checkpoint = @auto_update_coordinator.restore_from_checkpoint
245
+ return unless checkpoint
246
+
247
+ # Checkpoint exists and was successfully restored
248
+ display_message("✨ Restored from checkpoint after update to v#{Aidp::VERSION}", type: :success)
249
+
250
+ # Override instance variables with checkpoint state
251
+ if checkpoint.watch_mode? && checkpoint.watch_state
252
+ @interval = checkpoint.watch_state[:interval] || @interval
253
+ Aidp.log_info("watch_runner", "checkpoint_restored",
254
+ checkpoint_id: checkpoint.checkpoint_id,
255
+ interval: @interval)
256
+ end
257
+ rescue => e
258
+ # Log but don't fail - continue with fresh start
259
+ Aidp.log_error("watch_runner", "checkpoint_restore_failed",
260
+ error: e.message)
261
+ display_message("⚠️ Checkpoint restore failed, starting fresh: #{e.message}", type: :warning)
262
+ end
263
+
264
+ # Check for updates at appropriate intervals
265
+ def check_for_updates_if_due
266
+ return unless @auto_update_coordinator.policy.enabled
267
+ return unless time_for_update_check?
268
+
269
+ update_check = @auto_update_coordinator.check_for_update
270
+
271
+ if update_check.should_update?
272
+ display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}", type: :highlight)
273
+ display_message(" Saving checkpoint and initiating update...", type: :muted)
274
+
275
+ initiate_update(update_check)
276
+ # Never returns - exits with code 75
277
+ end
278
+ rescue Aidp::AutoUpdate::UpdateLoopError => e
279
+ # Restart loop detected - disable auto-update
280
+ display_message("⚠️ Auto-update disabled: #{e.message}", type: :error)
281
+ Aidp.log_error("watch_runner", "update_loop_detected", error: e.message)
282
+ rescue Aidp::AutoUpdate::UpdateError => e
283
+ # Non-fatal update error - log and continue
284
+ Aidp.log_error("watch_runner", "update_check_failed", error: e.message)
285
+ end
286
+
287
+ # Determine if it's time to check for updates
288
+ # @return [Boolean]
289
+ def time_for_update_check?
290
+ return true if @last_update_check.nil?
291
+
292
+ elapsed = Time.now - @last_update_check
293
+ elapsed >= @auto_update_coordinator.policy.check_interval_seconds
294
+ end
295
+
296
+ # Initiate update process: capture state, create checkpoint, exit
297
+ # @param update_check [Aidp::AutoUpdate::UpdateCheck] Update check result
298
+ def initiate_update(update_check)
299
+ current_state = capture_current_state
300
+
301
+ # This will exit with code 75 if successful
302
+ @auto_update_coordinator.initiate_update(current_state)
303
+ end
304
+
305
+ # Capture current watch mode state for checkpoint
306
+ # @return [Hash] Current state
307
+ def capture_current_state
308
+ {
309
+ mode: "watch",
310
+ watch_state: {
311
+ repository: @repository_client.full_repo,
312
+ interval: @interval,
313
+ provider_name: @provider_name,
314
+ persona: nil,
315
+ safety_config: @safety_config,
316
+ worktree_context: capture_worktree_context,
317
+ state_store_snapshot: @state_store.send(:state).dup
318
+ }
319
+ }
320
+ end
321
+
322
+ # Capture git worktree context
323
+ # @return [Hash] Worktree information
324
+ def capture_worktree_context
325
+ return {} unless system("git rev-parse --git-dir > /dev/null 2>&1")
326
+
327
+ {
328
+ branch: `git rev-parse --abbrev-ref HEAD`.strip,
329
+ commit_sha: `git rev-parse HEAD`.strip,
330
+ remote_url: `git config --get remote.origin.url`.strip
331
+ }
332
+ rescue => e
333
+ Aidp.log_debug("watch_runner", "worktree_context_unavailable", error: e.message)
334
+ {}
335
+ end
336
+
337
+ def pr_has_label?(pr, label)
338
+ Array(pr[:labels]).any? do |pr_label|
339
+ name = pr_label.is_a?(Hash) ? pr_label["name"] : pr_label.to_s
340
+ name.casecmp(label).zero?
341
+ end
342
+ end
158
343
  end
159
344
  end
160
345
  end
@@ -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
 
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aidp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -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
@@ -357,6 +369,7 @@ files:
357
369
  - lib/aidp/providers/error_taxonomy.rb
358
370
  - lib/aidp/providers/gemini.rb
359
371
  - lib/aidp/providers/github_copilot.rb
372
+ - lib/aidp/providers/kilocode.rb
360
373
  - lib/aidp/providers/opencode.rb
361
374
  - lib/aidp/rescue_logging.rb
362
375
  - lib/aidp/safe_directory.rb
@@ -387,10 +400,16 @@ files:
387
400
  - lib/aidp/version.rb
388
401
  - lib/aidp/watch.rb
389
402
  - lib/aidp/watch/build_processor.rb
403
+ - lib/aidp/watch/ci_fix_processor.rb
390
404
  - lib/aidp/watch/plan_generator.rb
391
405
  - lib/aidp/watch/plan_processor.rb
392
406
  - lib/aidp/watch/repository_client.rb
393
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
394
413
  - lib/aidp/watch/runner.rb
395
414
  - lib/aidp/watch/state_store.rb
396
415
  - lib/aidp/workflows/definitions.rb