aidp 0.26.0 → 0.28.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -0
  3. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  4. data/lib/aidp/cli/config_command.rb +71 -0
  5. data/lib/aidp/cli/enhanced_input.rb +2 -0
  6. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  7. data/lib/aidp/cli/harness_command.rb +102 -0
  8. data/lib/aidp/cli/jobs_command.rb +3 -3
  9. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  10. data/lib/aidp/cli/models_command.rb +661 -0
  11. data/lib/aidp/cli/providers_command.rb +223 -0
  12. data/lib/aidp/cli.rb +45 -464
  13. data/lib/aidp/config.rb +54 -0
  14. data/lib/aidp/daemon/runner.rb +2 -2
  15. data/lib/aidp/debug_mixin.rb +25 -10
  16. data/lib/aidp/execute/agent_signal_parser.rb +22 -0
  17. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  18. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  19. data/lib/aidp/execute/interactive_repl.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +4 -4
  21. data/lib/aidp/execute/repl_macros.rb +2 -2
  22. data/lib/aidp/execute/steps.rb +94 -1
  23. data/lib/aidp/execute/work_loop_runner.rb +238 -19
  24. data/lib/aidp/execute/workflow_selector.rb +4 -27
  25. data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
  26. data/lib/aidp/harness/ai_decision_engine.rb +35 -2
  27. data/lib/aidp/harness/config_manager.rb +5 -10
  28. data/lib/aidp/harness/config_schema.rb +8 -0
  29. data/lib/aidp/harness/configuration.rb +40 -2
  30. data/lib/aidp/harness/enhanced_runner.rb +25 -19
  31. data/lib/aidp/harness/error_handler.rb +23 -73
  32. data/lib/aidp/harness/model_cache.rb +269 -0
  33. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  34. data/lib/aidp/harness/model_registry.rb +201 -0
  35. data/lib/aidp/harness/provider_factory.rb +11 -2
  36. data/lib/aidp/harness/runner.rb +5 -0
  37. data/lib/aidp/harness/state_manager.rb +0 -7
  38. data/lib/aidp/harness/thinking_depth_manager.rb +202 -7
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
  40. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
  41. data/lib/aidp/harness/ui/progress_display.rb +6 -2
  42. data/lib/aidp/harness/user_interface.rb +0 -58
  43. data/lib/aidp/init/runner.rb +7 -2
  44. data/lib/aidp/message_display.rb +0 -46
  45. data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
  46. data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
  47. data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
  48. data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
  49. data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
  50. data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
  51. data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
  52. data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
  53. data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
  54. data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
  55. data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
  56. data/lib/aidp/planning/parsers/document_parser.rb +141 -0
  57. data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
  58. data/lib/aidp/provider_manager.rb +8 -32
  59. data/lib/aidp/providers/adapter.rb +2 -4
  60. data/lib/aidp/providers/aider.rb +264 -0
  61. data/lib/aidp/providers/anthropic.rb +206 -121
  62. data/lib/aidp/providers/base.rb +123 -3
  63. data/lib/aidp/providers/capability_registry.rb +0 -1
  64. data/lib/aidp/providers/codex.rb +75 -70
  65. data/lib/aidp/providers/cursor.rb +87 -59
  66. data/lib/aidp/providers/gemini.rb +57 -60
  67. data/lib/aidp/providers/github_copilot.rb +19 -66
  68. data/lib/aidp/providers/kilocode.rb +35 -80
  69. data/lib/aidp/providers/opencode.rb +35 -80
  70. data/lib/aidp/setup/wizard.rb +555 -8
  71. data/lib/aidp/version.rb +1 -1
  72. data/lib/aidp/watch/build_processor.rb +211 -30
  73. data/lib/aidp/watch/change_request_processor.rb +128 -14
  74. data/lib/aidp/watch/ci_fix_processor.rb +103 -37
  75. data/lib/aidp/watch/ci_log_extractor.rb +258 -0
  76. data/lib/aidp/watch/github_state_extractor.rb +177 -0
  77. data/lib/aidp/watch/implementation_verifier.rb +284 -0
  78. data/lib/aidp/watch/plan_generator.rb +95 -52
  79. data/lib/aidp/watch/plan_processor.rb +7 -6
  80. data/lib/aidp/watch/repository_client.rb +245 -17
  81. data/lib/aidp/watch/review_processor.rb +100 -19
  82. data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
  83. data/lib/aidp/watch/runner.rb +181 -29
  84. data/lib/aidp/watch/state_store.rb +22 -1
  85. data/lib/aidp/workflows/definitions.rb +147 -0
  86. data/lib/aidp/workflows/guided_agent.rb +3 -3
  87. data/lib/aidp/workstream_cleanup.rb +245 -0
  88. data/lib/aidp/worktree.rb +19 -0
  89. data/templates/aidp-development.yml.example +2 -2
  90. data/templates/aidp-production.yml.example +3 -3
  91. data/templates/aidp.yml.example +57 -0
  92. data/templates/implementation/generate_tdd_specs.md +213 -0
  93. data/templates/implementation/iterative_implementation.md +122 -0
  94. data/templates/planning/agile/analyze_feedback.md +183 -0
  95. data/templates/planning/agile/generate_iteration_plan.md +179 -0
  96. data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
  97. data/templates/planning/agile/generate_marketing_report.md +162 -0
  98. data/templates/planning/agile/generate_mvp_scope.md +127 -0
  99. data/templates/planning/agile/generate_user_test_plan.md +143 -0
  100. data/templates/planning/agile/ingest_feedback.md +174 -0
  101. data/templates/planning/assemble_project_plan.md +113 -0
  102. data/templates/planning/assign_personas.md +108 -0
  103. data/templates/planning/create_tasks.md +52 -6
  104. data/templates/planning/generate_gantt.md +86 -0
  105. data/templates/planning/generate_wbs.md +85 -0
  106. data/templates/planning/initialize_planning_mode.md +70 -0
  107. data/templates/skills/README.md +2 -2
  108. data/templates/skills/marketing_strategist/SKILL.md +279 -0
  109. data/templates/skills/product_manager/SKILL.md +177 -0
  110. data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
  111. data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
  112. data/templates/skills/ux_researcher/SKILL.md +222 -0
  113. metadata +47 -1
@@ -37,7 +37,7 @@ module Aidp
37
37
  TIMEOUT_REFACTORING_RECOMMENDATIONS = 600 # 10 minutes - refactoring
38
38
  TIMEOUT_IMPLEMENTATION = 900 # 15 minutes - implementation (write files, run tests, fix issues)
39
39
 
40
- attr_reader :activity_state, :last_activity_time, :start_time, :step_name
40
+ attr_reader :activity_state, :last_activity_time, :start_time, :step_name, :model
41
41
 
42
42
  def initialize(output: nil, prompt: TTY::Prompt.new)
43
43
  @activity_state = :idle
@@ -52,6 +52,7 @@ module Aidp
52
52
  @harness_context = nil
53
53
  @output = output
54
54
  @prompt = prompt
55
+ @model = nil
55
56
  @harness_metrics = {
56
57
  total_requests: 0,
57
58
  successful_requests: 0,
@@ -74,6 +75,12 @@ module Aidp
74
75
  name
75
76
  end
76
77
 
78
+ # Configure the provider with options
79
+ # @param config [Hash] Configuration options, may include :model
80
+ def configure(config)
81
+ @model = config[:model] if config[:model]
82
+ end
83
+
77
84
  def send_message(prompt:, session: nil)
78
85
  raise NotImplementedError, "#{self.class} must implement #send_message"
79
86
  end
@@ -324,6 +331,59 @@ module Aidp
324
331
  end
325
332
  end
326
333
 
334
+ # Helper method for registry-based model discovery
335
+ #
336
+ # Providers that use the model registry can call this method to discover models
337
+ # based on a model family pattern.
338
+ #
339
+ # @param model_pattern [Regexp] Pattern to match model families
340
+ # @param provider_name [String] Name of the provider
341
+ # @return [Array<Hash>] Array of discovered models
342
+ def self.discover_models_from_registry(model_pattern, provider_name)
343
+ require_relative "../harness/model_registry"
344
+ registry = Aidp::Harness::ModelRegistry.new
345
+
346
+ # Get all models from registry that match the pattern
347
+ models = registry.all_families.filter_map do |family|
348
+ next unless model_pattern.match?(family)
349
+
350
+ info = registry.get_model_info(family)
351
+ next unless info
352
+
353
+ {
354
+ name: family,
355
+ family: family,
356
+ tier: info["tier"],
357
+ capabilities: info["capabilities"] || [],
358
+ context_window: info["context_window"],
359
+ provider: provider_name
360
+ }
361
+ end
362
+
363
+ Aidp.log_info("#{provider_name}_provider", "using registry models", count: models.size)
364
+ models
365
+ rescue => e
366
+ Aidp.log_debug("#{provider_name}_provider", "discovery failed", error: e.message)
367
+ []
368
+ end
369
+
370
+ # Get firewall requirements for this provider
371
+ #
372
+ # Returns domains and IP ranges that need to be accessible for this provider
373
+ # to function properly. Used by devcontainer firewall configuration.
374
+ #
375
+ # @return [Hash] Firewall requirements with :domains and :ip_ranges keys
376
+ # - domains: Array of domain strings
377
+ # - ip_ranges: Array of CIDR strings
378
+ #
379
+ # Override in subclasses to provide provider-specific requirements
380
+ def self.firewall_requirements
381
+ {
382
+ domains: [],
383
+ ip_ranges: []
384
+ }
385
+ end
386
+
327
387
  protected
328
388
 
329
389
  # Log message to job if in background mode
@@ -371,8 +431,6 @@ module Aidp
371
431
  (success_rate * 50) + ((1 - rate_limit_ratio) * 30) + (response_time_score * 0.2)
372
432
  end
373
433
 
374
- protected
375
-
376
434
  # Update spinner status with elapsed time
377
435
  # This is a shared method used by all providers to display progress
378
436
  def update_spinner_status(spinner, elapsed, provider_name)
@@ -419,6 +477,68 @@ module Aidp
419
477
  false # Base implementation returns false, subclasses should override
420
478
  end
421
479
 
480
+ # Calculate timeout for provider operations
481
+ #
482
+ # Priority order:
483
+ # 1. Quick mode (for testing)
484
+ # 2. Provider-specific environment variable override
485
+ # 3. Adaptive timeout based on step type
486
+ # 4. Default timeout
487
+ #
488
+ # Override provider_env_var to customize the environment variable name
489
+ def calculate_timeout
490
+ if ENV["AIDP_QUICK_MODE"]
491
+ display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
492
+ return TIMEOUT_QUICK_MODE
493
+ end
494
+
495
+ provider_env_var = "AIDP_#{name.upcase}_TIMEOUT"
496
+ return ENV[provider_env_var].to_i if ENV[provider_env_var]
497
+
498
+ step_timeout = adaptive_timeout
499
+ if step_timeout
500
+ display_message("🧠 Using adaptive timeout: #{step_timeout} seconds", type: :info)
501
+ return step_timeout
502
+ end
503
+
504
+ # Default timeout
505
+ display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
506
+ TIMEOUT_DEFAULT
507
+ end
508
+
509
+ # Get adaptive timeout based on step type
510
+ #
511
+ # This method returns different timeout values based on the type of operation
512
+ # being performed, as indicated by the AIDP_CURRENT_STEP environment variable.
513
+ # Returns nil for unknown steps to allow calculate_timeout to use the default.
514
+ def adaptive_timeout
515
+ @adaptive_timeout ||= begin
516
+ # Timeout recommendations based on step type patterns
517
+ step_name = ENV["AIDP_CURRENT_STEP"] || ""
518
+
519
+ case step_name
520
+ when /REPOSITORY_ANALYSIS/
521
+ TIMEOUT_REPOSITORY_ANALYSIS
522
+ when /ARCHITECTURE_ANALYSIS/
523
+ TIMEOUT_ARCHITECTURE_ANALYSIS
524
+ when /TEST_ANALYSIS/
525
+ TIMEOUT_TEST_ANALYSIS
526
+ when /FUNCTIONALITY_ANALYSIS/
527
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
528
+ when /DOCUMENTATION_ANALYSIS/
529
+ TIMEOUT_DOCUMENTATION_ANALYSIS
530
+ when /STATIC_ANALYSIS/
531
+ TIMEOUT_STATIC_ANALYSIS
532
+ when /REFACTORING_RECOMMENDATIONS/
533
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
534
+ when /IMPLEMENTATION/
535
+ TIMEOUT_IMPLEMENTATION
536
+ else
537
+ nil # Return nil for unknown steps
538
+ end
539
+ end
540
+ end
541
+
422
542
  private
423
543
  end
424
544
  end
@@ -16,7 +16,6 @@ module Aidp
16
16
  :supports_tool_use, # Boolean: supports tool/function calling
17
17
  :supports_vision, # Boolean: supports image/vision inputs
18
18
  :supports_file_upload, # Boolean: supports file uploads
19
- :streaming, # Boolean: supports streaming responses
20
19
  :supports_mcp, # Boolean: supports Model Context Protocol
21
20
  :max_tokens, # Maximum tokens per response
22
21
  :supports_dangerous_mode # Boolean: supports elevated permissions mode
@@ -10,10 +10,52 @@ module Aidp
10
10
  class Codex < Base
11
11
  include Aidp::DebugMixin
12
12
 
13
+ # Model name pattern for OpenAI models (since Codex uses OpenAI)
14
+ MODEL_PATTERN = /^gpt-[\d.o-]+(?:-turbo)?(?:-mini)?$/i
15
+ LONG_PROMPT_THRESHOLD = 8000
16
+ LONG_PROMPT_TIMEOUT = 900 # 15 minutes for large prompts
17
+
13
18
  def self.available?
14
19
  !!Aidp::Util.which("codex")
15
20
  end
16
21
 
22
+ # Check if this provider supports a given model family
23
+ #
24
+ # @param family_name [String] The model family name
25
+ # @return [Boolean] True if it matches OpenAI model pattern
26
+ def self.supports_model_family?(family_name)
27
+ MODEL_PATTERN.match?(family_name)
28
+ end
29
+
30
+ # Discover available models from registry
31
+ #
32
+ # Note: Codex CLI doesn't have a standard model listing command
33
+ # Returns registry-based models that match OpenAI patterns
34
+ #
35
+ # @return [Array<Hash>] Array of discovered models
36
+ def self.discover_models
37
+ return [] unless available?
38
+
39
+ discover_models_from_registry(MODEL_PATTERN, "codex")
40
+ end
41
+
42
+ # Get firewall requirements for Codex provider
43
+ # Codex uses OpenAI APIs
44
+ def self.firewall_requirements
45
+ {
46
+ domains: [
47
+ "api.openai.com",
48
+ "auth.openai.com",
49
+ "openai.com",
50
+ "chat.openai.com",
51
+ "chatgpt.com",
52
+ "cdn.openai.com",
53
+ "oaiusercontent.com"
54
+ ],
55
+ ip_ranges: []
56
+ }
57
+ end
58
+
17
59
  def name
18
60
  "codex"
19
61
  end
@@ -37,18 +79,13 @@ module Aidp
37
79
  def send_message(prompt:, session: nil)
38
80
  raise "codex CLI not available" unless self.class.available?
39
81
 
40
- # Smart timeout calculation
82
+ # Smart timeout calculation (store prompt length for adaptive logic)
83
+ @current_codex_prompt_length = prompt.length
41
84
  timeout_seconds = calculate_timeout
42
85
 
43
86
  debug_provider("codex", "Starting execution", {timeout: timeout_seconds})
44
87
  debug_log("📝 Sending prompt to codex (length: #{prompt.length})", level: :info)
45
88
 
46
- # Check if streaming mode is enabled
47
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
48
- if streaming_enabled
49
- display_message("📺 Streaming mode enabled - output will appear in real-time", type: :info)
50
- end
51
-
52
89
  # Set up activity monitoring
53
90
  setup_activity_monitoring("codex", method(:activity_callback))
54
91
  record_activity("Starting codex execution")
@@ -74,6 +111,11 @@ module Aidp
74
111
  # Use non-interactive mode (exec) for automation
75
112
  args = ["exec", prompt]
76
113
 
114
+ # Add model if configured
115
+ if @model && !@model.empty?
116
+ args += ["--model", @model]
117
+ end
118
+
77
119
  # Add session support if provided (codex may support session/thread continuation)
78
120
  if session && !session.empty?
79
121
  args += ["--session", session]
@@ -94,7 +136,7 @@ module Aidp
94
136
  end
95
137
 
96
138
  # Use debug_execute_command for better debugging
97
- result = debug_execute_command("codex", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
139
+ result = debug_execute_command("codex", args: args, timeout: timeout_seconds)
98
140
 
99
141
  # Log the results
100
142
  debug_command("codex", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -116,6 +158,7 @@ module Aidp
116
158
  raise
117
159
  ensure
118
160
  cleanup_activity_display(activity_display_thread, spinner)
161
+ @current_codex_prompt_length = nil
119
162
  end
120
163
  end
121
164
 
@@ -128,9 +171,10 @@ module Aidp
128
171
  args += ["--session", session]
129
172
  end
130
173
 
131
- # Add model selection
132
- if model
133
- args += ["--model", model]
174
+ # Add model selection (from parameter or configured model)
175
+ model_to_use = model || @model
176
+ if model_to_use
177
+ args += ["--model", model_to_use]
134
178
  end
135
179
 
136
180
  # Add approval flag - but warn about interactive behavior
@@ -161,22 +205,17 @@ module Aidp
161
205
 
162
206
  # Internal helper for send_with_options - executes with custom arguments
163
207
  def send_with_custom_args(prompt:, args:)
208
+ @current_codex_prompt_length = prompt.length
164
209
  timeout_seconds = calculate_timeout
165
210
 
166
211
  debug_provider("codex", "Starting execution", {timeout: timeout_seconds, args: args})
167
212
  debug_log("📝 Sending prompt to codex with custom args", level: :info)
168
213
 
169
- # Check if streaming mode is enabled
170
- streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
171
- if streaming_enabled
172
- display_message("📺 Display streaming enabled - output buffering reduced (codex CLI does not support true streaming)", type: :info)
173
- end
174
-
175
214
  setup_activity_monitoring("codex", method(:activity_callback))
176
215
  record_activity("Starting codex execution with custom args")
177
216
 
178
217
  begin
179
- result = debug_execute_command("codex", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
218
+ result = debug_execute_command("codex", args: args, timeout: timeout_seconds)
180
219
  debug_command("codex", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
181
220
 
182
221
  if result.exit_status == 0
@@ -191,58 +230,8 @@ module Aidp
191
230
  mark_failed("codex execution failed: #{e.message}")
192
231
  debug_error(e, {provider: "codex", prompt_length: prompt.length})
193
232
  raise
194
- end
195
- end
196
-
197
- def calculate_timeout
198
- # Priority order for timeout calculation:
199
- # 1. Quick mode (for testing)
200
- # 2. Environment variable override
201
- # 3. Adaptive timeout based on step type
202
- # 4. Default timeout
203
-
204
- if ENV["AIDP_QUICK_MODE"]
205
- display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
206
- return TIMEOUT_QUICK_MODE
207
- end
208
-
209
- if ENV["AIDP_CODEX_TIMEOUT"]
210
- return ENV["AIDP_CODEX_TIMEOUT"].to_i
211
- end
212
-
213
- if adaptive_timeout
214
- display_message("🧠 Using adaptive timeout: #{adaptive_timeout} seconds", type: :info)
215
- return adaptive_timeout
216
- end
217
-
218
- # Default timeout
219
- display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
220
- TIMEOUT_DEFAULT
221
- end
222
-
223
- def adaptive_timeout
224
- @adaptive_timeout ||= begin
225
- # Timeout recommendations based on step type patterns
226
- step_name = ENV["AIDP_CURRENT_STEP"] || ""
227
-
228
- case step_name
229
- when /REPOSITORY_ANALYSIS/
230
- TIMEOUT_REPOSITORY_ANALYSIS
231
- when /ARCHITECTURE_ANALYSIS/
232
- TIMEOUT_ARCHITECTURE_ANALYSIS
233
- when /TEST_ANALYSIS/
234
- TIMEOUT_TEST_ANALYSIS
235
- when /FUNCTIONALITY_ANALYSIS/
236
- TIMEOUT_FUNCTIONALITY_ANALYSIS
237
- when /DOCUMENTATION_ANALYSIS/
238
- TIMEOUT_DOCUMENTATION_ANALYSIS
239
- when /STATIC_ANALYSIS/
240
- TIMEOUT_STATIC_ANALYSIS
241
- when /REFACTORING_RECOMMENDATIONS/
242
- TIMEOUT_REFACTORING_RECOMMENDATIONS
243
- else
244
- nil # Use default
245
- end
233
+ ensure
234
+ @current_codex_prompt_length = nil
246
235
  end
247
236
  end
248
237
 
@@ -257,6 +246,22 @@ module Aidp
257
246
  display_message("\n❌ Codex CLI failed: #{message}", type: :error)
258
247
  end
259
248
  end
249
+
250
+ def calculate_timeout
251
+ env_override = ENV["AIDP_CODEX_TIMEOUT"]
252
+ return env_override.to_i if env_override&.match?(/^\d+$/)
253
+
254
+ base_timeout = super
255
+
256
+ prompt_length = @current_codex_prompt_length
257
+ return base_timeout unless prompt_length && prompt_length >= LONG_PROMPT_THRESHOLD
258
+
259
+ extended_timeout = [base_timeout, LONG_PROMPT_TIMEOUT].max
260
+ if extended_timeout > base_timeout
261
+ display_message("⏱️ Codex prompt length #{prompt_length} detected - extending timeout to #{extended_timeout} seconds", type: :info)
262
+ end
263
+ extended_timeout
264
+ end
260
265
  end
261
266
  end
262
267
  end
@@ -14,6 +14,92 @@ 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
+ # Get firewall requirements for Cursor provider
50
+ def self.firewall_requirements
51
+ {
52
+ domains: [
53
+ "cursor.com",
54
+ "www.cursor.com",
55
+ "downloads.cursor.com",
56
+ "api.cursor.sh",
57
+ "cursor.sh",
58
+ "app.cursor.sh",
59
+ "www.cursor.sh"
60
+ ],
61
+ ip_ranges: []
62
+ }
63
+ end
64
+
65
+ # Discover available models from Cursor
66
+ #
67
+ # Note: Cursor doesn't have a public model listing API
68
+ # Returns registry-based models that match Cursor patterns
69
+ #
70
+ # @return [Array<Hash>] Array of discovered models
71
+ def self.discover_models
72
+ return [] unless available?
73
+
74
+ begin
75
+ require_relative "../harness/model_registry"
76
+ registry = Aidp::Harness::ModelRegistry.new
77
+
78
+ # Get all models from registry that Cursor might support
79
+ models = registry.all_families.filter_map do |family|
80
+ next unless supports_model_family?(family)
81
+
82
+ info = registry.get_model_info(family)
83
+ next unless info
84
+
85
+ {
86
+ name: provider_model_name(family),
87
+ family: family,
88
+ tier: info["tier"],
89
+ capabilities: info["capabilities"] || [],
90
+ context_window: info["context_window"],
91
+ provider: "cursor"
92
+ }
93
+ end
94
+
95
+ Aidp.log_info("cursor_provider", "using registry models", count: models.size)
96
+ models
97
+ rescue => e
98
+ Aidp.log_debug("cursor_provider", "discovery failed", error: e.message)
99
+ []
100
+ end
101
+ end
102
+
17
103
  def name
18
104
  "cursor"
19
105
  end
@@ -40,12 +126,6 @@ module Aidp
40
126
  debug_provider("cursor", "Starting execution", {timeout: timeout_seconds})
41
127
  debug_log("📝 Sending prompt to cursor-agent (length: #{prompt.length})", level: :info)
42
128
 
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
129
  # Set up activity monitoring
50
130
  setup_activity_monitoring("cursor-agent", method(:activity_callback))
51
131
  record_activity("Starting cursor-agent execution")
@@ -71,7 +151,7 @@ module Aidp
71
151
  # Use debug_execute_command for better debugging
72
152
  # Use -p mode (designed for non-interactive/script use)
73
153
  # 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)
154
+ result = debug_execute_command("cursor-agent", args: ["-p"], input: prompt, timeout: timeout_seconds)
75
155
 
76
156
  # Log the results
77
157
  debug_command("cursor-agent", args: ["-p"], input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -98,58 +178,6 @@ module Aidp
98
178
 
99
179
  private
100
180
 
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
181
  def activity_callback(state, message, provider)
154
182
  # This is now handled by the animated display thread
155
183
  # Only print static messages for state changes