aidp 0.31.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 (52) 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 +394 -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 +128 -2
  21. data/lib/aidp/harness/provider_metrics.rb +5 -3
  22. data/lib/aidp/harness/runner.rb +0 -11
  23. data/lib/aidp/harness/test_runner.rb +179 -41
  24. data/lib/aidp/harness/thinking_depth_manager.rb +16 -0
  25. data/lib/aidp/harness/ui/navigation/submenu.rb +0 -2
  26. data/lib/aidp/loader.rb +195 -0
  27. data/lib/aidp/metadata/compiler.rb +29 -17
  28. data/lib/aidp/metadata/query.rb +1 -1
  29. data/lib/aidp/metadata/scanner.rb +8 -1
  30. data/lib/aidp/metadata/tool_metadata.rb +13 -13
  31. data/lib/aidp/metadata/validator.rb +10 -0
  32. data/lib/aidp/metadata.rb +16 -0
  33. data/lib/aidp/pr_worktree_manager.rb +582 -0
  34. data/lib/aidp/provider_manager.rb +1 -7
  35. data/lib/aidp/setup/wizard.rb +279 -9
  36. data/lib/aidp/skills.rb +0 -5
  37. data/lib/aidp/storage/csv_storage.rb +3 -0
  38. data/lib/aidp/style_guide/selector.rb +360 -0
  39. data/lib/aidp/tooling_detector.rb +283 -16
  40. data/lib/aidp/util.rb +11 -0
  41. data/lib/aidp/version.rb +1 -1
  42. data/lib/aidp/watch/change_request_processor.rb +152 -14
  43. data/lib/aidp/watch/repository_client.rb +41 -0
  44. data/lib/aidp/watch/runner.rb +29 -18
  45. data/lib/aidp/watch.rb +5 -7
  46. data/lib/aidp/workstream_cleanup.rb +0 -2
  47. data/lib/aidp/workstream_executor.rb +0 -4
  48. data/lib/aidp/worktree.rb +0 -1
  49. data/lib/aidp/worktree_branch_manager.rb +70 -1
  50. data/lib/aidp.rb +21 -106
  51. metadata +73 -36
  52. data/lib/aidp/config/paths.rb +0 -131
@@ -120,6 +120,47 @@ module Aidp
120
120
  gh_available? ? fetch_pr_comments_via_gh(number) : fetch_pr_comments_via_api(number)
121
121
  end
122
122
 
123
+ # Create or update a categorized comment (e.g., under a header) on an issue.
124
+ # If a comment with the category header exists, either append to it or
125
+ # replace it while archiving the previous content inline.
126
+ def consolidate_category_comment(issue_number, category_header, content, append: false)
127
+ existing_comment = find_comment(issue_number, category_header)
128
+
129
+ if existing_comment.nil?
130
+ Aidp.log_debug("repository_client", "creating_category_comment",
131
+ issue: issue_number,
132
+ header: category_header)
133
+ return post_comment(issue_number, "#{category_header}\n\n#{content}")
134
+ end
135
+
136
+ existing_body = existing_comment[:body] || existing_comment["body"] || ""
137
+ content_without_header = existing_body.sub(/\A#{Regexp.escape(category_header)}\s*/, "").strip
138
+
139
+ new_body =
140
+ if append
141
+ Aidp.log_debug("repository_client", "appending_category_comment",
142
+ issue: issue_number,
143
+ header: category_header)
144
+ segments = [category_header, content_without_header, content].reject(&:empty?)
145
+ segments.join("\n\n")
146
+ else
147
+ Aidp.log_debug("repository_client", "replacing_category_comment",
148
+ issue: issue_number,
149
+ header: category_header)
150
+ timestamp = Time.now.utc.iso8601
151
+ archive_marker = "<!-- ARCHIVED_PLAN_START #{timestamp} ARCHIVED_PLAN_END -->"
152
+ [category_header, content, archive_marker, content_without_header].join("\n\n")
153
+ end
154
+
155
+ update_comment(existing_comment[:id] || existing_comment["id"], new_body)
156
+ rescue => e
157
+ Aidp.log_error("repository_client", "consolidate_category_comment_failed",
158
+ issue: issue_number,
159
+ header: category_header,
160
+ error: e.message)
161
+ raise "GitHub error: #{e.message}"
162
+ end
163
+
123
164
  private
124
165
 
125
166
  # Retry a GitHub CLI operation with exponential backoff
@@ -2,21 +2,6 @@
2
2
 
3
3
  require "tty-prompt"
4
4
 
5
- require_relative "../message_display"
6
- require_relative "repository_client"
7
- require_relative "repository_safety_checker"
8
- require_relative "state_store"
9
- require_relative "github_state_extractor"
10
- require_relative "plan_generator"
11
- require_relative "plan_processor"
12
- require_relative "build_processor"
13
- require_relative "../auto_update"
14
- require_relative "review_processor"
15
- require_relative "ci_fix_processor"
16
- require_relative "change_request_processor"
17
- require_relative "auto_processor"
18
- require_relative "auto_pr_processor"
19
-
20
5
  module Aidp
21
6
  module Watch
22
7
  # Coordinates the watch mode loop: monitors issues, handles plan/build
@@ -522,14 +507,21 @@ module Aidp
522
507
  return unless @auto_update_coordinator.policy.enabled
523
508
  return unless time_for_update_check?
524
509
 
510
+ @last_update_check = Time.now
525
511
  update_check = @auto_update_coordinator.check_for_update
526
512
 
527
513
  if update_check.should_update?
528
514
  display_message("🔄 Update available: #{update_check.current_version} → #{update_check.available_version}", type: :highlight)
529
- display_message(" Saving checkpoint and initiating update...", type: :muted)
530
515
 
531
- initiate_update(update_check)
532
- # Never returns - exits with code 75
516
+ # Prefer hot reloading if available (Zeitwerk enabled with reloading)
517
+ if @auto_update_coordinator.hot_reload_available?
518
+ perform_hot_reload(update_check)
519
+ else
520
+ # Fall back to checkpoint + exit approach
521
+ display_message(" Saving checkpoint and initiating update...", type: :muted)
522
+ initiate_update(update_check)
523
+ # Never returns - exits with code 75
524
+ end
533
525
  end
534
526
  rescue Aidp::AutoUpdate::UpdateLoopError => e
535
527
  # Restart loop detected - disable auto-update
@@ -540,6 +532,25 @@ module Aidp
540
532
  Aidp.log_error("watch_runner", "update_check_failed", error: e.message)
541
533
  end
542
534
 
535
+ # Perform hot code reload without restarting
536
+ # @param update_check [Aidp::AutoUpdate::UpdateCheck] Update check result
537
+ def perform_hot_reload(update_check)
538
+ display_message(" Performing hot code reload (no restart needed)...", type: :muted)
539
+
540
+ if @auto_update_coordinator.hot_reload_update(update_check)
541
+ display_message("✨ Hot reload complete! Now running #{Aidp::VERSION}", type: :success)
542
+ Aidp.log_info("watch_runner", "hot_reload_success",
543
+ version: Aidp::VERSION)
544
+ else
545
+ display_message("⚠️ Hot reload skipped (no update needed)", type: :muted)
546
+ end
547
+ rescue Aidp::AutoUpdate::UpdateError => e
548
+ display_message("⚠️ Hot reload failed: #{e.message}", type: :warning)
549
+ display_message(" Falling back to checkpoint + restart...", type: :muted)
550
+ # Fall back to cold restart
551
+ initiate_update(update_check)
552
+ end
553
+
543
554
  # Determine if it's time to check for updates
544
555
  # @return [Boolean]
545
556
  def time_for_update_check?
data/lib/aidp/watch.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "message_display"
4
- require_relative "watch/repository_client"
5
- require_relative "watch/state_store"
6
- require_relative "watch/plan_generator"
7
- require_relative "watch/plan_processor"
8
- require_relative "watch/build_processor"
9
- require_relative "watch/runner"
3
+ module Aidp
4
+ # Watch mode functionality for monitoring GitHub issues and PRs
5
+ module Watch
6
+ end
7
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "worktree"
4
- require_relative "workstream_state"
5
3
  require "open3"
6
4
  require "tty-prompt"
7
5
 
@@ -2,10 +2,6 @@
2
2
 
3
3
  require "concurrent-ruby"
4
4
  require "time"
5
- require_relative "worktree"
6
- require_relative "workstream_state"
7
- require_relative "harness/runner"
8
- require_relative "message_display"
9
5
 
10
6
  module Aidp
11
7
  # Executes multiple workstreams in parallel using concurrent-ruby.
data/lib/aidp/worktree.rb CHANGED
@@ -3,7 +3,6 @@
3
3
  require "fileutils"
4
4
  require "json"
5
5
  require "open3"
6
- require_relative "workstream_state"
7
6
 
8
7
  module Aidp
9
8
  # Manages git worktree operations for parallel workstreams.
@@ -12,6 +12,7 @@ module Aidp
12
12
  @project_dir = project_dir
13
13
  @logger = logger
14
14
  @worktree_registry_path = File.join(project_dir, ".aidp", "worktrees.json")
15
+ @pr_worktree_registry_path = File.join(project_dir, ".aidp", "pr_worktrees.json")
15
16
  end
16
17
 
17
18
  # Find an existing worktree for a given branch or PR
@@ -20,7 +21,7 @@ module Aidp
20
21
 
21
22
  raise WorktreeLookupError, "Invalid git repository: #{@project_dir}" unless git_repository?
22
23
 
23
- # Check registry first
24
+ # First, check registry first for exact branch match
24
25
  worktree_info = read_registry.find { |w| w["branch"] == branch }
25
26
 
26
27
  if worktree_info
@@ -42,6 +43,38 @@ module Aidp
42
43
  raise
43
44
  end
44
45
 
46
+ # Find or create a worktree for a PR, with advanced lookup strategies
47
+ def find_or_create_pr_worktree(pr_number:, head_branch:, base_branch: "main")
48
+ Aidp.log_debug("worktree_branch_manager", "finding_or_creating_pr_worktree",
49
+ pr_number: pr_number, head_branch: head_branch, base_branch: base_branch)
50
+
51
+ # First, check the PR-specific registry
52
+ pr_registry = read_pr_registry
53
+ pr_worktree = pr_registry.find { |w| w["pr_number"] == pr_number }
54
+
55
+ # If a valid worktree exists in the registry, return it
56
+ if pr_worktree
57
+ worktree_path = pr_worktree["path"]
58
+ return worktree_path if File.directory?(worktree_path)
59
+ end
60
+
61
+ # Attempt to find worktree by branch name
62
+ existing_worktree = find_worktree(branch: head_branch)
63
+ return existing_worktree if existing_worktree
64
+
65
+ # If no existing worktree, create a new one
66
+ worktree_path = create_worktree(branch: head_branch, base_branch: base_branch)
67
+
68
+ # Update PR-specific registry
69
+ update_pr_registry(pr_number, head_branch, worktree_path, base_branch)
70
+
71
+ worktree_path
72
+ rescue => e
73
+ Aidp.log_error("worktree_branch_manager", "pr_worktree_creation_failed",
74
+ error: e.message, pr_number: pr_number, head_branch: head_branch)
75
+ raise
76
+ end
77
+
45
78
  # Create a new worktree for a branch
46
79
  def create_worktree(branch:, base_branch: "main")
47
80
  Aidp.log_debug("worktree_branch_manager", "creating_worktree",
@@ -123,6 +156,19 @@ module Aidp
123
156
  end
124
157
  end
125
158
 
159
+ # Read the PR-specific worktree registry
160
+ def read_pr_registry
161
+ return [] unless File.exist?(@pr_worktree_registry_path)
162
+
163
+ begin
164
+ JSON.parse(File.read(@pr_worktree_registry_path))
165
+ rescue JSON::ParserError
166
+ Aidp.log_warn("worktree_branch_manager", "invalid_pr_registry",
167
+ path: @pr_worktree_registry_path)
168
+ []
169
+ end
170
+ end
171
+
126
172
  # Update the worktree registry
127
173
  def update_registry(branch, path)
128
174
  # Ensure .aidp directory exists
@@ -143,5 +189,28 @@ module Aidp
143
189
  # Write updated registry
144
190
  File.write(@worktree_registry_path, JSON.pretty_generate(registry))
145
191
  end
192
+
193
+ # Update the PR-specific worktree registry
194
+ def update_pr_registry(pr_number, head_branch, worktree_path, base_branch)
195
+ # Ensure .aidp directory exists
196
+ FileUtils.mkdir_p(File.dirname(@pr_worktree_registry_path))
197
+
198
+ registry = read_pr_registry
199
+
200
+ # Remove existing entries for the same PR
201
+ registry.reject! { |w| w["pr_number"] == pr_number }
202
+
203
+ # Add new entry
204
+ registry << {
205
+ "pr_number" => pr_number,
206
+ "head_branch" => head_branch,
207
+ "base_branch" => base_branch,
208
+ "path" => worktree_path,
209
+ "created_at" => Time.now.to_i
210
+ }
211
+
212
+ # Write updated registry
213
+ File.write(@pr_worktree_registry_path, JSON.pretty_generate(registry))
214
+ end
146
215
  end
147
216
  end
data/lib/aidp.rb CHANGED
@@ -1,115 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Core extensions
4
- require_relative "aidp/core_ext/class_attribute"
3
+ # Bootstrap: Load essential files before Zeitwerk
4
+ # These files must be loaded first and are excluded from autoloading:
5
+ # - version.rb: Needed for version checks during load
6
+ # - core_ext: Ruby core extensions that need to be available globally
7
+ # - logger.rb: Logging infrastructure used throughout loading
5
8
 
6
- # Shared modules
7
9
  require_relative "aidp/version"
8
- require_relative "aidp/config"
9
- require_relative "aidp/util"
10
- require_relative "aidp/rescue_logging"
11
- require_relative "aidp/message_display"
12
- require_relative "aidp/concurrency"
13
- require_relative "aidp/setup/wizard"
14
- require_relative "aidp/init"
15
- require_relative "aidp/watch"
16
- require_relative "aidp/cli"
17
-
18
- # Jobs and background execution
19
- require_relative "aidp/jobs/background_runner"
20
-
21
- # CLI commands
22
- require_relative "aidp/cli/jobs_command"
23
-
24
- # Providers
25
- require_relative "aidp/providers/base"
26
- require_relative "aidp/providers/cursor"
27
- require_relative "aidp/providers/anthropic"
28
- require_relative "aidp/providers/gemini"
29
- require_relative "aidp/providers/kilocode"
30
- # Supervised providers removed - using direct execution model
31
- require_relative "aidp/provider_manager"
32
-
33
- # Simple file-based storage
34
- require_relative "aidp/storage/json_storage"
35
- require_relative "aidp/storage/csv_storage"
36
- require_relative "aidp/storage/file_manager"
37
-
38
- # Analyze mode (simplified - file-based storage only)
39
- require_relative "aidp/analyze/json_file_storage"
40
- require_relative "aidp/analyze/error_handler"
41
- require_relative "aidp/analyze/ruby_maat_integration"
42
- require_relative "aidp/analyze/runner"
43
- require_relative "aidp/analyze/steps"
44
- require_relative "aidp/analyze/progress"
45
-
46
- # Tree-sitter analysis
47
- require_relative "aidp/analyze/tree_sitter_grammar_loader"
48
- require_relative "aidp/analyze/seams"
49
- require_relative "aidp/analyze/tree_sitter_scan"
50
- require_relative "aidp/analyze/kb_inspector"
51
-
52
- # Metadata system
53
- require_relative "aidp/metadata/tool_metadata"
54
- require_relative "aidp/metadata/validator"
55
- require_relative "aidp/metadata/parser"
56
- require_relative "aidp/metadata/scanner"
57
- require_relative "aidp/metadata/compiler"
58
- require_relative "aidp/metadata/query"
59
- require_relative "aidp/metadata/cache"
60
-
61
- # Workflows
62
- require_relative "aidp/workflows/definitions"
63
- require_relative "aidp/workflows/selector"
64
-
65
- # Execute mode
66
- require_relative "aidp/execute/steps"
67
- require_relative "aidp/execute/runner"
68
- require_relative "aidp/execute/progress"
69
- require_relative "aidp/execute/checkpoint"
70
- require_relative "aidp/execute/checkpoint_display"
71
- require_relative "aidp/execute/work_loop_state"
72
- require_relative "aidp/execute/instruction_queue"
73
- require_relative "aidp/execute/persistent_tasklist"
74
- require_relative "aidp/execute/async_work_loop_runner"
75
- require_relative "aidp/execute/interactive_repl"
76
-
77
- # Logging
10
+ require_relative "aidp/core_ext/class_attribute"
78
11
  require_relative "aidp/logger"
79
12
 
80
- # Daemon mode
81
- require_relative "aidp/daemon/process_manager"
82
- require_relative "aidp/daemon/runner"
83
-
84
- # Workstream/worktree management
85
- require_relative "aidp/worktree"
86
- require_relative "aidp/workstream_state"
87
- require_relative "aidp/workstream_executor"
13
+ # Now set up Zeitwerk autoloader for the rest of the codebase
14
+ require_relative "aidp/loader"
88
15
 
89
- # Harness mode
90
- require_relative "aidp/harness/configuration"
91
- require_relative "aidp/harness/config_schema"
92
- require_relative "aidp/harness/config_validator"
93
- require_relative "aidp/harness/config_loader"
94
- require_relative "aidp/harness/config_manager"
95
- require_relative "aidp/harness/ruby_llm_registry"
96
- require_relative "aidp/harness/model_registry"
97
- require_relative "aidp/harness/condition_detector"
98
- require_relative "aidp/harness/user_interface"
99
- require_relative "aidp/harness/simple_user_interface"
100
- require_relative "aidp/harness/provider_manager"
101
- require_relative "aidp/harness/provider_config"
102
- require_relative "aidp/harness/provider_factory"
103
- require_relative "aidp/harness/state_manager"
104
- require_relative "aidp/harness/error_handler"
105
- require_relative "aidp/harness/status_display"
106
- require_relative "aidp/harness/runner"
107
- require_relative "aidp/harness/filter_strategy"
108
- require_relative "aidp/harness/generic_filter_strategy"
109
- require_relative "aidp/harness/rspec_filter_strategy"
110
- require_relative "aidp/harness/output_filter"
16
+ # Configure Zeitwerk based on environment
17
+ # In watch mode or development, enable reloading for hot code updates
18
+ # In production, disable reloading and eager load for performance
19
+ reloading_enabled = ENV["AIDP_ENABLE_RELOADING"] == "1" ||
20
+ ENV["AIDP_WATCH_MODE"] == "1"
111
21
 
112
- # UI components
113
- require_relative "aidp/harness/ui/spinner_helper"
22
+ Aidp::Loader.setup(
23
+ enable_reloading: reloading_enabled,
24
+ eager_load: !reloading_enabled && ENV["AIDP_EAGER_LOAD"] == "1"
25
+ )
114
26
 
115
- # CLI commands
27
+ # Manually require files that contain multiple constants (not autoloadable by Zeitwerk)
28
+ require_relative "aidp/errors"
29
+ require_relative "aidp/auto_update/errors"
30
+ require_relative "aidp/harness/state/errors"