aidp 0.32.0 → 0.33.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/analyze/feature_analyzer.rb +322 -320
  3. data/lib/aidp/auto_update/coordinator.rb +97 -7
  4. data/lib/aidp/auto_update.rb +0 -12
  5. data/lib/aidp/cli/devcontainer_commands.rb +0 -5
  6. data/lib/aidp/cli.rb +2 -1
  7. data/lib/aidp/comment_consolidator.rb +78 -0
  8. data/lib/aidp/concurrency.rb +0 -3
  9. data/lib/aidp/config.rb +0 -1
  10. data/lib/aidp/config_paths.rb +71 -0
  11. data/lib/aidp/execute/work_loop_runner.rb +324 -15
  12. data/lib/aidp/harness/ai_filter_factory.rb +285 -0
  13. data/lib/aidp/harness/config_schema.rb +97 -1
  14. data/lib/aidp/harness/config_validator.rb +1 -1
  15. data/lib/aidp/harness/configuration.rb +61 -5
  16. data/lib/aidp/harness/filter_definition.rb +212 -0
  17. data/lib/aidp/harness/generated_filter_strategy.rb +197 -0
  18. data/lib/aidp/harness/output_filter.rb +50 -25
  19. data/lib/aidp/harness/output_filter_config.rb +129 -0
  20. data/lib/aidp/harness/provider_manager.rb +90 -2
  21. data/lib/aidp/harness/runner.rb +0 -11
  22. data/lib/aidp/harness/test_runner.rb +179 -41
  23. data/lib/aidp/harness/thinking_depth_manager.rb +16 -0
  24. data/lib/aidp/harness/ui/navigation/submenu.rb +0 -2
  25. data/lib/aidp/loader.rb +195 -0
  26. data/lib/aidp/metadata/compiler.rb +29 -17
  27. data/lib/aidp/metadata/query.rb +1 -1
  28. data/lib/aidp/metadata/scanner.rb +8 -1
  29. data/lib/aidp/metadata/tool_metadata.rb +13 -13
  30. data/lib/aidp/metadata/validator.rb +10 -0
  31. data/lib/aidp/metadata.rb +16 -0
  32. data/lib/aidp/pr_worktree_manager.rb +2 -2
  33. data/lib/aidp/provider_manager.rb +1 -7
  34. data/lib/aidp/setup/wizard.rb +279 -9
  35. data/lib/aidp/skills.rb +0 -5
  36. data/lib/aidp/storage/csv_storage.rb +3 -0
  37. data/lib/aidp/style_guide/selector.rb +360 -0
  38. data/lib/aidp/tooling_detector.rb +283 -16
  39. data/lib/aidp/version.rb +1 -1
  40. data/lib/aidp/watch/change_request_processor.rb +152 -14
  41. data/lib/aidp/watch/repository_client.rb +41 -0
  42. data/lib/aidp/watch/runner.rb +29 -18
  43. data/lib/aidp/watch.rb +5 -7
  44. data/lib/aidp/workstream_cleanup.rb +0 -2
  45. data/lib/aidp/workstream_executor.rb +0 -4
  46. data/lib/aidp/worktree.rb +0 -1
  47. data/lib/aidp.rb +21 -106
  48. metadata +72 -36
  49. data/lib/aidp/config/paths.rb +0 -131
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "version_detector"
4
- require_relative "checkpoint_store"
5
- require_relative "update_logger"
6
- require_relative "failure_tracker"
7
- require_relative "update_policy"
8
- require_relative "errors"
9
-
10
3
  module Aidp
11
4
  module AutoUpdate
12
5
  # Facade for orchestrating the complete auto-update workflow
@@ -188,8 +181,105 @@ module Aidp
188
181
  }
189
182
  end
190
183
 
184
+ # Perform hot code reload without restarting the process
185
+ #
186
+ # This method performs a git pull and then uses Zeitwerk to reload
187
+ # all Ruby classes. Unlike initiate_update (which exits with code 75),
188
+ # this method keeps the process running.
189
+ #
190
+ # @param update_check [UpdateCheck] Update check result
191
+ # @return [Boolean] Whether reload was successful
192
+ # @raise [UpdateError] If updates are disabled or reload fails
193
+ def hot_reload_update(update_check = nil)
194
+ raise UpdateError, "Updates disabled by configuration" unless @policy.enabled
195
+
196
+ # Verify Zeitwerk loader is set up for reloading
197
+ unless Aidp::Loader.setup? && Aidp::Loader.reloading?
198
+ Aidp.log_warn("auto_update_coordinator", "hot_reload_not_available",
199
+ reason: "Zeitwerk loader not configured for reloading")
200
+ raise UpdateError, "Hot reloading not available. Loader must be configured with enable_reloading: true"
201
+ end
202
+
203
+ update_check ||= check_for_update
204
+ return false unless update_check.should_update?
205
+
206
+ from_version = update_check.current_version
207
+ to_version = update_check.available_version
208
+
209
+ Aidp.log_info("auto_update_coordinator", "hot_reload_starting",
210
+ from_version: from_version,
211
+ to_version: to_version)
212
+
213
+ # Perform git pull
214
+ unless perform_git_pull
215
+ raise UpdateError, "Git pull failed"
216
+ end
217
+
218
+ # Reload all classes using Zeitwerk
219
+ unless Aidp::Loader.reload!
220
+ @failure_tracker.record_failure
221
+ @update_logger.log_failure("Zeitwerk reload failed")
222
+ raise UpdateError, "Zeitwerk reload failed"
223
+ end
224
+
225
+ # Log success
226
+ @update_logger.log_success(
227
+ from_version: from_version,
228
+ to_version: to_version
229
+ )
230
+ @failure_tracker.reset_on_success
231
+
232
+ Aidp.log_info("auto_update_coordinator", "hot_reload_complete",
233
+ from_version: from_version,
234
+ to_version: to_version)
235
+
236
+ true
237
+ rescue UpdateError
238
+ raise
239
+ rescue => e
240
+ @failure_tracker.record_failure
241
+ @update_logger.log_failure("Hot reload failed: #{e.message}")
242
+ Aidp.log_error("auto_update_coordinator", "hot_reload_failed",
243
+ error: e.message)
244
+ raise UpdateError, "Hot reload failed: #{e.message}"
245
+ end
246
+
247
+ # Check if hot reloading is available
248
+ # @return [Boolean]
249
+ def hot_reload_available?
250
+ Aidp::Loader.setup? && Aidp::Loader.reloading?
251
+ end
252
+
191
253
  private
192
254
 
255
+ # Perform git pull to fetch latest code
256
+ # @return [Boolean] Whether pull was successful
257
+ def perform_git_pull
258
+ require "open3"
259
+
260
+ Aidp.log_debug("auto_update_coordinator", "git_pull_starting",
261
+ project_dir: @project_dir)
262
+
263
+ Dir.chdir(@project_dir) do
264
+ stdout, stderr, status = Open3.capture3("git", "pull", "--ff-only")
265
+
266
+ if status.success?
267
+ Aidp.log_info("auto_update_coordinator", "git_pull_success",
268
+ output: stdout.strip)
269
+ true
270
+ else
271
+ Aidp.log_error("auto_update_coordinator", "git_pull_failed",
272
+ stderr: stderr.strip,
273
+ exit_code: status.exitstatus)
274
+ false
275
+ end
276
+ end
277
+ rescue => e
278
+ Aidp.log_error("auto_update_coordinator", "git_pull_error",
279
+ error: e.message)
280
+ false
281
+ end
282
+
193
283
  def build_checkpoint(current_state, target_version)
194
284
  Checkpoint.new(
195
285
  mode: current_state[:mode] || "watch",
@@ -1,17 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "auto_update/errors"
4
- require_relative "auto_update/update_policy"
5
- require_relative "auto_update/update_check"
6
- require_relative "auto_update/checkpoint"
7
- require_relative "auto_update/bundler_adapter"
8
- require_relative "auto_update/rubygems_api_adapter"
9
- require_relative "auto_update/version_detector"
10
- require_relative "auto_update/checkpoint_store"
11
- require_relative "auto_update/update_logger"
12
- require_relative "auto_update/failure_tracker"
13
- require_relative "auto_update/coordinator"
14
-
15
3
  module Aidp
16
4
  # Auto-update functionality for Aidp in devcontainers
17
5
  module AutoUpdate
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../setup/devcontainer/parser"
4
- require_relative "../setup/devcontainer/generator"
5
- require_relative "../setup/devcontainer/port_manager"
6
- require_relative "../setup/devcontainer/backup_manager"
7
- require_relative "../message_display"
8
3
  require "json"
9
4
  require "yaml"
10
5
 
data/lib/aidp/cli.rb CHANGED
@@ -8,6 +8,7 @@ require_relative "harness/ui/enhanced_tui"
8
8
  require_relative "harness/ui/enhanced_workflow_selector"
9
9
  require_relative "harness/enhanced_runner"
10
10
  require_relative "cli/first_run_wizard"
11
+ require_relative "cli/issue_importer"
11
12
  require_relative "rescue_logging"
12
13
  require_relative "concurrency"
13
14
 
@@ -669,7 +670,7 @@ module Aidp
669
670
  return
670
671
  end
671
672
 
672
- importer = IssueImporter.new
673
+ importer = Aidp::IssueImporter.new
673
674
  issue_data = importer.import_issue(identifier)
674
675
 
675
676
  if issue_data
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ # Consolidates comments for GitHub issues and PRs by category
5
+ class CommentConsolidator
6
+ CATEGORY_HEADERS = {
7
+ progress: "## 🔄 Progress Report",
8
+ exceptions: "## 🚨 Exceptions and Errors",
9
+ completion: "## ✅ Completion Summary"
10
+ }
11
+
12
+ # @param repository_client [Aidp::Watch::RepositoryClient] GitHub repository client
13
+ # @param number [Integer] Issue or PR number
14
+ def initialize(repository_client:, number:)
15
+ @client = repository_client
16
+ @number = number
17
+ end
18
+
19
+ # Search for an existing comment by its category header
20
+ # @param category [Symbol] Comment category (:progress, :exceptions, :completion)
21
+ # @return [Hash, nil] Existing comment or nil if not found
22
+ def find_category_comment(category)
23
+ Aidp.log_debug("comment_consolidator", "searching_category_comment",
24
+ number: @number, category: category)
25
+
26
+ header = CATEGORY_HEADERS[category]
27
+ raise ArgumentError, "Invalid category: #{category}" unless header
28
+
29
+ comment = @client.find_comment(@number, header)
30
+ Aidp.log_debug("comment_consolidator", "find_category_comment_result",
31
+ found: !comment.nil?)
32
+ comment
33
+ end
34
+
35
+ # Update an existing category comment or create a new one
36
+ # @param category [Symbol] Comment category (:progress, :exceptions, :completion)
37
+ # @param new_content [String] New content to add to the comment
38
+ # @param append [Boolean] Whether to append or replace existing content
39
+ # @return [String] Result of comment operation (comment ID or response body)
40
+ def consolidate_comment(category:, new_content:, append: true)
41
+ Aidp.log_debug("comment_consolidator", "consolidating_comment", number: @number, category: category, append: append)
42
+
43
+ header = CATEGORY_HEADERS[category]
44
+ raise ArgumentError, "Invalid category: #{category}" unless header
45
+
46
+ existing_comment = find_category_comment(category)
47
+
48
+ content = if existing_comment && append
49
+ # Append new content with timestamp
50
+ existing_body = existing_comment[:body]
51
+ updated_body = if existing_body.include?(header)
52
+ existing_body.lines.first(1).join +
53
+ "### #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}\n\n" +
54
+ new_content + "\n\n" +
55
+ existing_body.lines[1..]&.join
56
+ else
57
+ # Reconstruct comment if header is missing
58
+ "#{header}\n\n### #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}\n\n" +
59
+ new_content + "\n\n" +
60
+ existing_body
61
+ end
62
+ updated_body
63
+ else
64
+ # Create new or replace content
65
+ "#{header}\n\n### #{Time.now.strftime("%Y-%m-%d %H:%M:%S")}\n\n" + new_content
66
+ end
67
+
68
+ # Update or create comment
69
+ if existing_comment
70
+ Aidp.log_debug("comment_consolidator", "updating_existing_comment", comment_id: existing_comment[:id])
71
+ @client.update_comment(existing_comment[:id], content)
72
+ else
73
+ Aidp.log_debug("comment_consolidator", "creating_new_comment")
74
+ @client.post_comment(@number, content)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "concurrent-ruby"
4
- require_relative "concurrency/wait"
5
- require_relative "concurrency/backoff"
6
- require_relative "concurrency/exec"
7
4
 
8
5
  module Aidp
9
6
  # Concurrency utilities for deterministic waiting, retry/backoff, and executor management.
data/lib/aidp/config.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
- require_relative "config/paths"
5
4
 
6
5
  module Aidp
7
6
  # Configuration management for both execute and analyze modes
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Aidp
6
+ # Centralized path management for all AIDP internal files
7
+ # Ensures consistent file locations and prevents path-related bugs
8
+ module ConfigPaths
9
+ def self.aidp_dir(project_dir = Dir.pwd) = File.join(project_dir, ".aidp")
10
+ def self.config_file(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "aidp.yml")
11
+ def self.config_dir(project_dir = Dir.pwd) = aidp_dir(project_dir)
12
+ def self.progress_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "progress")
13
+ def self.execute_progress_file(project_dir = Dir.pwd) = File.join(progress_dir(project_dir), "execute.yml")
14
+ def self.analyze_progress_file(project_dir = Dir.pwd) = File.join(progress_dir(project_dir), "analyze.yml")
15
+ def self.harness_state_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "harness")
16
+ def self.harness_state_file(mode, project_dir = Dir.pwd) = File.join(harness_state_dir(project_dir), "#{mode}_state.json")
17
+ def self.providers_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "providers")
18
+ def self.provider_info_file(provider_name, project_dir = Dir.pwd) = File.join(providers_dir(project_dir), "#{provider_name}_info.yml")
19
+ def self.jobs_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "jobs")
20
+ def self.checkpoint_file(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "checkpoint.yml")
21
+ def self.checkpoint_history_file(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "checkpoint_history.jsonl")
22
+ def self.json_storage_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "json")
23
+ def self.model_cache_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "model_cache")
24
+ def self.work_loop_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "work_loop")
25
+ def self.logs_dir(project_dir = Dir.pwd) = File.join(aidp_dir(project_dir), "logs")
26
+
27
+ def self.config_exists?(project_dir = Dir.pwd)
28
+ File.exist?(config_file(project_dir))
29
+ end
30
+
31
+ def self.ensure_aidp_dir(project_dir = Dir.pwd)
32
+ dir = aidp_dir(project_dir)
33
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
34
+ dir
35
+ end
36
+
37
+ def self.ensure_config_dir(project_dir = Dir.pwd)
38
+ ensure_aidp_dir(project_dir)
39
+ end
40
+
41
+ def self.ensure_progress_dir(project_dir = Dir.pwd)
42
+ dir = progress_dir(project_dir)
43
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
44
+ dir
45
+ end
46
+
47
+ def self.ensure_harness_state_dir(project_dir = Dir.pwd)
48
+ dir = harness_state_dir(project_dir)
49
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
50
+ dir
51
+ end
52
+
53
+ def self.ensure_providers_dir(project_dir = Dir.pwd)
54
+ dir = providers_dir(project_dir)
55
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
56
+ dir
57
+ end
58
+
59
+ def self.ensure_jobs_dir(project_dir = Dir.pwd)
60
+ dir = jobs_dir(project_dir)
61
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
62
+ dir
63
+ end
64
+
65
+ def self.ensure_json_storage_dir(project_dir = Dir.pwd)
66
+ dir = json_storage_dir(project_dir)
67
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
68
+ dir
69
+ end
70
+ end
71
+ end