aidp 0.26.0 → 0.27.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  3. data/lib/aidp/cli/config_command.rb +71 -0
  4. data/lib/aidp/cli/enhanced_input.rb +2 -0
  5. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  6. data/lib/aidp/cli/harness_command.rb +102 -0
  7. data/lib/aidp/cli/jobs_command.rb +3 -3
  8. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  9. data/lib/aidp/cli/models_command.rb +662 -0
  10. data/lib/aidp/cli/providers_command.rb +223 -0
  11. data/lib/aidp/cli.rb +35 -456
  12. data/lib/aidp/daemon/runner.rb +2 -2
  13. data/lib/aidp/debug_mixin.rb +2 -9
  14. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  15. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  16. data/lib/aidp/execute/interactive_repl.rb +2 -1
  17. data/lib/aidp/execute/prompt_manager.rb +4 -4
  18. data/lib/aidp/execute/work_loop_runner.rb +29 -2
  19. data/lib/aidp/execute/workflow_selector.rb +2 -2
  20. data/lib/aidp/harness/config_manager.rb +5 -5
  21. data/lib/aidp/harness/configuration.rb +32 -2
  22. data/lib/aidp/harness/enhanced_runner.rb +24 -15
  23. data/lib/aidp/harness/error_handler.rb +26 -5
  24. data/lib/aidp/harness/model_cache.rb +269 -0
  25. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  26. data/lib/aidp/harness/model_registry.rb +201 -0
  27. data/lib/aidp/harness/runner.rb +5 -0
  28. data/lib/aidp/harness/thinking_depth_manager.rb +223 -7
  29. data/lib/aidp/message_display.rb +0 -46
  30. data/lib/aidp/providers/adapter.rb +2 -4
  31. data/lib/aidp/providers/anthropic.rb +141 -128
  32. data/lib/aidp/providers/base.rb +98 -2
  33. data/lib/aidp/providers/capability_registry.rb +0 -1
  34. data/lib/aidp/providers/codex.rb +49 -67
  35. data/lib/aidp/providers/cursor.rb +71 -59
  36. data/lib/aidp/providers/gemini.rb +44 -60
  37. data/lib/aidp/providers/github_copilot.rb +2 -66
  38. data/lib/aidp/providers/kilocode.rb +24 -80
  39. data/lib/aidp/providers/opencode.rb +24 -80
  40. data/lib/aidp/setup/wizard.rb +345 -8
  41. data/lib/aidp/version.rb +1 -1
  42. data/lib/aidp/watch/plan_generator.rb +93 -14
  43. data/lib/aidp/watch/review_processor.rb +3 -3
  44. data/lib/aidp/workflows/guided_agent.rb +3 -3
  45. data/templates/aidp-development.yml.example +2 -2
  46. data/templates/aidp-production.yml.example +3 -3
  47. metadata +9 -1
@@ -14,6 +14,76 @@ module Aidp
14
14
  !!Aidp::Util.which("cursor-agent")
15
15
  end
16
16
 
17
+ # Normalize Cursor's model name to family name
18
+ #
19
+ # Cursor may use different naming conventions (e.g., dots vs hyphens)
20
+ #
21
+ # @param provider_model_name [String] Cursor's model name
22
+ # @return [String] The normalized family name
23
+ def self.model_family(provider_model_name)
24
+ # Normalize cursor naming to standard family names
25
+ # cursor uses dots: "claude-3.5-sonnet" -> "claude-3-5-sonnet"
26
+ provider_model_name.gsub(/(\d)\.(\d)/, '\1-\2')
27
+ end
28
+
29
+ # Convert family name to Cursor's naming convention
30
+ #
31
+ # @param family_name [String] The model family name
32
+ # @return [String] Cursor's model name
33
+ def self.provider_model_name(family_name)
34
+ # Cursor uses dots for version numbers
35
+ # "claude-3-5-sonnet" -> "claude-3.5-sonnet"
36
+ family_name.gsub(/(\d)-(\d)/, '\1.\2')
37
+ end
38
+
39
+ # Check if this provider supports a given model family
40
+ #
41
+ # Cursor supports Claude, GPT, and Cursor-specific models
42
+ #
43
+ # @param family_name [String] The model family name
44
+ # @return [Boolean] True if likely supported
45
+ def self.supports_model_family?(family_name)
46
+ family_name.match?(/^(claude|gpt|cursor)-/)
47
+ end
48
+
49
+ # Discover available models from Cursor
50
+ #
51
+ # Note: Cursor doesn't have a public model listing API
52
+ # Returns registry-based models that match Cursor patterns
53
+ #
54
+ # @return [Array<Hash>] Array of discovered models
55
+ def self.discover_models
56
+ return [] unless available?
57
+
58
+ begin
59
+ require_relative "../harness/model_registry"
60
+ registry = Aidp::Harness::ModelRegistry.new
61
+
62
+ # Get all models from registry that Cursor might support
63
+ models = registry.all_families.filter_map do |family|
64
+ next unless supports_model_family?(family)
65
+
66
+ info = registry.get_model_info(family)
67
+ next unless info
68
+
69
+ {
70
+ name: provider_model_name(family),
71
+ family: family,
72
+ tier: info["tier"],
73
+ capabilities: info["capabilities"] || [],
74
+ context_window: info["context_window"],
75
+ provider: "cursor"
76
+ }
77
+ end
78
+
79
+ Aidp.log_info("cursor_provider", "using registry models", count: models.size)
80
+ models
81
+ rescue => e
82
+ Aidp.log_debug("cursor_provider", "discovery failed", error: e.message)
83
+ []
84
+ end
85
+ end
86
+
17
87
  def name
18
88
  "cursor"
19
89
  end
@@ -40,12 +110,6 @@ module Aidp
40
110
  debug_provider("cursor", "Starting execution", {timeout: timeout_seconds})
41
111
  debug_log("📝 Sending prompt to cursor-agent (length: #{prompt.length})", level: :info)
42
112
 
43
- # Check if streaming mode is enabled
44
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
45
- if streaming_enabled
46
- display_message("📺 Display streaming enabled - output buffering reduced (cursor-agent does not support true streaming)", type: :info)
47
- end
48
-
49
113
  # Set up activity monitoring
50
114
  setup_activity_monitoring("cursor-agent", method(:activity_callback))
51
115
  record_activity("Starting cursor-agent execution")
@@ -71,7 +135,7 @@ module Aidp
71
135
  # Use debug_execute_command for better debugging
72
136
  # Use -p mode (designed for non-interactive/script use)
73
137
  # No fallback to interactive modes - they would hang AIDP's automation workflow
74
- result = debug_execute_command("cursor-agent", args: ["-p"], input: prompt, timeout: timeout_seconds, streaming: streaming_enabled)
138
+ result = debug_execute_command("cursor-agent", args: ["-p"], input: prompt, timeout: timeout_seconds)
75
139
 
76
140
  # Log the results
77
141
  debug_command("cursor-agent", args: ["-p"], input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -98,58 +162,6 @@ module Aidp
98
162
 
99
163
  private
100
164
 
101
- def calculate_timeout
102
- # Priority order for timeout calculation:
103
- # 1. Quick mode (for testing)
104
- # 2. Environment variable override
105
- # 3. Adaptive timeout based on step type
106
- # 4. Default timeout
107
-
108
- if ENV["AIDP_QUICK_MODE"]
109
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
110
- return TIMEOUT_QUICK_MODE
111
- end
112
-
113
- if ENV["AIDP_CURSOR_TIMEOUT"]
114
- return ENV["AIDP_CURSOR_TIMEOUT"].to_i
115
- end
116
-
117
- if adaptive_timeout
118
- display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
119
- return adaptive_timeout
120
- end
121
-
122
- # Default timeout
123
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
124
- TIMEOUT_DEFAULT
125
- end
126
-
127
- def adaptive_timeout
128
- @adaptive_timeout ||= begin
129
- # Timeout recommendations based on step type patterns
130
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
131
-
132
- case step_name
133
- when /REPOSITORY_ANALYSIS/
134
- TIMEOUT_REPOSITORY_ANALYSIS
135
- when /ARCHITECTURE_ANALYSIS/
136
- TIMEOUT_ARCHITECTURE_ANALYSIS
137
- when /TEST_ANALYSIS/
138
- TIMEOUT_TEST_ANALYSIS
139
- when /FUNCTIONALITY_ANALYSIS/
140
- TIMEOUT_FUNCTIONALITY_ANALYSIS
141
- when /DOCUMENTATION_ANALYSIS/
142
- TIMEOUT_DOCUMENTATION_ANALYSIS
143
- when /STATIC_ANALYSIS/
144
- TIMEOUT_STATIC_ANALYSIS
145
- when /REFACTORING_RECOMMENDATIONS/
146
- TIMEOUT_REFACTORING_RECOMMENDATIONS
147
- else
148
- nil # Use default
149
- end
150
- end
151
- end
152
-
153
165
  def activity_callback(state, message, provider)
154
166
  # This is now handled by the animated display thread
155
167
  # Only print static messages for state changes
@@ -8,10 +8,53 @@ module Aidp
8
8
  class Gemini < Base
9
9
  include Aidp::DebugMixin
10
10
 
11
+ # Model name pattern for Gemini models
12
+ MODEL_PATTERN = /^gemini-[\d.]+-(?:pro|flash|ultra)(?:-\d+)?$/i
13
+
11
14
  def self.available?
12
15
  !!Aidp::Util.which("gemini")
13
16
  end
14
17
 
18
+ # Normalize a provider-specific model name to its model family
19
+ #
20
+ # Gemini may use version suffixes (e.g., "gemini-1.5-pro-001").
21
+ # This method strips version suffixes to get the family name.
22
+ #
23
+ # @param provider_model_name [String] The model name
24
+ # @return [String] The model family name
25
+ def self.model_family(provider_model_name)
26
+ # Strip version suffix: "gemini-1.5-pro-001" → "gemini-1.5-pro"
27
+ provider_model_name.sub(/-\d+$/, "")
28
+ end
29
+
30
+ # Convert a model family name to the provider's preferred model name
31
+ #
32
+ # @param family_name [String] The model family name
33
+ # @return [String] The provider-specific model name (same as family)
34
+ def self.provider_model_name(family_name)
35
+ family_name
36
+ end
37
+
38
+ # Check if this provider supports a given model family
39
+ #
40
+ # @param family_name [String] The model family name
41
+ # @return [Boolean] True if it matches Gemini model pattern
42
+ def self.supports_model_family?(family_name)
43
+ MODEL_PATTERN.match?(family_name)
44
+ end
45
+
46
+ # Discover available models from Gemini
47
+ #
48
+ # Note: Gemini CLI doesn't have a standard model listing command
49
+ # Returns registry-based models that match Gemini patterns
50
+ #
51
+ # @return [Array<Hash>] Array of discovered models
52
+ def self.discover_models
53
+ return [] unless available?
54
+
55
+ discover_models_from_registry(MODEL_PATTERN, "gemini")
56
+ end
57
+
15
58
  def name
16
59
  "gemini"
17
60
  end
@@ -29,16 +72,9 @@ module Aidp
29
72
  debug_provider("gemini", "Starting execution", {timeout: timeout_seconds})
30
73
  debug_log("📝 Sending prompt to gemini...", level: :info)
31
74
 
32
- # Check if streaming mode is enabled
33
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
34
- if streaming_enabled
35
- display_message("📺 Display streaming enabled - output buffering reduced (gemini CLI does not support true streaming)", type: :info)
36
- end
37
-
38
75
  begin
39
76
  command_args = ["--prompt", prompt]
40
- # Use debug_execute_command with streaming support
41
- result = debug_execute_command("gemini", args: command_args, timeout: timeout_seconds, streaming: streaming_enabled)
77
+ result = debug_execute_command("gemini", args: command_args, timeout: timeout_seconds)
42
78
 
43
79
  # Log the results
44
80
  debug_command("gemini", args: command_args, input: nil, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -56,58 +92,6 @@ module Aidp
56
92
  end
57
93
 
58
94
  private
59
-
60
- def calculate_timeout
61
- # Priority order for timeout calculation:
62
- # 1. Quick mode (for testing)
63
- # 2. Environment variable override
64
- # 3. Adaptive timeout based on step type
65
- # 4. Default timeout
66
-
67
- if ENV["AIDP_QUICK_MODE"]
68
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
69
- return TIMEOUT_QUICK_MODE
70
- end
71
-
72
- if ENV["AIDP_GEMINI_TIMEOUT"]
73
- return ENV["AIDP_GEMINI_TIMEOUT"].to_i
74
- end
75
-
76
- # Adaptive timeout based on step type
77
- step_timeout = get_adaptive_timeout
78
- if step_timeout
79
- display_message("🧠 Using adaptive timeout: #{step_timeout} seconds", type: :info)
80
- return step_timeout
81
- end
82
-
83
- # Default timeout
84
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
85
- TIMEOUT_DEFAULT
86
- end
87
-
88
- def get_adaptive_timeout
89
- # Timeout recommendations based on step type patterns
90
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
91
-
92
- case step_name
93
- when /REPOSITORY_ANALYSIS/
94
- TIMEOUT_REPOSITORY_ANALYSIS
95
- when /ARCHITECTURE_ANALYSIS/
96
- TIMEOUT_ARCHITECTURE_ANALYSIS
97
- when /TEST_ANALYSIS/
98
- TIMEOUT_TEST_ANALYSIS
99
- when /FUNCTIONALITY_ANALYSIS/
100
- TIMEOUT_FUNCTIONALITY_ANALYSIS
101
- when /DOCUMENTATION_ANALYSIS/
102
- TIMEOUT_DOCUMENTATION_ANALYSIS
103
- when /STATIC_ANALYSIS/
104
- TIMEOUT_STATIC_ANALYSIS
105
- when /REFACTORING_RECOMMENDATIONS/
106
- TIMEOUT_REFACTORING_RECOMMENDATIONS
107
- else
108
- nil # Use default
109
- end
110
- end
111
95
  end
112
96
  end
113
97
  end
@@ -43,12 +43,6 @@ module Aidp
43
43
  debug_provider("copilot", "Starting execution", {timeout: timeout_seconds})
44
44
  debug_log("📝 Sending prompt to copilot (length: #{prompt.length})", level: :info)
45
45
 
46
- # Check if streaming mode is enabled
47
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
48
- if streaming_enabled
49
- display_message("📺 Display streaming enabled - output buffering reduced (copilot CLI does not support true streaming)", type: :info)
50
- end
51
-
52
46
  # Set up activity monitoring
53
47
  setup_activity_monitoring("copilot", method(:activity_callback))
54
48
  record_activity("Starting copilot execution")
@@ -80,7 +74,7 @@ module Aidp
80
74
  end
81
75
 
82
76
  # Use debug_execute_command for better debugging (no input since prompt is in args)
83
- result = debug_execute_command("copilot", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
77
+ result = debug_execute_command("copilot", args: args, timeout: timeout_seconds)
84
78
 
85
79
  # Log the results
86
80
  debug_command("copilot", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -178,17 +172,11 @@ module Aidp
178
172
  debug_provider("copilot", "Starting execution", {timeout: timeout_seconds, args: args})
179
173
  debug_log("📝 Sending prompt to copilot with custom args", level: :info)
180
174
 
181
- # Check if streaming mode is enabled
182
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
183
- if streaming_enabled
184
- display_message("📺 Display streaming enabled - output buffering reduced (copilot CLI does not support true streaming)", type: :info)
185
- end
186
-
187
175
  setup_activity_monitoring("copilot", method(:activity_callback))
188
176
  record_activity("Starting copilot execution with custom args")
189
177
 
190
178
  begin
191
- result = debug_execute_command("copilot", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
179
+ result = debug_execute_command("copilot", args: args, timeout: timeout_seconds)
192
180
  debug_command("copilot", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
193
181
 
194
182
  if result.exit_status == 0
@@ -206,58 +194,6 @@ module Aidp
206
194
  end
207
195
  end
208
196
 
209
- def calculate_timeout
210
- # Priority order for timeout calculation:
211
- # 1. Quick mode (for testing)
212
- # 2. Environment variable override
213
- # 3. Adaptive timeout based on step type
214
- # 4. Default timeout
215
-
216
- if ENV["AIDP_QUICK_MODE"]
217
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
218
- return TIMEOUT_QUICK_MODE
219
- end
220
-
221
- if ENV["AIDP_GITHUB_COPILOT_TIMEOUT"]
222
- return ENV["AIDP_GITHUB_COPILOT_TIMEOUT"].to_i
223
- end
224
-
225
- if adaptive_timeout
226
- display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
227
- return adaptive_timeout
228
- end
229
-
230
- # Default timeout
231
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
232
- TIMEOUT_DEFAULT
233
- end
234
-
235
- def adaptive_timeout
236
- @adaptive_timeout ||= begin
237
- # Timeout recommendations based on step type patterns
238
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
239
-
240
- case step_name
241
- when /REPOSITORY_ANALYSIS/
242
- TIMEOUT_REPOSITORY_ANALYSIS
243
- when /ARCHITECTURE_ANALYSIS/
244
- TIMEOUT_ARCHITECTURE_ANALYSIS
245
- when /TEST_ANALYSIS/
246
- TIMEOUT_TEST_ANALYSIS
247
- when /FUNCTIONALITY_ANALYSIS/
248
- TIMEOUT_FUNCTIONALITY_ANALYSIS
249
- when /DOCUMENTATION_ANALYSIS/
250
- TIMEOUT_DOCUMENTATION_ANALYSIS
251
- when /STATIC_ANALYSIS/
252
- TIMEOUT_STATIC_ANALYSIS
253
- when /REFACTORING_RECOMMENDATIONS/
254
- TIMEOUT_REFACTORING_RECOMMENDATIONS
255
- else
256
- nil # Use default
257
- end
258
- end
259
- end
260
-
261
197
  def activity_callback(state, message, provider)
262
198
  # Handle activity state changes
263
199
  case state
@@ -10,10 +10,33 @@ module Aidp
10
10
  class Kilocode < Base
11
11
  include Aidp::DebugMixin
12
12
 
13
+ # Model name pattern for OpenAI models (since Kilocode uses OpenAI)
14
+ MODEL_PATTERN = /^gpt-[\d.o-]+(?:-turbo)?(?:-mini)?$/i
15
+
13
16
  def self.available?
14
17
  !!Aidp::Util.which("kilocode")
15
18
  end
16
19
 
20
+ # Check if this provider supports a given model family
21
+ #
22
+ # @param family_name [String] The model family name
23
+ # @return [Boolean] True if it matches OpenAI model pattern
24
+ def self.supports_model_family?(family_name)
25
+ MODEL_PATTERN.match?(family_name)
26
+ end
27
+
28
+ # Discover available models from registry
29
+ #
30
+ # Note: Kilocode CLI doesn't have a standard model listing command
31
+ # Returns registry-based models that match OpenAI patterns
32
+ #
33
+ # @return [Array<Hash>] Array of discovered models
34
+ def self.discover_models
35
+ return [] unless available?
36
+
37
+ discover_models_from_registry(MODEL_PATTERN, "kilocode")
38
+ end
39
+
17
40
  def name
18
41
  "kilocode"
19
42
  end
@@ -31,12 +54,6 @@ module Aidp
31
54
  debug_provider("kilocode", "Starting execution", {timeout: timeout_seconds})
32
55
  debug_log("📝 Sending prompt to kilocode (length: #{prompt.length})", level: :info)
33
56
 
34
- # Check if streaming mode is enabled
35
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
36
- if streaming_enabled
37
- display_message("📺 Display streaming enabled - output buffering reduced", type: :info)
38
- end
39
-
40
57
  # Check if prompt is too large and warn
41
58
  if prompt.length > 3000
42
59
  debug_log("⚠️ Large prompt detected (#{prompt.length} chars) - this may cause rate limiting", level: :warn)
@@ -85,7 +102,7 @@ module Aidp
85
102
  end
86
103
 
87
104
  # Use debug_execute_command for better debugging
88
- result = debug_execute_command("kilocode", args: args, input: prompt, timeout: timeout_seconds, streaming: streaming_enabled, env: env_vars)
105
+ result = debug_execute_command("kilocode", args: args, input: prompt, timeout: timeout_seconds, env: env_vars)
89
106
 
90
107
  # Log the results
91
108
  debug_command("kilocode", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -112,58 +129,6 @@ module Aidp
112
129
 
113
130
  private
114
131
 
115
- def calculate_timeout
116
- # Priority order for timeout calculation:
117
- # 1. Quick mode (for testing)
118
- # 2. Environment variable override
119
- # 3. Adaptive timeout based on step type
120
- # 4. Default timeout
121
-
122
- if ENV["AIDP_QUICK_MODE"]
123
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
124
- return TIMEOUT_QUICK_MODE
125
- end
126
-
127
- if ENV["AIDP_KILOCODE_TIMEOUT"]
128
- return ENV["AIDP_KILOCODE_TIMEOUT"].to_i
129
- end
130
-
131
- if adaptive_timeout
132
- display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
133
- return adaptive_timeout
134
- end
135
-
136
- # Default timeout
137
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
138
- TIMEOUT_DEFAULT
139
- end
140
-
141
- def adaptive_timeout
142
- @adaptive_timeout ||= begin
143
- # Timeout recommendations based on step type patterns
144
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
145
-
146
- case step_name
147
- when /REPOSITORY_ANALYSIS/
148
- TIMEOUT_REPOSITORY_ANALYSIS
149
- when /ARCHITECTURE_ANALYSIS/
150
- TIMEOUT_ARCHITECTURE_ANALYSIS
151
- when /TEST_ANALYSIS/
152
- TIMEOUT_TEST_ANALYSIS
153
- when /FUNCTIONALITY_ANALYSIS/
154
- TIMEOUT_FUNCTIONALITY_ANALYSIS
155
- when /DOCUMENTATION_ANALYSIS/
156
- TIMEOUT_DOCUMENTATION_ANALYSIS
157
- when /STATIC_ANALYSIS/
158
- TIMEOUT_STATIC_ANALYSIS
159
- when /REFACTORING_RECOMMENDATIONS/
160
- TIMEOUT_REFACTORING_RECOMMENDATIONS
161
- else
162
- nil # Use default
163
- end
164
- end
165
- end
166
-
167
132
  def activity_callback(state, message, provider)
168
133
  # This is now handled by the animated display thread
169
134
  # Only print static messages for state changes
@@ -176,27 +141,6 @@ module Aidp
176
141
  display_message("❌ kilocode execution failed: #{message}", type: :error)
177
142
  end
178
143
  end
179
-
180
- def setup_activity_monitoring(provider_name, callback)
181
- @activity_callback = callback
182
- @activity_state = :starting
183
- @activity_start_time = Time.now
184
- end
185
-
186
- def record_activity(message)
187
- @activity_state = :running
188
- @activity_callback&.call(:running, message, "kilocode")
189
- end
190
-
191
- def mark_completed
192
- @activity_state = :completed
193
- @activity_callback&.call(:completed, "Execution completed", "kilocode")
194
- end
195
-
196
- def mark_failed(reason)
197
- @activity_state = :failed
198
- @activity_callback&.call(:failed, reason, "kilocode")
199
- end
200
144
  end
201
145
  end
202
146
  end
@@ -10,10 +10,33 @@ module Aidp
10
10
  class Opencode < Base
11
11
  include Aidp::DebugMixin
12
12
 
13
+ # Model name pattern for OpenAI models (since OpenCode uses OpenAI)
14
+ MODEL_PATTERN = /^gpt-[\d.o-]+(?:-turbo)?(?:-mini)?$/i
15
+
13
16
  def self.available?
14
17
  !!Aidp::Util.which("opencode")
15
18
  end
16
19
 
20
+ # Check if this provider supports a given model family
21
+ #
22
+ # @param family_name [String] The model family name
23
+ # @return [Boolean] True if it matches OpenAI model pattern
24
+ def self.supports_model_family?(family_name)
25
+ MODEL_PATTERN.match?(family_name)
26
+ end
27
+
28
+ # Discover available models from registry
29
+ #
30
+ # Note: OpenCode CLI doesn't have a standard model listing command
31
+ # Returns registry-based models that match OpenAI patterns
32
+ #
33
+ # @return [Array<Hash>] Array of discovered models
34
+ def self.discover_models
35
+ return [] unless available?
36
+
37
+ discover_models_from_registry(MODEL_PATTERN, "opencode")
38
+ end
39
+
17
40
  def name
18
41
  "opencode"
19
42
  end
@@ -31,12 +54,6 @@ module Aidp
31
54
  debug_provider("opencode", "Starting execution", {timeout: timeout_seconds})
32
55
  debug_log("📝 Sending prompt to opencode (length: #{prompt.length})", level: :info)
33
56
 
34
- # Check if streaming mode is enabled
35
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
36
- if streaming_enabled
37
- display_message("📺 Display streaming enabled - output buffering reduced (opencode does not support true streaming)", type: :info)
38
- end
39
-
40
57
  # Check if prompt is too large and warn
41
58
  if prompt.length > 3000
42
59
  debug_log("⚠️ Large prompt detected (#{prompt.length} chars) - this may cause rate limiting", level: :warn)
@@ -67,7 +84,7 @@ module Aidp
67
84
  # Use debug_execute_command for better debugging
68
85
  # opencode run command with prompt and model
69
86
  model = ENV["OPENCODE_MODEL"] || "github-copilot/claude-3.5-sonnet"
70
- result = debug_execute_command("opencode", args: ["run", "-m", model, prompt], timeout: timeout_seconds, streaming: streaming_enabled)
87
+ result = debug_execute_command("opencode", args: ["run", "-m", model, prompt], timeout: timeout_seconds)
71
88
 
72
89
  # Log the results
73
90
  debug_command("opencode", args: ["run", "-m", model, prompt], input: nil, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -94,58 +111,6 @@ module Aidp
94
111
 
95
112
  private
96
113
 
97
- def calculate_timeout
98
- # Priority order for timeout calculation:
99
- # 1. Quick mode (for testing)
100
- # 2. Environment variable override
101
- # 3. Adaptive timeout based on step type
102
- # 4. Default timeout
103
-
104
- if ENV["AIDP_QUICK_MODE"]
105
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
106
- return TIMEOUT_QUICK_MODE
107
- end
108
-
109
- if ENV["AIDP_OPENCODE_TIMEOUT"]
110
- return ENV["AIDP_OPENCODE_TIMEOUT"].to_i
111
- end
112
-
113
- if adaptive_timeout
114
- display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
115
- return adaptive_timeout
116
- end
117
-
118
- # Default timeout
119
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
120
- TIMEOUT_DEFAULT
121
- end
122
-
123
- def adaptive_timeout
124
- @adaptive_timeout ||= begin
125
- # Timeout recommendations based on step type patterns
126
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
127
-
128
- case step_name
129
- when /REPOSITORY_ANALYSIS/
130
- TIMEOUT_REPOSITORY_ANALYSIS
131
- when /ARCHITECTURE_ANALYSIS/
132
- TIMEOUT_ARCHITECTURE_ANALYSIS
133
- when /TEST_ANALYSIS/
134
- TIMEOUT_TEST_ANALYSIS
135
- when /FUNCTIONALITY_ANALYSIS/
136
- TIMEOUT_FUNCTIONALITY_ANALYSIS
137
- when /DOCUMENTATION_ANALYSIS/
138
- TIMEOUT_DOCUMENTATION_ANALYSIS
139
- when /STATIC_ANALYSIS/
140
- TIMEOUT_STATIC_ANALYSIS
141
- when /REFACTORING_RECOMMENDATIONS/
142
- TIMEOUT_REFACTORING_RECOMMENDATIONS
143
- else
144
- nil # Use default
145
- end
146
- end
147
- end
148
-
149
114
  def activity_callback(state, message, provider)
150
115
  # This is now handled by the animated display thread
151
116
  # Only print static messages for state changes
@@ -158,27 +123,6 @@ module Aidp
158
123
  display_message("❌ opencode execution failed: #{message}", type: :error)
159
124
  end
160
125
  end
161
-
162
- def setup_activity_monitoring(provider_name, callback)
163
- @activity_callback = callback
164
- @activity_state = :starting
165
- @activity_start_time = Time.now
166
- end
167
-
168
- def record_activity(message)
169
- @activity_state = :running
170
- @activity_callback&.call(:running, message, "opencode")
171
- end
172
-
173
- def mark_completed
174
- @activity_state = :completed
175
- @activity_callback&.call(:completed, "Execution completed", "opencode")
176
- end
177
-
178
- def mark_failed(reason)
179
- @activity_state = :failed
180
- @activity_callback&.call(:failed, reason, "opencode")
181
- end
182
126
  end
183
127
  end
184
128
  end