Class: Aidp::Watch::ChangeRequestProcessor
- Inherits:
-
Object
- Object
- Aidp::Watch::ChangeRequestProcessor
- Includes:
- MessageDisplay
- Defined in:
- lib/aidp/watch/change_request_processor.rb
Overview
Handles the aidp-request-changes label trigger by analyzing PR comments and automatically implementing the requested changes.
Constant Summary collapse
- DEFAULT_CHANGE_REQUEST_LABEL =
Default label names
"aidp-request-changes"- DEFAULT_NEEDS_INPUT_LABEL =
"aidp-needs-input"- COMMENT_HEADER =
"## đ¤ AIDP Change Request"- MAX_CLARIFICATION_ROUNDS =
3
Constants included from MessageDisplay
MessageDisplay::COLOR_MAP, MessageDisplay::CRITICAL_TYPES
Instance Attribute Summary collapse
-
#change_request_label ⇒ Object
readonly
Returns the value of attribute change_request_label.
-
#needs_input_label ⇒ Object
readonly
Returns the value of attribute needs_input_label.
-
#project_dir ⇒ Object
Expose state for testability.
-
#worktree_branch_manager ⇒ Object
Expose state for testability.
Instance Method Summary collapse
-
#initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, change_request_config: {}, safety_config: {}, verbose: false) ⇒ ChangeRequestProcessor
constructor
A new instance of ChangeRequestProcessor.
- #process(pr) ⇒ Object
Methods included from MessageDisplay
#display_message, included, #message_display_prompt, #quiet_mode?
Constructor Details
#initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, change_request_config: {}, safety_config: {}, verbose: false) ⇒ ChangeRequestProcessor
Returns a new instance of ChangeRequestProcessor.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/aidp/watch/change_request_processor.rb', line 40 def initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, change_request_config: {}, safety_config: {}, verbose: false) @repository_client = repository_client @state_store = state_store @state_extractor = GitHubStateExtractor.new(repository_client: repository_client) @provider_name = provider_name @project_dir = project_dir @verbose = verbose # Initialize worktree managers @pr_worktree_manager = Aidp::PRWorktreeManager.new(project_dir: project_dir) @worktree_branch_manager = Aidp::WorktreeBranchManager.new(project_dir: project_dir) # Initialize verifier @verifier = ImplementationVerifier.new( repository_client: repository_client, project_dir: project_dir ) # Load label configuration @change_request_label = label_config[:change_request_trigger] || label_config["change_request_trigger"] || DEFAULT_CHANGE_REQUEST_LABEL @needs_input_label = label_config[:needs_input] || label_config["needs_input"] || DEFAULT_NEEDS_INPUT_LABEL # Load change request configuration @config = { enabled: true, allow_multi_file_edits: true, run_tests_before_push: true, commit_message_prefix: "aidp: pr-change", require_comment_reference: true, max_diff_size: 2000, large_pr_strategy: "create_worktree", # Options: create_worktree, manual, skip worktree_strategy: "auto", # Options: auto, always_create, reuse_only worktree_max_age: 7 * 24 * 60 * 60, # 7 days in seconds worktree_cleanup_on_success: true, worktree_cleanup_on_failure: false }.merge(symbolize_keys(change_request_config)) # Log the configuration, especially the large PR strategy Aidp.log_debug( "change_request_processor", "Initialized with config", max_diff_size: @config[:max_diff_size], large_pr_strategy: @config[:large_pr_strategy], run_tests_before_push: @config[:run_tests_before_push], enabled: @config[:enabled], worktree_strategy: @config[:worktree_strategy], worktree_max_age: @config[:worktree_max_age], worktree_cleanup_on_success: @config[:worktree_cleanup_on_success], worktree_cleanup_on_failure: @config[:worktree_cleanup_on_failure] ) # Load safety configuration @safety_config = safety_config = Array(@safety_config[:author_allowlist] || @safety_config["author_allowlist"]) end |
Instance Attribute Details
#change_request_label ⇒ Object (readonly)
Returns the value of attribute change_request_label.
35 36 37 |
# File 'lib/aidp/watch/change_request_processor.rb', line 35 def change_request_label @change_request_label end |
#needs_input_label ⇒ Object (readonly)
Returns the value of attribute needs_input_label.
35 36 37 |
# File 'lib/aidp/watch/change_request_processor.rb', line 35 def needs_input_label @needs_input_label end |
#project_dir ⇒ Object
Expose state for testability
38 39 40 |
# File 'lib/aidp/watch/change_request_processor.rb', line 38 def project_dir @project_dir end |
#worktree_branch_manager ⇒ Object
Expose state for testability
38 39 40 |
# File 'lib/aidp/watch/change_request_processor.rb', line 38 def worktree_branch_manager @worktree_branch_manager end |
Instance Method Details
#process(pr) ⇒ Object
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/aidp/watch/change_request_processor.rb', line 95 def process(pr) number = pr[:number] Aidp.log_debug( "change_request_processor", "Starting change request processing", pr_number: number, pr_title: pr[:title] ) unless @config[:enabled] ( "âšī¸ PR change requests are disabled in configuration. Skipping PR ##{number}.", type: :muted ) return end # Check clarification round limit existing_data = @state_store.change_request_data(number) if existing_data && existing_data["clarification_count"].to_i >= MAX_CLARIFICATION_ROUNDS ( "â ī¸ Max clarification rounds (#{MAX_CLARIFICATION_ROUNDS}) reached for PR ##{number}. Skipping.", type: :warn ) post_max_rounds_comment(pr) return end ( "đ Processing change request for PR ##{number} (#{pr[:title]})", type: :info ) # Fetch PR details pr_data = @repository_client.fetch_pull_request(number) comments = @repository_client.fetch_pr_comments(number) # Filter comments from authorized users = (comments, pr_data) if .empty? ( "âšī¸ No authorized comments found for PR ##{number}. Skipping.", type: :muted ) return end # Fetch diff to check size with enhanced strategy diff = @repository_client.fetch_pull_request_diff(number) diff_size = diff.lines.count # Enhanced diff size and worktree handling large_pr = diff_size > @config[:max_diff_size] if large_pr # Comprehensive logging for large PR detection Aidp.log_debug( "change_request_processor", "Large PR detected", pr_number: number, diff_size: diff_size, max_diff_size: @config[:max_diff_size], large_pr_strategy: @config[:large_pr_strategy] ) ( "â ī¸ Large PR detected - applying enhanced worktree handling strategy.", type: :info ) # Handle different strategies for large PRs case @config[:large_pr_strategy] when "skip" Aidp.log_debug( "change_request_processor", "Skipping large PR processing", pr_number: number ) post_diff_too_large_comment(pr_data, diff_size) return when "manual" post_diff_too_large_comment(pr_data, diff_size) ("â Change request processing failed: Large PR requires manual processing. See comment for details.", type: :error) @state_store.record_change_request(pr_data[:number], { status: "manual_processing_required", timestamp: Time.now.utc.iso8601, diff_size: diff_size, max_diff_size: @config[:max_diff_size] }) raise "Large PR requires manual processing. See comment for details." when "create_worktree" # Use our enhanced WorktreeBranchManager to handle PR worktrees begin # Get PR branch information head_ref = @worktree_branch_manager.get_pr_branch(number) # Check if a PR-specific worktree already exists Aidp.log_debug( "change_request_processor", "Checking for existing PR worktree", pr_number: number, head_branch: head_ref ) # Try to find existing worktree first existing_worktree = @worktree_branch_manager.find_worktree( branch: head_ref, pr_number: number ) # Create new worktree if none exists if existing_worktree.nil? Aidp.log_info( "change_request_processor", "Creating PR-specific worktree", pr_number: number, head_branch: head_ref, base_branch: pr_data[:base_ref], strategy: "create_worktree" ) # Use find_or_create_pr_worktree for PR-specific handling @worktree_branch_manager.find_or_create_pr_worktree( pr_number: number, head_branch: head_ref, base_branch: pr_data[:base_ref] ) else Aidp.log_debug( "change_request_processor", "Using existing PR worktree", pr_number: number, worktree_path: existing_worktree ) end rescue => e Aidp.log_error( "change_request_processor", "Large PR worktree handling failed", pr_number: number, error: e., strategy: @config[:large_pr_strategy] ) # Fallback error handling post_diff_too_large_comment(pr_data, diff_size) raise "Failed to handle large PR: #{e.message}" end else # Default fallback Aidp.log_warn( "change_request_processor", "Unknown large_pr_strategy", strategy: @config[:large_pr_strategy], fallback: "skip" ) post_diff_too_large_comment(pr_data, diff_size) return end # Provide additional context via debug log Aidp.log_info( "change_request_processor", "Large PR worktree strategy applied", pr_number: number, diff_size: diff_size, max_diff_size: @config[:max_diff_size], strategy: @config[:large_pr_strategy] ) end # Analyze change requests analysis_result = analyze_change_requests( pr_data: pr_data, comments: , diff: diff ) if analysis_result[:needs_clarification] handle_clarification_needed(pr: pr_data, analysis: analysis_result) elsif analysis_result[:can_implement] implement_changes(pr: pr_data, analysis: analysis_result, diff: diff) else handle_cannot_implement(pr: pr_data, analysis: analysis_result) end rescue => e ( "â Change request processing failed: #{e.message}", type: :error ) Aidp.log_error( "change_request_processor", "Change request failed", pr: pr[:number], error: e., backtrace: e.backtrace&.first(10), error_class: e.class.name ) # Record failure state internally but DON'T post error to GitHub # (per issue #280 - error messages should never appear on issues) @state_store.record_change_request(pr[:number], { status: "error", error: e., error_class: e.class.name, timestamp: Time.now.utc.iso8601 }) end |