aidp 0.27.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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -0
  3. data/lib/aidp/cli/models_command.rb +5 -6
  4. data/lib/aidp/cli.rb +10 -8
  5. data/lib/aidp/config.rb +54 -0
  6. data/lib/aidp/debug_mixin.rb +23 -1
  7. data/lib/aidp/execute/agent_signal_parser.rb +22 -0
  8. data/lib/aidp/execute/repl_macros.rb +2 -2
  9. data/lib/aidp/execute/steps.rb +94 -1
  10. data/lib/aidp/execute/work_loop_runner.rb +209 -17
  11. data/lib/aidp/execute/workflow_selector.rb +2 -25
  12. data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
  13. data/lib/aidp/harness/ai_decision_engine.rb +35 -2
  14. data/lib/aidp/harness/config_manager.rb +0 -5
  15. data/lib/aidp/harness/config_schema.rb +8 -0
  16. data/lib/aidp/harness/configuration.rb +27 -19
  17. data/lib/aidp/harness/enhanced_runner.rb +1 -4
  18. data/lib/aidp/harness/error_handler.rb +1 -72
  19. data/lib/aidp/harness/provider_factory.rb +11 -2
  20. data/lib/aidp/harness/state_manager.rb +0 -7
  21. data/lib/aidp/harness/thinking_depth_manager.rb +47 -68
  22. data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
  23. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
  24. data/lib/aidp/harness/ui/progress_display.rb +6 -2
  25. data/lib/aidp/harness/user_interface.rb +0 -58
  26. data/lib/aidp/init/runner.rb +7 -2
  27. data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
  28. data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
  29. data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
  30. data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
  31. data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
  32. data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
  33. data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
  34. data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
  35. data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
  36. data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
  37. data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
  38. data/lib/aidp/planning/parsers/document_parser.rb +141 -0
  39. data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
  40. data/lib/aidp/provider_manager.rb +8 -32
  41. data/lib/aidp/providers/aider.rb +264 -0
  42. data/lib/aidp/providers/anthropic.rb +74 -2
  43. data/lib/aidp/providers/base.rb +25 -1
  44. data/lib/aidp/providers/codex.rb +26 -3
  45. data/lib/aidp/providers/cursor.rb +16 -0
  46. data/lib/aidp/providers/gemini.rb +13 -0
  47. data/lib/aidp/providers/github_copilot.rb +17 -0
  48. data/lib/aidp/providers/kilocode.rb +11 -0
  49. data/lib/aidp/providers/opencode.rb +11 -0
  50. data/lib/aidp/setup/wizard.rb +249 -39
  51. data/lib/aidp/version.rb +1 -1
  52. data/lib/aidp/watch/build_processor.rb +211 -30
  53. data/lib/aidp/watch/change_request_processor.rb +128 -14
  54. data/lib/aidp/watch/ci_fix_processor.rb +103 -37
  55. data/lib/aidp/watch/ci_log_extractor.rb +258 -0
  56. data/lib/aidp/watch/github_state_extractor.rb +177 -0
  57. data/lib/aidp/watch/implementation_verifier.rb +284 -0
  58. data/lib/aidp/watch/plan_generator.rb +7 -43
  59. data/lib/aidp/watch/plan_processor.rb +7 -6
  60. data/lib/aidp/watch/repository_client.rb +245 -17
  61. data/lib/aidp/watch/review_processor.rb +98 -17
  62. data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
  63. data/lib/aidp/watch/runner.rb +181 -29
  64. data/lib/aidp/watch/state_store.rb +22 -1
  65. data/lib/aidp/workflows/definitions.rb +147 -0
  66. data/lib/aidp/workstream_cleanup.rb +245 -0
  67. data/lib/aidp/worktree.rb +19 -0
  68. data/templates/aidp.yml.example +57 -0
  69. data/templates/implementation/generate_tdd_specs.md +213 -0
  70. data/templates/implementation/iterative_implementation.md +122 -0
  71. data/templates/planning/agile/analyze_feedback.md +183 -0
  72. data/templates/planning/agile/generate_iteration_plan.md +179 -0
  73. data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
  74. data/templates/planning/agile/generate_marketing_report.md +162 -0
  75. data/templates/planning/agile/generate_mvp_scope.md +127 -0
  76. data/templates/planning/agile/generate_user_test_plan.md +143 -0
  77. data/templates/planning/agile/ingest_feedback.md +174 -0
  78. data/templates/planning/assemble_project_plan.md +113 -0
  79. data/templates/planning/assign_personas.md +108 -0
  80. data/templates/planning/create_tasks.md +52 -6
  81. data/templates/planning/generate_gantt.md +86 -0
  82. data/templates/planning/generate_wbs.md +85 -0
  83. data/templates/planning/initialize_planning_mode.md +70 -0
  84. data/templates/skills/README.md +2 -2
  85. data/templates/skills/marketing_strategist/SKILL.md +279 -0
  86. data/templates/skills/product_manager/SKILL.md +177 -0
  87. data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
  88. data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
  89. data/templates/skills/ux_researcher/SKILL.md +222 -0
  90. metadata +39 -1
@@ -0,0 +1,264 @@
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)
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
@@ -64,6 +64,18 @@ module Aidp
64
64
  end
65
65
  end
66
66
 
67
+ # Get firewall requirements for Anthropic provider
68
+ def self.firewall_requirements
69
+ {
70
+ domains: [
71
+ "api.anthropic.com",
72
+ "claude.ai",
73
+ "console.anthropic.com"
74
+ ],
75
+ ip_ranges: []
76
+ }
77
+ end
78
+
67
79
  class << self
68
80
  private
69
81
 
@@ -250,6 +262,11 @@ module Aidp
250
262
  # Build command arguments
251
263
  args = ["--print", "--output-format=text"]
252
264
 
265
+ # Add model if specified
266
+ if @model && !@model.empty?
267
+ args << "--model" << @model
268
+ end
269
+
253
270
  # Check if we should skip permissions (devcontainer support)
254
271
  if should_skip_permissions?
255
272
  args << "--dangerously-skip-permissions"
@@ -265,14 +282,26 @@ module Aidp
265
282
  if result.exit_status == 0
266
283
  result.out
267
284
  else
268
- # Detect auth issues in stdout/stderr (Claude sometimes prints JSON with auth error to stdout)
285
+ # Detect issues in stdout/stderr (Claude sometimes prints to stdout)
269
286
  combined = [result.out, result.err].compact.join("\n")
287
+
288
+ # Check for rate limit (Session limit reached)
289
+ if combined.match?(/session limit reached/i)
290
+ Aidp.log_debug("anthropic_provider", "rate_limit_detected",
291
+ exit_code: result.exit_status,
292
+ message: combined)
293
+ notify_rate_limit(combined)
294
+ error_message = "Rate limit reached for Claude CLI.\n#{combined}"
295
+ debug_error(StandardError.new(error_message), {exit_code: result.exit_status, stdout: result.out, stderr: result.err})
296
+ raise error_message
297
+ end
298
+
299
+ # Check for auth issues
270
300
  if combined.downcase.include?("oauth token has expired") || combined.downcase.include?("authentication_error")
271
301
  error_message = "Authentication error from Claude CLI: token expired or invalid.\n" \
272
302
  "Run 'claude /login' or refresh credentials.\n" \
273
303
  "Note: Model discovery requires valid authentication."
274
304
  debug_error(StandardError.new(error_message), {exit_code: result.exit_status, stdout: result.out, stderr: result.err})
275
- # Raise a recognizable error for classifier
276
305
  raise error_message
277
306
  end
278
307
 
@@ -287,6 +316,49 @@ module Aidp
287
316
 
288
317
  private
289
318
 
319
+ # Notify harness about rate limit detection
320
+ def notify_rate_limit(message)
321
+ return unless @harness_context
322
+
323
+ # Extract reset time from message (e.g., "resets 4am")
324
+ reset_time = extract_reset_time_from_message(message)
325
+
326
+ # Notify provider manager if available
327
+ if @harness_context.respond_to?(:provider_manager)
328
+ provider_manager = @harness_context.provider_manager
329
+ if provider_manager.respond_to?(:mark_rate_limited)
330
+ provider_manager.mark_rate_limited("anthropic", reset_time)
331
+ Aidp.log_debug("anthropic_provider", "notified_provider_manager",
332
+ reset_time: reset_time)
333
+ end
334
+ end
335
+ rescue => e
336
+ Aidp.log_debug("anthropic_provider", "notify_rate_limit_failed",
337
+ error: e.message)
338
+ end
339
+
340
+ # Extract reset time from rate limit message
341
+ def extract_reset_time_from_message(message)
342
+ # Handle expressions like "resets 4am" or "reset at 4:30pm"
343
+ time_of_day_match = message.match(/reset(?:s)?(?:\s+at)?\s+(\d{1,2})(?::(\d{2}))?\s*(am|pm)/i)
344
+ if time_of_day_match
345
+ hour = time_of_day_match[1].to_i
346
+ minute = time_of_day_match[2] ? time_of_day_match[2].to_i : 0
347
+ meridiem = time_of_day_match[3].downcase
348
+
349
+ hour %= 12
350
+ hour += 12 if meridiem == "pm"
351
+
352
+ now = Time.now
353
+ reset_time = Time.new(now.year, now.month, now.day, hour, minute, 0, now.utc_offset)
354
+ reset_time += 86_400 if reset_time <= now
355
+ return reset_time
356
+ end
357
+
358
+ # Default to 1 hour from now if no specific time found
359
+ Time.now + 3600
360
+ end
361
+
290
362
  # Check if we should skip permissions based on devcontainer configuration
291
363
  # Overrides base class to add logging and Claude-specific config check
292
364
  def should_skip_permissions?
@@ -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
@@ -360,6 +367,23 @@ module Aidp
360
367
  []
361
368
  end
362
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
+
363
387
  protected
364
388
 
365
389
  # Log message to job if in background mode
@@ -39,6 +39,23 @@ module Aidp
39
39
  discover_models_from_registry(MODEL_PATTERN, "codex")
40
40
  end
41
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
+
42
59
  def name
43
60
  "codex"
44
61
  end
@@ -94,6 +111,11 @@ module Aidp
94
111
  # Use non-interactive mode (exec) for automation
95
112
  args = ["exec", prompt]
96
113
 
114
+ # Add model if configured
115
+ if @model && !@model.empty?
116
+ args += ["--model", @model]
117
+ end
118
+
97
119
  # Add session support if provided (codex may support session/thread continuation)
98
120
  if session && !session.empty?
99
121
  args += ["--session", session]
@@ -149,9 +171,10 @@ module Aidp
149
171
  args += ["--session", session]
150
172
  end
151
173
 
152
- # Add model selection
153
- if model
154
- 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]
155
178
  end
156
179
 
157
180
  # Add approval flag - but warn about interactive behavior
@@ -46,6 +46,22 @@ module Aidp
46
46
  family_name.match?(/^(claude|gpt|cursor)-/)
47
47
  end
48
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
+
49
65
  # Discover available models from Cursor
50
66
  #
51
67
  # Note: Cursor doesn't have a public model listing API
@@ -55,6 +55,19 @@ module Aidp
55
55
  discover_models_from_registry(MODEL_PATTERN, "gemini")
56
56
  end
57
57
 
58
+ # Get firewall requirements for Gemini provider
59
+ def self.firewall_requirements
60
+ {
61
+ domains: [
62
+ "generativelanguage.googleapis.com",
63
+ "oauth2.googleapis.com",
64
+ "accounts.google.com",
65
+ "www.googleapis.com"
66
+ ],
67
+ ip_ranges: []
68
+ }
69
+ end
70
+
58
71
  def name
59
72
  "gemini"
60
73
  end
@@ -14,6 +14,23 @@ module Aidp
14
14
  !!Aidp::Util.which("copilot")
15
15
  end
16
16
 
17
+ # Get firewall requirements for GitHub Copilot provider
18
+ def self.firewall_requirements
19
+ {
20
+ domains: [
21
+ "copilot-proxy.githubusercontent.com",
22
+ "api.githubcopilot.com",
23
+ "copilot-telemetry.githubusercontent.com",
24
+ "default.exp-tas.com",
25
+ "copilot-completions.githubusercontent.com",
26
+ "business.githubcopilot.com",
27
+ "enterprise.githubcopilot.com",
28
+ "individual.githubcopilot.com"
29
+ ],
30
+ ip_ranges: []
31
+ }
32
+ end
33
+
17
34
  def name
18
35
  "github_copilot"
19
36
  end
@@ -37,6 +37,17 @@ module Aidp
37
37
  discover_models_from_registry(MODEL_PATTERN, "kilocode")
38
38
  end
39
39
 
40
+ # Get firewall requirements for Kilocode provider
41
+ def self.firewall_requirements
42
+ {
43
+ domains: [
44
+ "kilocode.ai",
45
+ "api.kilocode.ai"
46
+ ],
47
+ ip_ranges: []
48
+ }
49
+ end
50
+
40
51
  def name
41
52
  "kilocode"
42
53
  end
@@ -37,6 +37,17 @@ module Aidp
37
37
  discover_models_from_registry(MODEL_PATTERN, "opencode")
38
38
  end
39
39
 
40
+ # Get firewall requirements for OpenCode provider
41
+ def self.firewall_requirements
42
+ {
43
+ domains: [
44
+ "api.opencode.ai",
45
+ "auth.opencode.ai"
46
+ ],
47
+ ip_ranges: []
48
+ }
49
+ end
50
+
40
51
  def name
41
52
  "opencode"
42
53
  end