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.
- checksums.yaml +4 -4
- data/README.md +14 -14
- data/lib/aidp/agent_harness_adapter.rb +309 -0
- data/lib/aidp/cli/issue_importer.rb +20 -6
- data/lib/aidp/cli/jobs_command.rb +1 -1
- data/lib/aidp/cli/models_command.rb +9 -16
- data/lib/aidp/cli/prompts_command.rb +253 -0
- data/lib/aidp/cli/security_command.rb +4 -10
- data/lib/aidp/cli/storage_command.rb +277 -0
- data/lib/aidp/cli/temporal_command.rb +460 -0
- data/lib/aidp/cli.rb +24 -2
- data/lib/aidp/config.rb +15 -0
- data/lib/aidp/config_paths.rb +12 -2
- data/lib/aidp/database/migrations.rb +114 -0
- data/lib/aidp/database/repositories/checkpoint_repository.rb +174 -0
- data/lib/aidp/database/repositories/deprecated_models_repository.rb +206 -0
- data/lib/aidp/database/repositories/evaluation_repository.rb +208 -0
- data/lib/aidp/database/repositories/harness_state_repository.rb +140 -0
- data/lib/aidp/database/repositories/job_repository.rb +278 -0
- data/lib/aidp/database/repositories/model_cache_repository.rb +194 -0
- data/lib/aidp/database/repositories/progress_repository.rb +210 -0
- data/lib/aidp/database/repositories/prompt_archive_repository.rb +195 -0
- data/lib/aidp/database/repositories/prompt_feedback_repository.rb +266 -0
- data/lib/aidp/database/repositories/provider_info_cache_repository.rb +208 -0
- data/lib/aidp/database/repositories/provider_metrics_repository.rb +245 -0
- data/lib/aidp/database/repositories/secrets_repository.rb +187 -0
- data/lib/aidp/database/repositories/task_repository.rb +231 -0
- data/lib/aidp/database/repositories/template_version_repository.rb +383 -0
- data/lib/aidp/database/repositories/watch_state_repository.rb +472 -0
- data/lib/aidp/database/repositories/workstream_repository.rb +310 -0
- data/lib/aidp/database/repositories/worktree_repository.rb +240 -0
- data/lib/aidp/database/repository.rb +166 -0
- data/lib/aidp/database/schema.rb +347 -0
- data/lib/aidp/database/storage_migrator.rb +693 -0
- data/lib/aidp/database.rb +139 -0
- data/lib/aidp/debug_mixin.rb +2 -2
- data/lib/aidp/execute/checkpoint.rb +26 -0
- data/lib/aidp/execute/checkpoint_display.rb +16 -0
- data/lib/aidp/execute/prompt_evaluator.rb +30 -57
- data/lib/aidp/execute/work_loop_runner.rb +294 -46
- data/lib/aidp/firewall/provider_requirements_collector.rb +7 -23
- data/lib/aidp/harness/agent_harness_provider_manager.rb +275 -0
- data/lib/aidp/harness/ai_decision_engine.rb +78 -296
- data/lib/aidp/harness/config_schema.rb +59 -0
- data/lib/aidp/harness/configuration.rb +229 -10
- data/lib/aidp/harness/deprecation_cache.rb +53 -11
- data/lib/aidp/harness/enhanced_runner.rb +1 -1
- data/lib/aidp/harness/error_handler.rb +11 -5
- data/lib/aidp/harness/provider_config.rb +36 -0
- data/lib/aidp/harness/provider_factory.rb +26 -24
- data/lib/aidp/harness/provider_info.rb +9 -5
- data/lib/aidp/harness/provider_manager.rb +2 -2
- data/lib/aidp/harness/provider_metrics.rb +103 -1
- data/lib/aidp/harness/rspec_command_optimizer.rb +166 -0
- data/lib/aidp/harness/ruby_llm_registry.rb +97 -8
- data/lib/aidp/harness/runner.rb +7 -2
- data/lib/aidp/harness/state_manager.rb +1 -1
- data/lib/aidp/harness/test_runner.rb +167 -11
- data/lib/aidp/harness/thinking_depth_manager.rb +564 -5
- data/lib/aidp/harness/ui.rb +11 -0
- data/lib/aidp/harness/usage_limit.rb +179 -0
- data/lib/aidp/harness/usage_limit_enforcer.rb +240 -0
- data/lib/aidp/harness/usage_limit_tracker.rb +308 -0
- data/lib/aidp/harness/usage_period.rb +234 -0
- data/lib/aidp/init/agent_instructions_generator.rb +317 -0
- data/lib/aidp/init/runner.rb +25 -4
- data/lib/aidp/interfaces/activity_monitor_interface.rb +234 -0
- data/lib/aidp/interfaces/binary_checker_interface.rb +232 -0
- data/lib/aidp/interfaces/command_executor_interface.rb +216 -0
- data/lib/aidp/interfaces/logger_interface.rb +150 -0
- data/lib/aidp/interfaces/ui_interface.rb +254 -0
- data/lib/aidp/logger.rb +25 -7
- data/lib/aidp/orchestration_adapter.rb +279 -0
- data/lib/aidp/pr_worktree_manager.rb +4 -15
- data/lib/aidp/prompts/feedback_collector.rb +198 -0
- data/lib/aidp/prompts/prompt_template_manager.rb +415 -0
- data/lib/aidp/prompts/template_evolver.rb +305 -0
- data/lib/aidp/prompts/template_version_manager.rb +325 -0
- data/lib/aidp/setup/in_memory_config_adapter.rb +257 -0
- data/lib/aidp/setup/in_memory_config_manager.rb +262 -0
- data/lib/aidp/setup/wizard.rb +616 -176
- data/lib/aidp/shell_executor.rb +58 -0
- data/lib/aidp/style_guide/selector.rb +56 -1
- data/lib/aidp/temporal/activities/analyze_issue_activity.rb +198 -0
- data/lib/aidp/temporal/activities/analyze_sub_task_activity.rb +163 -0
- data/lib/aidp/temporal/activities/base_activity.rb +78 -0
- data/lib/aidp/temporal/activities/create_plan_activity.rb +115 -0
- data/lib/aidp/temporal/activities/create_pr_activity.rb +193 -0
- data/lib/aidp/temporal/activities/create_prompt_activity.rb +118 -0
- data/lib/aidp/temporal/activities/diagnose_failure_activity.rb +228 -0
- data/lib/aidp/temporal/activities/prepare_next_iteration_activity.rb +147 -0
- data/lib/aidp/temporal/activities/record_checkpoint_activity.rb +81 -0
- data/lib/aidp/temporal/activities/run_agent_activity.rb +139 -0
- data/lib/aidp/temporal/activities/run_tests_activity.rb +114 -0
- data/lib/aidp/temporal/activities/run_work_loop_iteration_activity.rb +249 -0
- data/lib/aidp/temporal/configuration.rb +169 -0
- data/lib/aidp/temporal/connection.rb +93 -0
- data/lib/aidp/temporal/worker.rb +119 -0
- data/lib/aidp/temporal/workflow_client.rb +171 -0
- data/lib/aidp/temporal/workflows/base_workflow.rb +73 -0
- data/lib/aidp/temporal/workflows/issue_to_pr_workflow.rb +278 -0
- data/lib/aidp/temporal/workflows/sub_issue_workflow.rb +253 -0
- data/lib/aidp/temporal/workflows/work_loop_workflow.rb +321 -0
- data/lib/aidp/temporal.rb +120 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/base_processor.rb +35 -0
- data/lib/aidp/watch/build_processor.rb +132 -0
- data/lib/aidp/watch/github_state_extractor.rb +0 -8
- data/lib/aidp/watch/rebase_processor.rb +245 -0
- data/lib/aidp/watch/round_robin_scheduler.rb +150 -0
- data/lib/aidp/watch/runner.rb +366 -285
- data/lib/aidp/watch/state_store.rb +80 -0
- data/lib/aidp/watch/work_item.rb +96 -0
- data/lib/aidp/watch/worktree_cleanup_job.rb +226 -0
- data/lib/aidp/watch/worktree_reconciler.rb +643 -0
- data/lib/aidp/workflows/guided_agent.rb +1 -1
- data/lib/aidp/worktree.rb +62 -0
- data/templates/aidp.yml.example +70 -0
- metadata +118 -16
- data/lib/aidp/analyze/json_file_storage.rb +0 -292
- data/lib/aidp/execute/future_work_backlog.rb +0 -411
- data/lib/aidp/providers/adapter.rb +0 -239
- data/lib/aidp/providers/aider.rb +0 -264
- data/lib/aidp/providers/anthropic.rb +0 -614
- data/lib/aidp/providers/base.rb +0 -617
- data/lib/aidp/providers/capability_registry.rb +0 -204
- data/lib/aidp/providers/codex.rb +0 -267
- data/lib/aidp/providers/cursor.rb +0 -304
- data/lib/aidp/providers/error_taxonomy.rb +0 -195
- data/lib/aidp/providers/gemini.rb +0 -110
- data/lib/aidp/providers/github_copilot.rb +0 -227
- data/lib/aidp/providers/kilocode.rb +0 -157
- 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
|
data/lib/aidp/providers/aider.rb
DELETED
|
@@ -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
|