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
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "pathname"
5
+
6
+ module Aidp
7
+ module Harness
8
+ # ModelRegistry manages the static registry of known model families and their tier classifications
9
+ #
10
+ # The registry uses model families (e.g., "claude-3-5-sonnet") rather than specific versioned
11
+ # model IDs (e.g., "claude-3-5-sonnet-20241022"). This design provides:
12
+ # - No version tracking burden - registry tracks families, not every dated version
13
+ # - Future-proofing - new model versions automatically inherit family tier
14
+ # - Provider autonomy - each provider handles version-specific naming
15
+ #
16
+ # Usage:
17
+ # registry = ModelRegistry.new
18
+ # registry.get_model_info("claude-3-5-sonnet")
19
+ # # => { name: "Claude 3.5 Sonnet", tier: "standard", ... }
20
+ #
21
+ # registry.models_for_tier("standard")
22
+ # # => ["claude-3-5-sonnet", "gpt-4-turbo", ...]
23
+ #
24
+ # registry.match_to_family("claude-3-5-sonnet-20241022")
25
+ # # => "claude-3-5-sonnet"
26
+ class ModelRegistry
27
+ class RegistryError < StandardError; end
28
+ class InvalidRegistrySchema < RegistryError; end
29
+ class ModelNotFound < RegistryError; end
30
+
31
+ VALID_TIERS = %w[mini standard advanced].freeze
32
+ VALID_CAPABILITIES = %w[chat code vision tool_use streaming json_mode].freeze
33
+ VALID_SPEEDS = %w[very_fast fast medium slow].freeze
34
+
35
+ attr_reader :registry_data
36
+
37
+ def initialize(registry_path: nil)
38
+ @registry_path = registry_path || default_registry_path
39
+ @registry_data = load_static_registry
40
+ validate_registry_schema
41
+ Aidp.log_debug("model_registry", "initialized", models: @registry_data["model_families"].keys.size)
42
+ end
43
+
44
+ # Get complete model information for a family
45
+ #
46
+ # @param family_name [String] The model family name (e.g., "claude-3-5-sonnet")
47
+ # @return [Hash, nil] Model metadata hash or nil if not found
48
+ def get_model_info(family_name)
49
+ info = @registry_data["model_families"][family_name]
50
+ return nil unless info
51
+
52
+ info.merge("family" => family_name)
53
+ end
54
+
55
+ # Get all model families for a specific tier
56
+ #
57
+ # @param tier [String, Symbol] The tier name (mini, standard, advanced)
58
+ # @return [Array<String>] List of model family names for the tier
59
+ def models_for_tier(tier)
60
+ tier_str = tier.to_s
61
+ unless VALID_TIERS.include?(tier_str)
62
+ Aidp.log_warn("model_registry", "invalid tier requested", tier: tier_str, valid: VALID_TIERS)
63
+ return []
64
+ end
65
+
66
+ families = @registry_data["model_families"].select { |_family, info|
67
+ info["tier"] == tier_str
68
+ }.keys
69
+
70
+ Aidp.log_debug("model_registry", "found models for tier", tier: tier_str, count: families.size)
71
+ families
72
+ end
73
+
74
+ # Classify a model family's tier
75
+ #
76
+ # @param family_name [String] The model family name
77
+ # @return [String, nil] The tier name or nil if not found
78
+ def classify_model_tier(family_name)
79
+ info = get_model_info(family_name)
80
+ tier = info&.fetch("tier", nil)
81
+ Aidp.log_debug("model_registry", "classified tier", family: family_name, tier: tier)
82
+ tier
83
+ end
84
+
85
+ # Match a versioned model name to its family using pattern matching
86
+ #
87
+ # This method attempts to normalize versioned model names (e.g., "claude-3-5-sonnet-20241022")
88
+ # to their family name (e.g., "claude-3-5-sonnet") by testing against version_pattern regexes.
89
+ #
90
+ # @param versioned_name [String] The versioned model name
91
+ # @return [String, nil] The family name if matched, nil otherwise
92
+ def match_to_family(versioned_name)
93
+ @registry_data["model_families"].each do |family, info|
94
+ pattern = info["version_pattern"]
95
+ next unless pattern
96
+
97
+ begin
98
+ regex = Regexp.new("^#{pattern}$")
99
+ if regex.match?(versioned_name)
100
+ Aidp.log_debug("model_registry", "matched to family", versioned: versioned_name, family: family)
101
+ return family
102
+ end
103
+ rescue RegexpError => e
104
+ Aidp.log_error("model_registry", "invalid pattern", family: family, pattern: pattern, error: e.message)
105
+ end
106
+ end
107
+
108
+ # If no pattern matches, check if the versioned_name is itself a family
109
+ if @registry_data["model_families"].key?(versioned_name)
110
+ Aidp.log_debug("model_registry", "exact family match", name: versioned_name)
111
+ return versioned_name
112
+ end
113
+
114
+ Aidp.log_debug("model_registry", "no family match", versioned: versioned_name)
115
+ nil
116
+ end
117
+
118
+ # Get all registered model families
119
+ #
120
+ # @return [Array<String>] List of all model family names
121
+ def all_families
122
+ @registry_data["model_families"].keys
123
+ end
124
+
125
+ # Check if a model family exists in the registry
126
+ #
127
+ # @param family_name [String] The model family name
128
+ # @return [Boolean] True if the family exists
129
+ def family_exists?(family_name)
130
+ @registry_data["model_families"].key?(family_name)
131
+ end
132
+
133
+ # Get all tiers that have at least one model
134
+ #
135
+ # @return [Array<String>] List of tier names
136
+ def available_tiers
137
+ @registry_data["model_families"].values.map { |info| info["tier"] }.uniq.sort
138
+ end
139
+
140
+ private
141
+
142
+ def default_registry_path
143
+ Pathname.new(__dir__).parent.join("data", "model_registry.yml")
144
+ end
145
+
146
+ def load_static_registry
147
+ unless File.exist?(@registry_path)
148
+ raise RegistryError, "Model registry file not found at #{@registry_path}"
149
+ end
150
+
151
+ data = YAML.load_file(@registry_path)
152
+ unless data.is_a?(Hash) && data.key?("model_families")
153
+ raise InvalidRegistrySchema, "Registry must contain 'model_families' key"
154
+ end
155
+
156
+ Aidp.log_info("model_registry", "loaded registry", path: @registry_path, families: data["model_families"].size)
157
+ data
158
+ rescue Psych::SyntaxError => e
159
+ raise InvalidRegistrySchema, "Invalid YAML in registry file: #{e.message}"
160
+ end
161
+
162
+ def validate_registry_schema
163
+ @registry_data["model_families"].each do |family, info|
164
+ validate_model_entry(family, info)
165
+ end
166
+ end
167
+
168
+ def validate_model_entry(family, info)
169
+ # Required fields
170
+ unless info["tier"]
171
+ raise InvalidRegistrySchema, "Model family '#{family}' missing required 'tier' field"
172
+ end
173
+ unless VALID_TIERS.include?(info["tier"])
174
+ raise InvalidRegistrySchema, "Model family '#{family}' has invalid tier '#{info["tier"]}'. Valid: #{VALID_TIERS.join(", ")}"
175
+ end
176
+
177
+ # Optional but validated if present
178
+ if info["capabilities"]
179
+ unless info["capabilities"].is_a?(Array)
180
+ raise InvalidRegistrySchema, "Model family '#{family}' capabilities must be an array"
181
+ end
182
+ invalid_caps = info["capabilities"] - VALID_CAPABILITIES
183
+ unless invalid_caps.empty?
184
+ Aidp.log_warn("model_registry", "unknown capabilities", family: family, unknown: invalid_caps)
185
+ end
186
+ end
187
+
188
+ if info["speed"] && !VALID_SPEEDS.include?(info["speed"])
189
+ Aidp.log_warn("model_registry", "invalid speed", family: family, speed: info["speed"], valid: VALID_SPEEDS)
190
+ end
191
+
192
+ # Validate numeric fields if present
193
+ %w[context_window max_output cost_per_1m_input cost_per_1m_output].each do |field|
194
+ if info[field] && !info[field].is_a?(Numeric)
195
+ raise InvalidRegistrySchema, "Model family '#{family}' field '#{field}' must be numeric"
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -11,6 +11,7 @@ require_relative "error_handler"
11
11
  require_relative "status_display"
12
12
  require_relative "completion_checker"
13
13
  require_relative "../concurrency"
14
+ require_relative "../errors"
14
15
 
15
16
  module Aidp
16
17
  module Harness
@@ -135,6 +136,10 @@ module Aidp
135
136
  end
136
137
  end
137
138
  end
139
+ rescue Aidp::Errors::ConfigurationError
140
+ # Configuration errors should crash immediately (crash-early principle)
141
+ # Re-raise without catching
142
+ raise
138
143
  rescue => e
139
144
  @state = STATES[:error]
140
145
  @last_error = e
@@ -2,12 +2,15 @@
2
2
 
3
3
  require_relative "capability_registry"
4
4
  require_relative "configuration"
5
+ require_relative "../message_display"
5
6
 
6
7
  module Aidp
7
8
  module Harness
8
9
  # Manages thinking depth tier selection and escalation
9
10
  # Integrates with CapabilityRegistry and Configuration to select appropriate models
10
11
  class ThinkingDepthManager
12
+ include Aidp::MessageDisplay
13
+
11
14
  attr_reader :configuration, :registry
12
15
 
13
16
  def initialize(configuration, registry: nil, root_dir: nil)
@@ -150,11 +153,48 @@ module Aidp
150
153
  tier ||= current_tier
151
154
  validate_tier!(tier)
152
155
 
153
- # If provider specified, try to find model for that provider
156
+ # First, try to get models from user's configuration for this tier
157
+ configured_models = configuration.models_for_tier(tier)
158
+
159
+ if configured_models.any?
160
+ # If provider specified, try to find model for that provider in config
161
+ if provider
162
+ matching_model = configured_models.find { |m| m[:provider] == provider }
163
+ if matching_model
164
+ Aidp.log_debug("thinking_depth_manager", "Selected model from user config",
165
+ tier: tier,
166
+ provider: provider,
167
+ model: matching_model[:model])
168
+ return [matching_model[:provider], matching_model[:model], {}]
169
+ end
170
+
171
+ # If provider doesn't support tier and switching allowed, try other providers in config
172
+ unless configuration.allow_provider_switch_for_tier?
173
+ Aidp.log_warn("thinking_depth_manager", "Provider lacks tier in config, switching disabled",
174
+ tier: tier,
175
+ provider: provider)
176
+ return nil
177
+ end
178
+ end
179
+
180
+ # Try any configured model for this tier (prioritize first in list)
181
+ first_model = configured_models.first
182
+ if first_model
183
+ Aidp.log_info("thinking_depth_manager", "Selected model from user config",
184
+ tier: tier,
185
+ original_provider: provider,
186
+ selected_provider: first_model[:provider],
187
+ model: first_model[:model])
188
+ return [first_model[:provider], first_model[:model], {}]
189
+ end
190
+ end
191
+
192
+ # Fall back to catalog-based selection if no models in user config
193
+ # If provider specified, try to find model for that provider in catalog
154
194
  if provider
155
195
  model_name, model_data = @registry.best_model_for_tier(tier, provider)
156
196
  if model_name
157
- Aidp.log_debug("thinking_depth_manager", "Selected model",
197
+ Aidp.log_debug("thinking_depth_manager", "Selected model from catalog",
158
198
  tier: tier,
159
199
  provider: provider,
160
200
  model: model_name)
@@ -163,20 +203,24 @@ module Aidp
163
203
 
164
204
  # If provider doesn't support tier and switching allowed, try others
165
205
  unless configuration.allow_provider_switch_for_tier?
166
- Aidp.log_warn("thinking_depth_manager", "Provider lacks tier, switching disabled",
206
+ Aidp.log_warn("thinking_depth_manager", "Provider lacks tier in catalog, switching disabled",
167
207
  tier: tier,
168
208
  provider: provider)
169
209
  return nil
170
210
  end
171
211
  end
172
212
 
173
- # Try all providers
213
+ # Try all providers in catalog
214
+ if provider && !configuration.allow_provider_switch_for_tier?
215
+ return nil
216
+ end
217
+
174
218
  providers_to_try = provider ? [@registry.provider_names - [provider]].flatten : @registry.provider_names
175
219
 
176
220
  providers_to_try.each do |prov_name|
177
221
  model_name, model_data = @registry.best_model_for_tier(tier, prov_name)
178
222
  if model_name
179
- Aidp.log_info("thinking_depth_manager", "Selected model from alternate provider",
223
+ Aidp.log_info("thinking_depth_manager", "Selected model from catalog (alternate provider)",
180
224
  tier: tier,
181
225
  original_provider: provider,
182
226
  selected_provider: prov_name,
@@ -185,10 +229,23 @@ module Aidp
185
229
  end
186
230
  end
187
231
 
188
- Aidp.log_error("thinking_depth_manager", "No model found for tier",
232
+ # No model found for requested tier - try fallback to other tiers
233
+ Aidp.log_warn("thinking_depth_manager", "No model found for requested tier, trying fallback",
189
234
  tier: tier,
190
235
  provider: provider)
191
- nil
236
+
237
+ result = try_fallback_tiers(tier, provider)
238
+
239
+ unless result
240
+ # Enhanced error message with discovery hints
241
+ display_enhanced_tier_error(tier, provider)
242
+
243
+ Aidp.log_error("thinking_depth_manager", "No model found for tier or fallback tiers",
244
+ tier: tier,
245
+ provider: provider)
246
+ end
247
+
248
+ result
192
249
  end
193
250
 
194
251
  # Get tier for a specific model
@@ -330,6 +387,165 @@ module Aidp
330
387
  # Keep history bounded
331
388
  @tier_history.shift if @tier_history.size > 100
332
389
  end
390
+
391
+ # Try to find a model in fallback tiers when requested tier has no models
392
+ # Tries lower tiers first (cheaper), then higher tiers
393
+ # Returns [provider_name, model_name, model_data] or nil
394
+ def try_fallback_tiers(requested_tier, provider)
395
+ # Generate fallback order: try lower tiers first, then higher
396
+ fallback_tiers = generate_fallback_tier_order(requested_tier)
397
+
398
+ fallback_tiers.each do |fallback_tier|
399
+ # First, try user's configuration for this fallback tier
400
+ configured_models = configuration.models_for_tier(fallback_tier)
401
+
402
+ if configured_models.any?
403
+ # Try specified provider first if given
404
+ if provider
405
+ matching_model = configured_models.find { |m| m[:provider] == provider }
406
+ if matching_model
407
+ Aidp.log_warn("thinking_depth_manager", "Falling back to different tier (from config)",
408
+ requested_tier: requested_tier,
409
+ fallback_tier: fallback_tier,
410
+ provider: provider,
411
+ model: matching_model[:model])
412
+ return [matching_model[:provider], matching_model[:model], {}]
413
+ end
414
+ end
415
+
416
+ # Try any configured model for this tier
417
+ first_model = configured_models.first
418
+ if first_model
419
+ Aidp.log_warn("thinking_depth_manager", "Falling back to different tier and provider (from config)",
420
+ requested_tier: requested_tier,
421
+ fallback_tier: fallback_tier,
422
+ requested_provider: provider,
423
+ fallback_provider: first_model[:provider],
424
+ model: first_model[:model])
425
+ return [first_model[:provider], first_model[:model], {}]
426
+ end
427
+ end
428
+
429
+ # Fall back to catalog if no models in config
430
+ # Try specified provider first if given
431
+ if provider
432
+ model_name, model_data = @registry.best_model_for_tier(fallback_tier, provider)
433
+ if model_name
434
+ Aidp.log_warn("thinking_depth_manager", "Falling back to different tier (from catalog)",
435
+ requested_tier: requested_tier,
436
+ fallback_tier: fallback_tier,
437
+ provider: provider,
438
+ model: model_name)
439
+ return [provider, model_name, model_data]
440
+ end
441
+ end
442
+
443
+ # Try all available providers in catalog
444
+ @registry.provider_names.each do |prov_name|
445
+ next if prov_name == provider # Skip if already tried above
446
+
447
+ model_name, model_data = @registry.best_model_for_tier(fallback_tier, prov_name)
448
+ if model_name
449
+ Aidp.log_warn("thinking_depth_manager", "Falling back to different tier and provider (from catalog)",
450
+ requested_tier: requested_tier,
451
+ fallback_tier: fallback_tier,
452
+ requested_provider: provider,
453
+ fallback_provider: prov_name,
454
+ model: model_name)
455
+ return [prov_name, model_name, model_data]
456
+ end
457
+ end
458
+ end
459
+
460
+ nil
461
+ end
462
+
463
+ # Generate fallback tier order: lower tiers first (cheaper), then higher
464
+ # For example, if tier is "standard", try: mini, thinking, pro, max
465
+ def generate_fallback_tier_order(tier)
466
+ current_priority = @registry.tier_priority(tier) || 1
467
+ all_tiers = CapabilityRegistry::VALID_TIERS
468
+
469
+ # Split into lower and higher tiers
470
+ lower_tiers = all_tiers.select { |t| (@registry.tier_priority(t) || 0) < current_priority }.reverse
471
+ higher_tiers = all_tiers.select { |t| (@registry.tier_priority(t) || 0) > current_priority }
472
+
473
+ # Try lower tiers first (cost optimization), then higher tiers
474
+ lower_tiers + higher_tiers
475
+ end
476
+
477
+ # Display enhanced error message with discovery hints
478
+ def display_enhanced_tier_error(tier, provider)
479
+ return unless defined?(Aidp::MessageDisplay)
480
+
481
+ # Check if there are discovered models in cache
482
+ discovered_models = check_discovered_models(tier, provider)
483
+
484
+ if discovered_models&.any?
485
+ display_tier_error_with_suggestions(tier, provider, discovered_models)
486
+ else
487
+ display_tier_error_with_discovery_hint(tier, provider)
488
+ end
489
+ end
490
+
491
+ # Check cache for discovered models for this tier
492
+ def check_discovered_models(tier, provider)
493
+ require_relative "model_cache"
494
+ require_relative "model_registry"
495
+
496
+ cache = Aidp::Harness::ModelCache.new
497
+ registry = Aidp::Harness::ModelRegistry.new
498
+
499
+ # Get all cached models for the provider
500
+ cached_models = cache.get_cached_models(provider)
501
+ return nil unless cached_models&.any?
502
+
503
+ # Filter to models for the requested tier
504
+ tier_models = cached_models.select do |model|
505
+ family = model[:family] || model["family"]
506
+ model_info = registry.get_model_info(family)
507
+ model_info && model_info["tier"] == tier.to_s
508
+ end
509
+
510
+ tier_models.any? ? tier_models : nil
511
+ rescue => e
512
+ Aidp.log_debug("thinking_depth_manager", "failed to check cached models",
513
+ error: e.message)
514
+ nil
515
+ end
516
+
517
+ # Display error with model suggestions from cache
518
+ def display_tier_error_with_suggestions(tier, provider, models)
519
+ display_message("\nāŒ No model configured for '#{tier}' tier", type: :error)
520
+ display_message(" Provider: #{provider}", type: :info) if provider
521
+
522
+ display_message("\nšŸ’” Discovered models for this tier:", type: :highlight)
523
+ models.first(3).each do |model|
524
+ model_name = model[:name] || model["name"]
525
+ display_message(" - #{model_name}", type: :info)
526
+ end
527
+
528
+ display_message("\n Add to aidp.yml:", type: :highlight)
529
+ display_message(" providers:", type: :info)
530
+ display_message(" #{provider}:", type: :info)
531
+ display_message(" thinking:", type: :info)
532
+ display_message(" tiers:", type: :info)
533
+ display_message(" #{tier}:", type: :info)
534
+ display_message(" models:", type: :info)
535
+ first_model = models.first[:name] || models.first["name"]
536
+ display_message(" - model: #{first_model}\n", type: :info)
537
+ end
538
+
539
+ # Display error with discovery hint
540
+ def display_tier_error_with_discovery_hint(tier, provider)
541
+ display_message("\nāŒ No model configured for '#{tier}' tier", type: :error)
542
+ display_message(" Provider: #{provider}", type: :info) if provider
543
+
544
+ display_message("\nšŸ’” Suggested actions:", type: :highlight)
545
+ display_message(" 1. Run 'aidp models discover' to find available models", type: :info)
546
+ display_message(" 2. Run 'aidp models list --tier=#{tier}' to see models for this tier", type: :info)
547
+ display_message(" 3. Run 'aidp models validate' to check your configuration\n", type: :info)
548
+ end
333
549
  end
334
550
  end
335
551
  end
@@ -25,7 +25,6 @@ module Aidp
25
25
 
26
26
  # Instance helper for displaying a colored message via TTY::Prompt
27
27
  def display_message(message, type: :info)
28
- return if suppress_display_message?(message)
29
28
  # Ensure message is UTF-8 encoded to handle emoji and special characters
30
29
  message_str = message.to_s
31
30
  message_str = message_str.force_encoding("UTF-8") if message_str.encoding.name == "ASCII-8BIT"
@@ -43,32 +42,9 @@ module Aidp
43
42
  end
44
43
  end
45
44
 
46
- # Check if specific display message should be suppressed in test/CI environments
47
- def suppress_display_message?(message)
48
- return false unless in_test_environment?
49
-
50
- message_str = message.to_s
51
- # Only suppress specific automated status messages, not CLI output
52
- message_str.include?("šŸ”„ Provider switch:") ||
53
- message_str.include?("šŸ”„ Model switch:") ||
54
- message_str.include?("šŸ”“ Circuit breaker opened") ||
55
- message_str.include?("🟢 Circuit breaker reset") ||
56
- message_str.include?("āŒ No providers available") ||
57
- message_str.include?("āŒ No models available") ||
58
- message_str.include?("šŸ“Š Execution Summary") ||
59
- message_str.include?("ā–¶ļø [") || # Workstream execution messages
60
- message_str.include?("āœ… [") || # Workstream success messages
61
- message_str.include?("āŒ [") # Workstream failure messages
62
- end
63
-
64
- def in_test_environment?
65
- ENV["RSPEC_RUNNING"] || ENV["CI"] || ENV["RAILS_ENV"] == "test" || ENV["RACK_ENV"] == "test"
66
- end
67
-
68
45
  module ClassMethods
69
46
  # Class-level display helper (uses fresh prompt to respect $stdout changes)
70
47
  def display_message(message, type: :info)
71
- return if suppress_display_message?(message)
72
48
  # Ensure message is UTF-8 encoded to handle emoji and special characters
73
49
  message_str = message.to_s
74
50
  message_str = message_str.force_encoding("UTF-8") if message_str.encoding.name == "ASCII-8BIT"
@@ -78,28 +54,6 @@ module Aidp
78
54
 
79
55
  private
80
56
 
81
- # Check if specific display message should be suppressed in test/CI environments
82
- def suppress_display_message?(message)
83
- return false unless in_test_environment?
84
-
85
- message_str = message.to_s
86
- # Only suppress specific automated status messages, not CLI output
87
- message_str.include?("šŸ”„ Provider switch:") ||
88
- message_str.include?("šŸ”„ Model switch:") ||
89
- message_str.include?("šŸ”“ Circuit breaker opened") ||
90
- message_str.include?("🟢 Circuit breaker reset") ||
91
- message_str.include?("āŒ No providers available") ||
92
- message_str.include?("āŒ No models available") ||
93
- message_str.include?("šŸ“Š Execution Summary") ||
94
- message_str.include?("ā–¶ļø [") || # Workstream execution messages
95
- message_str.include?("āœ… [") || # Workstream success messages
96
- message_str.include?("āŒ [") # Workstream failure messages
97
- end
98
-
99
- def in_test_environment?
100
- ENV["RSPEC_RUNNING"] || ENV["CI"] || ENV["RAILS_ENV"] == "test" || ENV["RACK_ENV"] == "test"
101
- end
102
-
103
57
  # Don't memoize - create fresh prompt each time to respect $stdout redirection in tests
104
58
  def class_message_display_prompt
105
59
  TTY::Prompt.new
@@ -65,8 +65,7 @@ module Aidp
65
65
  # supports_json_mode: true,
66
66
  # supports_tool_use: true,
67
67
  # supports_vision: false,
68
- # supports_file_upload: true,
69
- # streaming: true
68
+ # supports_file_upload: true
70
69
  # }
71
70
  def capabilities
72
71
  {
@@ -75,8 +74,7 @@ module Aidp
75
74
  supports_json_mode: false,
76
75
  supports_tool_use: false,
77
76
  supports_vision: false,
78
- supports_file_upload: false,
79
- streaming: false
77
+ supports_file_upload: false
80
78
  }
81
79
  end
82
80