aidp 0.34.0 → 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 (133) 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/ui.rb +11 -0
  61. data/lib/aidp/harness/usage_limit.rb +179 -0
  62. data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
  63. data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
  64. data/lib/aidp/harness/usage_period.rb +234 -0
  65. data/lib/aidp/init/agent_instructions_generator.rb +317 -0
  66. data/lib/aidp/init/runner.rb +25 -4
  67. data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
  68. data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
  69. data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
  70. data/lib/aidp/interfaces/logger_interface.rb +150 -0
  71. data/lib/aidp/interfaces/ui_interface.rb +254 -0
  72. data/lib/aidp/logger.rb +25 -7
  73. data/lib/aidp/orchestration_adapter.rb +279 -0
  74. data/lib/aidp/pr_worktree_manager.rb +4 -15
  75. data/lib/aidp/prompts/feedback_collector.rb +198 -0
  76. data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
  77. data/lib/aidp/prompts/template_evolver.rb +305 -0
  78. data/lib/aidp/prompts/template_version_manager.rb +325 -0
  79. data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
  80. data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
  81. data/lib/aidp/setup/wizard.rb +616 -176
  82. data/lib/aidp/shell_executor.rb +58 -0
  83. data/lib/aidp/style_guide/selector.rb +56 -1
  84. data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
  85. data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
  86. data/lib/aidp/temporal/activities/base_activity.rb +78 -0
  87. data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
  88. data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
  89. data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
  90. data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
  91. data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
  92. data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
  93. data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
  94. data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
  95. data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
  96. data/lib/aidp/temporal/configuration.rb +169 -0
  97. data/lib/aidp/temporal/connection.rb +93 -0
  98. data/lib/aidp/temporal/worker.rb +119 -0
  99. data/lib/aidp/temporal/workflow_client.rb +171 -0
  100. data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
  101. data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
  102. data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
  103. data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
  104. data/lib/aidp/temporal.rb +120 -0
  105. data/lib/aidp/version.rb +1 -1
  106. data/lib/aidp/watch/base_processor.rb +35 -0
  107. data/lib/aidp/watch/build_processor.rb +132 -0
  108. data/lib/aidp/watch/github_state_extractor.rb +0 -8
  109. data/lib/aidp/watch/rebase_processor.rb +245 -0
  110. data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
  111. data/lib/aidp/watch/runner.rb +366 -285
  112. data/lib/aidp/watch/state_store.rb +80 -0
  113. data/lib/aidp/watch/work_item.rb +96 -0
  114. data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
  115. data/lib/aidp/watch/worktree_reconciler.rb +643 -0
  116. data/lib/aidp/workflows/guided_agent.rb +1 -1
  117. data/lib/aidp/worktree.rb +62 -0
  118. data/templates/aidp.yml.example +70 -0
  119. metadata +118 -16
  120. data/lib/aidp/analyze/json_file_storage.rb +0 -292
  121. data/lib/aidp/execute/future_work_backlog.rb +0 -411
  122. data/lib/aidp/providers/adapter.rb +0 -239
  123. data/lib/aidp/providers/aider.rb +0 -264
  124. data/lib/aidp/providers/anthropic.rb +0 -614
  125. data/lib/aidp/providers/base.rb +0 -617
  126. data/lib/aidp/providers/capability_registry.rb +0 -204
  127. data/lib/aidp/providers/codex.rb +0 -267
  128. data/lib/aidp/providers/cursor.rb +0 -304
  129. data/lib/aidp/providers/error_taxonomy.rb +0 -195
  130. data/lib/aidp/providers/gemini.rb +0 -110
  131. data/lib/aidp/providers/github_copilot.rb +0 -227
  132. data/lib/aidp/providers/kilocode.rb +0 -157
  133. data/lib/aidp/providers/opencode.rb +0 -139
@@ -1,239 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aidp
4
- module Providers
5
- # ProviderAdapter defines the standardized interface that all provider implementations
6
- # must conform to. This ensures consistent behavior across different AI model providers
7
- # while allowing for provider-specific implementations.
8
- #
9
- # Design Philosophy:
10
- # - Adapters are stateless; delegate throttling, retries, and escalation to coordinator
11
- # - Store provider-specific regex matchers adjacent to adapters for maintainability
12
- # - Single semantic flags map to provider-specific equivalents
13
- #
14
- # @see https://github.com/viamin/aidp/issues/243
15
- module Adapter
16
- # Core interface methods that all providers must implement
17
-
18
- # Provider identifier (e.g., "anthropic", "cursor", "gemini")
19
- # @return [String] unique lowercase identifier for this provider
20
- def name
21
- raise NotImplementedError, "#{self.class} must implement #name"
22
- end
23
-
24
- # Human-friendly display name for UI
25
- # @return [String] display name (e.g., "Anthropic Claude", "Cursor AI")
26
- def display_name
27
- name
28
- end
29
-
30
- # Send a message to the provider and get a response
31
- # @param prompt [String] the prompt to send
32
- # @param session [String, nil] optional session identifier for context
33
- # @param options [Hash] additional options for the request
34
- # @return [Hash, String] provider response
35
- def send_message(prompt:, session: nil, **options)
36
- raise NotImplementedError, "#{self.class} must implement #send_message"
37
- end
38
-
39
- # Capability declaration methods
40
-
41
- # Check if the provider supports Model Context Protocol
42
- # @return [Boolean] true if MCP is supported
43
- def supports_mcp?
44
- false
45
- end
46
-
47
- # Fetch MCP servers configured for this provider
48
- # @return [Array<Hash>] array of MCP server configurations
49
- def fetch_mcp_servers
50
- []
51
- end
52
-
53
- # Check if the provider is available on this system
54
- # @return [Boolean] true if provider CLI or API is accessible
55
- def available?
56
- true
57
- end
58
-
59
- # Declare provider capabilities
60
- # @return [Hash] capabilities hash with feature flags
61
- # @example
62
- # {
63
- # reasoning_tiers: ["mini", "standard", "thinking"],
64
- # context_window: 200_000,
65
- # supports_json_mode: true,
66
- # supports_tool_use: true,
67
- # supports_vision: false,
68
- # supports_file_upload: true
69
- # }
70
- def capabilities
71
- {
72
- reasoning_tiers: [],
73
- context_window: 100_000,
74
- supports_json_mode: false,
75
- supports_tool_use: false,
76
- supports_vision: false,
77
- supports_file_upload: false
78
- }
79
- end
80
-
81
- # Dangerous permissions abstraction
82
-
83
- # Check if the provider supports dangerous/elevated permissions mode
84
- # @return [Boolean] true if dangerous mode is supported
85
- def supports_dangerous_mode?
86
- false
87
- end
88
-
89
- # Get the provider-specific flag(s) for enabling dangerous mode
90
- # Maps the semantic `dangerous: true` flag to provider-specific equivalents
91
- # @return [Array<String>] provider-specific CLI flags
92
- # @example Anthropic
93
- # ["--dangerously-skip-permissions"]
94
- # @example Gemini (hypothetical)
95
- # ["--yolo"]
96
- def dangerous_mode_flags
97
- []
98
- end
99
-
100
- # Check if dangerous mode is currently enabled
101
- # @return [Boolean] true if dangerous mode is active
102
- def dangerous_mode_enabled?
103
- @dangerous_mode_enabled ||= false
104
- end
105
-
106
- # Enable or disable dangerous mode
107
- # @param enabled [Boolean] whether to enable dangerous mode
108
- # @return [void]
109
- def dangerous_mode=(enabled)
110
- @dangerous_mode_enabled = enabled
111
- end
112
-
113
- # Error classification and handling
114
-
115
- # Get error classification regex patterns for this provider
116
- # @return [Hash<Symbol, Array<Regexp>>] mapping of error categories to regex patterns
117
- # @example
118
- # {
119
- # rate_limited: [/rate.?limit/i, /quota.*exceeded/i],
120
- # auth_expired: [/authentication.*failed/i, /invalid.*api.*key/i],
121
- # quota_exceeded: [/quota.*exceeded/i, /usage.*limit/i],
122
- # transient: [/timeout/i, /connection.*reset/i, /temporary.*error/i],
123
- # permanent: [/invalid.*model/i, /unsupported.*operation/i]
124
- # }
125
- def error_patterns
126
- {}
127
- end
128
-
129
- # Classify an error into the standardized error taxonomy
130
- # @param error [StandardError] the error to classify
131
- # @return [Symbol] error category (:rate_limited, :auth_expired, :quota_exceeded, :transient, :permanent)
132
- def classify_error(error)
133
- message = error.message.to_s
134
-
135
- # First check provider-specific patterns
136
- error_patterns.each do |category, patterns|
137
- patterns.each do |pattern|
138
- return category if message.match?(pattern)
139
- end
140
- end
141
-
142
- # Fall back to ErrorTaxonomy for classification
143
- require_relative "error_taxonomy"
144
- Aidp::Providers::ErrorTaxonomy.classify_message(message)
145
- end
146
-
147
- # Get normalized error metadata
148
- # @param error [StandardError] the error to process
149
- # @return [Hash] normalized error information
150
- def error_metadata(error)
151
- {
152
- provider: name,
153
- error_category: classify_error(error),
154
- error_class: error.class.name,
155
- message: redact_secrets(error.message),
156
- timestamp: Time.now.iso8601,
157
- retryable: retryable_error?(error)
158
- }
159
- end
160
-
161
- # Check if an error is retryable
162
- # @param error [StandardError] the error to check
163
- # @return [Boolean] true if the error should be retried
164
- def retryable_error?(error)
165
- category = classify_error(error)
166
- [:transient].include?(category)
167
- end
168
-
169
- # Logging and metrics
170
-
171
- # Get logging metadata for this provider
172
- # @return [Hash] metadata for structured logging
173
- def logging_metadata
174
- {
175
- provider: name,
176
- display_name: display_name,
177
- supports_mcp: supports_mcp?,
178
- available: available?,
179
- dangerous_mode: dangerous_mode_enabled?
180
- }
181
- end
182
-
183
- # Redact secrets from log messages
184
- # @param message [String] message potentially containing secrets
185
- # @return [String] message with secrets redacted
186
- def redact_secrets(message)
187
- # Redact common secret patterns
188
- message = message.gsub(/api[_-]?key[:\s=]+[^\s&]+/i, "api_key=[REDACTED]")
189
- message = message.gsub(/token[:\s=]+[^\s&]+/i, "token=[REDACTED]")
190
- message = message.gsub(/password[:\s=]+[^\s&]+/i, "password=[REDACTED]")
191
- message = message.gsub(/bearer\s+[^\s&]+/i, "bearer [REDACTED]")
192
- message.gsub(/sk-[a-zA-Z0-9_-]{20,}/i, "sk-[REDACTED]")
193
- end
194
-
195
- # Configuration validation
196
-
197
- # Validate provider configuration
198
- # @param config [Hash] configuration to validate
199
- # @return [Hash] validation result with :valid, :errors, :warnings keys
200
- def validate_config(config)
201
- errors = []
202
- warnings = []
203
-
204
- # Validate required fields
205
- unless config[:type]
206
- errors << "Provider type is required"
207
- end
208
-
209
- unless ["usage_based", "subscription", "passthrough"].include?(config[:type])
210
- errors << "Provider type must be one of: usage_based, subscription, passthrough"
211
- end
212
-
213
- # Validate models if present
214
- if config[:models] && !config[:models].is_a?(Array)
215
- errors << "Models must be an array"
216
- end
217
-
218
- {
219
- valid: errors.empty?,
220
- errors: errors,
221
- warnings: warnings
222
- }
223
- end
224
-
225
- # Provider health and status
226
-
227
- # Check provider health
228
- # @return [Hash] health status information
229
- def health_status
230
- {
231
- provider: name,
232
- available: available?,
233
- healthy: available?,
234
- timestamp: Time.now.iso8601
235
- }
236
- end
237
- end
238
- end
239
- end
@@ -1,264 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "timeout"
4
- require_relative "base"
5
- require_relative "../util"
6
- require_relative "../debug_mixin"
7
-
8
- module Aidp
9
- module Providers
10
- class Aider < Base
11
- include Aidp::DebugMixin
12
-
13
- # Model name pattern for Aider (supports various models via OpenRouter)
14
- # Aider can use any model, but we'll match common patterns
15
- MODEL_PATTERN = /^(gpt-|claude-|gemini-|deepseek-|qwen-|o1-)/i
16
- LONG_PROMPT_THRESHOLD = 8000
17
- LONG_PROMPT_TIMEOUT = 900 # 15 minutes for large prompts
18
-
19
- def self.available?
20
- !!Aidp::Util.which("aider")
21
- end
22
-
23
- # Check if this provider supports a given model family
24
- #
25
- # @param family_name [String] The model family name
26
- # @return [Boolean] True if it matches common model patterns
27
- def self.supports_model_family?(family_name)
28
- MODEL_PATTERN.match?(family_name)
29
- end
30
-
31
- # Discover available models from registry
32
- #
33
- # Note: Aider uses its own configuration for models
34
- # Returns registry-based models that match common patterns
35
- #
36
- # @return [Array<Hash>] Array of discovered models
37
- def self.discover_models
38
- return [] unless available?
39
-
40
- discover_models_from_registry(MODEL_PATTERN, "aider")
41
- end
42
-
43
- # Get firewall requirements for Aider provider
44
- # Aider uses aider.chat for updates, openrouter.ai for API access,
45
- # and pypi.org for version checking
46
- def self.firewall_requirements
47
- {
48
- domains: [
49
- "aider.chat",
50
- "openrouter.ai",
51
- "api.openrouter.ai",
52
- "pypi.org"
53
- ],
54
- ip_ranges: []
55
- }
56
- end
57
-
58
- def name
59
- "aider"
60
- end
61
-
62
- def display_name
63
- "Aider"
64
- end
65
-
66
- def available?
67
- return false unless self.class.available?
68
-
69
- # Additional check to ensure the CLI is properly configured
70
- begin
71
- result = Aidp::Util.execute_command("aider", ["--version"], timeout: 10)
72
- result.exit_status == 0
73
- rescue
74
- false
75
- end
76
- end
77
-
78
- def send_message(prompt:, session: nil, options: {})
79
- raise "aider CLI not available" unless self.class.available?
80
-
81
- # Smart timeout calculation (store prompt length for adaptive logic)
82
- @current_aider_prompt_length = prompt.length
83
- timeout_seconds = calculate_timeout
84
-
85
- debug_provider("aider", "Starting execution", {timeout: timeout_seconds})
86
- debug_log("šŸ“ Sending prompt to aider (length: #{prompt.length})", level: :info)
87
-
88
- # Set up activity monitoring
89
- setup_activity_monitoring("aider", method(:activity_callback))
90
- record_activity("Starting aider execution")
91
-
92
- # Create a spinner for activity display
93
- spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
94
- spinner.auto_spin
95
-
96
- activity_display_thread = Thread.new do
97
- start_time = Time.now
98
- loop do
99
- sleep 0.5 # Update every 500ms to reduce spam
100
- elapsed = Time.now - start_time
101
-
102
- # Break if we've been running too long or state changed
103
- break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
104
-
105
- update_spinner_status(spinner, elapsed, "šŸ¤– Aider")
106
- end
107
- end
108
-
109
- begin
110
- # Use non-interactive mode with --yes-always flag and --message
111
- # --yes-always is equivalent to Claude's --dangerously-skip-permissions
112
- args = ["--yes-always", "--message", prompt]
113
-
114
- # Disable aider's auto-commits by default - let AIDP handle commits
115
- # based on work_loop.version_control.behavior configuration
116
- args += ["--no-auto-commits"]
117
-
118
- # Add model if configured
119
- if @model && !@model.empty?
120
- args += ["--model", @model]
121
- end
122
-
123
- # Add session support if provided (aider supports chat history)
124
- if session && !session.empty?
125
- args += ["--restore-chat-history"]
126
- end
127
-
128
- # In devcontainer, aider should run in non-interactive mode
129
- if in_devcontainer_or_codespace?
130
- debug_log("šŸ”“ Running aider in non-interactive mode with --yes-always (devcontainer)", level: :info)
131
- end
132
-
133
- # Use debug_execute_command for better debugging
134
- result = debug_execute_command("aider", args: args, timeout: timeout_seconds)
135
-
136
- # Log the results
137
- debug_command("aider", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
138
-
139
- if result.exit_status == 0
140
- spinner.success("āœ“")
141
- mark_completed
142
- result.out
143
- else
144
- spinner.error("āœ—")
145
- mark_failed("aider failed with exit code #{result.exit_status}")
146
- debug_error(StandardError.new("aider failed"), {exit_code: result.exit_status, stderr: result.err})
147
- raise "aider failed with exit code #{result.exit_status}: #{result.err}"
148
- end
149
- rescue => e
150
- spinner&.error("āœ—")
151
- mark_failed("aider execution failed: #{e.message}")
152
- debug_error(e, {provider: "aider", prompt_length: prompt.length})
153
- raise
154
- ensure
155
- cleanup_activity_display(activity_display_thread, spinner)
156
- @current_aider_prompt_length = nil
157
- end
158
- end
159
-
160
- # Enhanced send method with additional options
161
- def send_with_options(prompt:, session: nil, model: nil, auto_commits: false)
162
- args = ["--yes-always", "--message", prompt]
163
-
164
- # Disable auto-commits by default (let AIDP handle commits)
165
- # unless explicitly enabled via auto_commits parameter
166
- args += if auto_commits
167
- ["--auto-commits"]
168
- else
169
- ["--no-auto-commits"]
170
- end
171
-
172
- # Add session support
173
- if session && !session.empty?
174
- args += ["--restore-chat-history"]
175
- end
176
-
177
- # Add model selection (from parameter or configured model)
178
- model_to_use = model || @model
179
- if model_to_use
180
- args += ["--model", model_to_use]
181
- end
182
-
183
- # Use the enhanced version of send
184
- send_with_custom_args(prompt: prompt, args: args)
185
- end
186
-
187
- # Override health check for Aider specific considerations
188
- def harness_healthy?
189
- return false unless super
190
-
191
- # Additional health checks specific to Aider
192
- # Check if we can access the CLI (basic connectivity test)
193
- begin
194
- result = Aidp::Util.execute_command("aider", ["--help"], timeout: 5)
195
- result.exit_status == 0
196
- rescue
197
- false
198
- end
199
- end
200
-
201
- private
202
-
203
- # Internal helper for send_with_options - executes with custom arguments
204
- def send_with_custom_args(prompt:, args:)
205
- @current_aider_prompt_length = prompt.length
206
- timeout_seconds = calculate_timeout
207
-
208
- debug_provider("aider", "Starting execution", {timeout: timeout_seconds, args: args})
209
- debug_log("šŸ“ Sending prompt to aider with custom args", level: :info)
210
-
211
- setup_activity_monitoring("aider", method(:activity_callback))
212
- record_activity("Starting aider execution with custom args")
213
-
214
- begin
215
- result = debug_execute_command("aider", args: args, timeout: timeout_seconds)
216
- debug_command("aider", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
217
-
218
- if result.exit_status == 0
219
- mark_completed
220
- result.out
221
- else
222
- mark_failed("aider failed with exit code #{result.exit_status}")
223
- debug_error(StandardError.new("aider failed"), {exit_code: result.exit_status, stderr: result.err})
224
- raise "aider failed with exit code #{result.exit_status}: #{result.err}"
225
- end
226
- rescue => e
227
- mark_failed("aider execution failed: #{e.message}")
228
- debug_error(e, {provider: "aider", prompt_length: prompt.length})
229
- raise
230
- ensure
231
- @current_aider_prompt_length = nil
232
- end
233
- end
234
-
235
- def activity_callback(state, message, provider)
236
- # Handle activity state changes
237
- case state
238
- when :stuck
239
- display_message("\nāš ļø Aider appears stuck: #{message}", type: :warning)
240
- when :completed
241
- display_message("\nāœ… Aider completed: #{message}", type: :success)
242
- when :failed
243
- display_message("\nāŒ Aider failed: #{message}", type: :error)
244
- end
245
- end
246
-
247
- def calculate_timeout
248
- env_override = ENV["AIDP_AIDER_TIMEOUT"]
249
- return env_override.to_i if env_override&.match?(/^\d+$/)
250
-
251
- base_timeout = super
252
-
253
- prompt_length = @current_aider_prompt_length
254
- return base_timeout unless prompt_length && prompt_length >= LONG_PROMPT_THRESHOLD
255
-
256
- extended_timeout = [base_timeout, LONG_PROMPT_TIMEOUT].max
257
- if extended_timeout > base_timeout
258
- display_message("ā±ļø Aider prompt length #{prompt_length} detected - extending timeout to #{extended_timeout} seconds", type: :info)
259
- end
260
- extended_timeout
261
- end
262
- end
263
- end
264
- end