aidp 0.5.0 → 0.8.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +128 -151
  3. data/bin/aidp +1 -1
  4. data/lib/aidp/analysis/kb_inspector.rb +471 -0
  5. data/lib/aidp/analysis/seams.rb +159 -0
  6. data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
  7. data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
  8. data/lib/aidp/analyze/error_handler.rb +2 -78
  9. data/lib/aidp/analyze/json_file_storage.rb +292 -0
  10. data/lib/aidp/analyze/progress.rb +12 -0
  11. data/lib/aidp/analyze/progress_visualizer.rb +12 -17
  12. data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
  13. data/lib/aidp/analyze/runner.rb +256 -87
  14. data/lib/aidp/analyze/steps.rb +6 -0
  15. data/lib/aidp/cli/jobs_command.rb +103 -435
  16. data/lib/aidp/cli.rb +317 -191
  17. data/lib/aidp/config.rb +298 -10
  18. data/lib/aidp/debug_logger.rb +195 -0
  19. data/lib/aidp/debug_mixin.rb +187 -0
  20. data/lib/aidp/execute/progress.rb +9 -0
  21. data/lib/aidp/execute/runner.rb +221 -40
  22. data/lib/aidp/execute/steps.rb +17 -7
  23. data/lib/aidp/execute/workflow_selector.rb +211 -0
  24. data/lib/aidp/harness/completion_checker.rb +268 -0
  25. data/lib/aidp/harness/condition_detector.rb +1526 -0
  26. data/lib/aidp/harness/config_loader.rb +373 -0
  27. data/lib/aidp/harness/config_manager.rb +382 -0
  28. data/lib/aidp/harness/config_schema.rb +1006 -0
  29. data/lib/aidp/harness/config_validator.rb +355 -0
  30. data/lib/aidp/harness/configuration.rb +477 -0
  31. data/lib/aidp/harness/enhanced_runner.rb +494 -0
  32. data/lib/aidp/harness/error_handler.rb +616 -0
  33. data/lib/aidp/harness/provider_config.rb +423 -0
  34. data/lib/aidp/harness/provider_factory.rb +306 -0
  35. data/lib/aidp/harness/provider_manager.rb +1269 -0
  36. data/lib/aidp/harness/provider_type_checker.rb +88 -0
  37. data/lib/aidp/harness/runner.rb +411 -0
  38. data/lib/aidp/harness/state/errors.rb +28 -0
  39. data/lib/aidp/harness/state/metrics.rb +219 -0
  40. data/lib/aidp/harness/state/persistence.rb +128 -0
  41. data/lib/aidp/harness/state/provider_state.rb +132 -0
  42. data/lib/aidp/harness/state/ui_state.rb +68 -0
  43. data/lib/aidp/harness/state/workflow_state.rb +123 -0
  44. data/lib/aidp/harness/state_manager.rb +586 -0
  45. data/lib/aidp/harness/status_display.rb +888 -0
  46. data/lib/aidp/harness/ui/base.rb +16 -0
  47. data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
  48. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
  49. data/lib/aidp/harness/ui/error_handler.rb +132 -0
  50. data/lib/aidp/harness/ui/frame_manager.rb +361 -0
  51. data/lib/aidp/harness/ui/job_monitor.rb +500 -0
  52. data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
  53. data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
  54. data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
  55. data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
  56. data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
  57. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
  58. data/lib/aidp/harness/ui/progress_display.rb +280 -0
  59. data/lib/aidp/harness/ui/question_collector.rb +141 -0
  60. data/lib/aidp/harness/ui/spinner_group.rb +184 -0
  61. data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
  62. data/lib/aidp/harness/ui/status_manager.rb +312 -0
  63. data/lib/aidp/harness/ui/status_widget.rb +280 -0
  64. data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
  65. data/lib/aidp/harness/user_interface.rb +2381 -0
  66. data/lib/aidp/provider_manager.rb +131 -7
  67. data/lib/aidp/providers/anthropic.rb +28 -109
  68. data/lib/aidp/providers/base.rb +170 -0
  69. data/lib/aidp/providers/cursor.rb +52 -183
  70. data/lib/aidp/providers/gemini.rb +24 -109
  71. data/lib/aidp/providers/macos_ui.rb +99 -5
  72. data/lib/aidp/providers/opencode.rb +194 -0
  73. data/lib/aidp/storage/csv_storage.rb +172 -0
  74. data/lib/aidp/storage/file_manager.rb +214 -0
  75. data/lib/aidp/storage/json_storage.rb +140 -0
  76. data/lib/aidp/version.rb +1 -1
  77. data/lib/aidp.rb +56 -35
  78. data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
  79. data/templates/COMMON/AGENT_BASE.md +11 -0
  80. data/templates/EXECUTE/00_PRD.md +4 -4
  81. data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
  82. data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
  83. data/templates/EXECUTE/08_TASKS.md +4 -4
  84. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
  85. data/templates/README.md +279 -0
  86. data/templates/aidp-development.yml.example +373 -0
  87. data/templates/aidp-minimal.yml.example +48 -0
  88. data/templates/aidp-production.yml.example +475 -0
  89. data/templates/aidp.yml.example +598 -0
  90. metadata +106 -64
  91. data/lib/aidp/analyze/agent_personas.rb +0 -71
  92. data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
  93. data/lib/aidp/analyze/data_retention_manager.rb +0 -426
  94. data/lib/aidp/analyze/database.rb +0 -260
  95. data/lib/aidp/analyze/dependencies.rb +0 -335
  96. data/lib/aidp/analyze/export_manager.rb +0 -425
  97. data/lib/aidp/analyze/focus_guidance.rb +0 -517
  98. data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
  99. data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
  100. data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
  101. data/lib/aidp/analyze/memory_manager.rb +0 -365
  102. data/lib/aidp/analyze/metrics_storage.rb +0 -336
  103. data/lib/aidp/analyze/parallel_processor.rb +0 -460
  104. data/lib/aidp/analyze/performance_optimizer.rb +0 -694
  105. data/lib/aidp/analyze/repository_chunker.rb +0 -704
  106. data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
  107. data/lib/aidp/analyze/storage.rb +0 -662
  108. data/lib/aidp/analyze/tool_configuration.rb +0 -456
  109. data/lib/aidp/analyze/tool_modernization.rb +0 -750
  110. data/lib/aidp/database/pg_adapter.rb +0 -148
  111. data/lib/aidp/database_config.rb +0 -69
  112. data/lib/aidp/database_connection.rb +0 -72
  113. data/lib/aidp/database_migration.rb +0 -158
  114. data/lib/aidp/job_manager.rb +0 -41
  115. data/lib/aidp/jobs/base_job.rb +0 -47
  116. data/lib/aidp/jobs/provider_execution_job.rb +0 -96
  117. data/lib/aidp/project_detector.rb +0 -117
  118. data/lib/aidp/providers/agent_supervisor.rb +0 -348
  119. data/lib/aidp/providers/supervised_base.rb +0 -317
  120. data/lib/aidp/providers/supervised_cursor.rb +0 -22
  121. data/lib/aidp/sync.rb +0 -13
  122. data/lib/aidp/workspace.rb +0 -19
@@ -0,0 +1,888 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ module Harness
5
+ # Real-time status updates and monitoring interface
6
+ class StatusDisplay
7
+ def initialize(provider_manager = nil, metrics_manager = nil, circuit_breaker_manager = nil, error_logger = nil)
8
+ @provider_manager = provider_manager
9
+ @metrics_manager = metrics_manager
10
+ @circuit_breaker_manager = circuit_breaker_manager
11
+ @error_logger = error_logger
12
+
13
+ @start_time = nil
14
+ @current_step = nil
15
+ @current_provider = nil
16
+ @current_model = nil
17
+ @status_thread = nil
18
+ @running = false
19
+ @display_mode = :compact
20
+ @update_interval = 2
21
+ @last_update = Time.now
22
+ @status_data = {}
23
+ @performance_metrics = {}
24
+ @error_summary = {}
25
+ @provider_status = {}
26
+ @model_status = {}
27
+ @circuit_breaker_status = {}
28
+ @token_usage = {}
29
+ @rate_limit_status = {}
30
+ @recovery_status = {}
31
+ @user_feedback_status = {}
32
+ @work_completion_status = {}
33
+ @configuration = {}
34
+ @display_config = initialize_display_config
35
+ @status_formatter = StatusFormatter.new
36
+ @metrics_calculator = MetricsCalculator.new
37
+ @alert_manager = AlertManager.new
38
+ @display_animator = DisplayAnimator.new
39
+ end
40
+
41
+ # Start real-time status updates
42
+ def start_status_updates(display_mode = :compact)
43
+ return if @running
44
+
45
+ @running = true
46
+ @start_time = Time.now
47
+ @display_mode = display_mode
48
+ @last_update = Time.now
49
+
50
+ # Start status display using Async (skip in test mode)
51
+ unless ENV["RACK_ENV"] == "test" || defined?(RSpec)
52
+ require "async"
53
+ Async do |task|
54
+ task.async do
55
+ while @running
56
+ begin
57
+ collect_status_data
58
+ display_status
59
+ check_alerts
60
+ if ENV["RACK_ENV"] == "test" || defined?(RSpec)
61
+ sleep(@update_interval)
62
+ else
63
+ Async::Task.current.sleep(@update_interval)
64
+ end
65
+ rescue => e
66
+ handle_display_error(e)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ # Stop status updates
75
+ def stop_status_updates
76
+ @running = false
77
+ @status_thread&.join
78
+ clear_display
79
+ end
80
+
81
+ # Update current step
82
+ def update_current_step(step_name)
83
+ @current_step = step_name
84
+ @status_data[:current_step] = step_name
85
+ @status_data[:step_start_time] = Time.now
86
+ end
87
+
88
+ # Update current provider
89
+ def update_current_provider(provider_name)
90
+ @current_provider = provider_name
91
+ @status_data[:current_provider] = provider_name
92
+ @status_data[:provider_switch_time] = Time.now
93
+ end
94
+
95
+ # Update current model
96
+ def update_current_model(_provider_name, model_name)
97
+ @current_model = model_name
98
+ @status_data[:current_model] = model_name
99
+ @status_data[:model_switch_time] = Time.now
100
+ end
101
+
102
+ # Update token usage
103
+ def update_token_usage(provider, model, tokens_used, tokens_remaining = nil)
104
+ @token_usage[provider] ||= {}
105
+ @token_usage[provider][model] = {
106
+ used: tokens_used,
107
+ remaining: tokens_remaining,
108
+ last_updated: Time.now
109
+ }
110
+ end
111
+
112
+ # Update rate limit status
113
+ def update_rate_limit_status(provider, model, rate_limit_info)
114
+ @rate_limit_status[provider] ||= {}
115
+ @rate_limit_status[provider][model] = {
116
+ rate_limited: true,
117
+ reset_time: rate_limit_info[:reset_time],
118
+ retry_after: rate_limit_info[:retry_after],
119
+ quota_remaining: rate_limit_info[:quota_remaining],
120
+ quota_limit: rate_limit_info[:quota_limit],
121
+ last_updated: Time.now
122
+ }
123
+ end
124
+
125
+ # Update recovery status
126
+ def update_recovery_status(recovery_type, status, details = {})
127
+ @recovery_status[recovery_type] = {
128
+ status: status,
129
+ details: details,
130
+ last_updated: Time.now
131
+ }
132
+ end
133
+
134
+ # Update user feedback status
135
+ def update_user_feedback_status(feedback_type, status, details = {})
136
+ @user_feedback_status[feedback_type] = {
137
+ status: status,
138
+ details: details,
139
+ last_updated: Time.now
140
+ }
141
+ end
142
+
143
+ # Update work completion status
144
+ def update_work_completion_status(completion_info)
145
+ @work_completion_status = completion_info.merge(last_updated: Time.now)
146
+ end
147
+
148
+ # Update performance metrics
149
+ def update_performance_metrics(metrics)
150
+ @performance_metrics.merge!(metrics)
151
+ @performance_metrics[:last_updated] = Time.now
152
+ end
153
+
154
+ # Update error summary
155
+ def update_error_summary(error_summary)
156
+ @error_summary.merge!(error_summary)
157
+ @error_summary[:last_updated] = Time.now
158
+ end
159
+
160
+ # Set display mode
161
+ def set_display_mode(mode)
162
+ @display_mode = mode
163
+ @display_config[:mode] = mode
164
+ end
165
+
166
+ # Set update interval
167
+ def set_update_interval(interval)
168
+ @update_interval = interval
169
+ @display_config[:update_interval] = interval
170
+ end
171
+
172
+ # Configure display settings
173
+ def configure_display(config)
174
+ @display_config.merge!(config)
175
+ end
176
+
177
+ # Show paused status
178
+ def show_paused_status
179
+ clear_display
180
+ puts "\n⏸️ Harness PAUSED"
181
+ puts " Press 'r' to resume, 's' to stop"
182
+ puts " Current step: #{@current_step}" if @current_step
183
+ puts " Current provider: #{@current_provider}" if @current_provider
184
+ puts " Current model: #{@current_model}" if @current_model
185
+ puts " Duration: #{format_duration(Time.now - @start_time)}" if @start_time
186
+ end
187
+
188
+ # Show resumed status
189
+ def show_resumed_status
190
+ clear_display
191
+ puts "\n▶️ Harness RESUMED"
192
+ puts " Continuing execution..."
193
+ end
194
+
195
+ # Show stopped status
196
+ def show_stopped_status
197
+ clear_display
198
+ puts "\n⏹️ Harness STOPPED"
199
+ puts " Execution terminated by user"
200
+ end
201
+
202
+ # Show rate limit wait
203
+ def show_rate_limit_wait(reset_time)
204
+ clear_display
205
+ remaining = reset_time - Time.now
206
+ puts "\n🚫 Rate limit reached"
207
+ puts " Waiting for reset at #{reset_time.strftime("%H:%M:%S")}"
208
+ puts " Remaining: #{format_duration(remaining)}"
209
+ puts " Press Ctrl+C to cancel"
210
+ end
211
+
212
+ # Update rate limit countdown
213
+ def update_rate_limit_countdown(remaining_seconds)
214
+ clear_display
215
+ puts "\n🚫 Rate limit - waiting..."
216
+ puts " Resets in: #{format_duration(remaining_seconds)}"
217
+ puts " Press Ctrl+C to cancel"
218
+ end
219
+
220
+ # Show completion status
221
+ def show_completion_status(duration, steps_completed, total_steps)
222
+ clear_display
223
+ puts "\n✅ Harness COMPLETED"
224
+ puts " Duration: #{format_duration(duration)}"
225
+ puts " Steps completed: #{steps_completed}/#{total_steps}"
226
+ puts " All workflows finished successfully!"
227
+ end
228
+
229
+ # Show error status
230
+ def show_error_status(error_message)
231
+ clear_display
232
+ puts "\n❌ Harness ERROR"
233
+ puts " Error: #{error_message}"
234
+ puts " Check logs for details"
235
+ end
236
+
237
+ # Cleanup display
238
+ def cleanup
239
+ stop_status_updates
240
+ clear_display
241
+ end
242
+
243
+ # Get comprehensive status data
244
+ def get_status_data
245
+ {
246
+ basic_info: get_basic_status,
247
+ provider_info: get_provider_status,
248
+ performance_info: get_performance_status,
249
+ error_info: get_error_status,
250
+ circuit_breaker_info: get_circuit_breaker_status,
251
+ token_info: get_token_status,
252
+ rate_limit_info: get_rate_limit_status,
253
+ recovery_info: get_recovery_status,
254
+ user_feedback_info: get_user_feedback_status,
255
+ work_completion_info: get_work_completion_status,
256
+ alerts: get_alerts
257
+ }
258
+ end
259
+
260
+ # Export status data
261
+ def export_status_data(format = :json)
262
+ case format
263
+ when :json
264
+ JSON.pretty_generate(get_status_data)
265
+ when :yaml
266
+ get_status_data.to_yaml
267
+ when :text
268
+ format_status_as_text
269
+ else
270
+ raise ArgumentError, "Unsupported format: #{format}"
271
+ end
272
+ end
273
+
274
+ private
275
+
276
+ def initialize_display_config
277
+ {
278
+ mode: :compact,
279
+ update_interval: 2,
280
+ show_animations: true,
281
+ show_colors: true,
282
+ show_metrics: true,
283
+ show_alerts: true,
284
+ max_display_lines: 20,
285
+ auto_scroll: true
286
+ }
287
+ end
288
+
289
+ def collect_status_data
290
+ @last_update = Time.now
291
+
292
+ # Collect data from various managers
293
+ collect_provider_status
294
+ collect_circuit_breaker_status
295
+ collect_metrics_data
296
+ collect_error_data
297
+ collect_performance_data
298
+ end
299
+
300
+ def collect_provider_status
301
+ return unless @provider_manager
302
+ begin
303
+ @provider_status = {
304
+ current_provider: safe_manager_call(@provider_manager, :current_provider),
305
+ current_model: safe_manager_call(@provider_manager, :current_model),
306
+ available_providers: safe_manager_call(@provider_manager, :get_available_providers) || [],
307
+ provider_health: safe_manager_call(@provider_manager, :get_provider_health_status) || {}
308
+ }
309
+ rescue => e
310
+ # Log minimal info without breaking display; keep previous provider_status if available
311
+ @provider_status ||= {}
312
+ @provider_status[:error] = "provider_status_error: #{e.class}"
313
+ end
314
+ end
315
+
316
+ def safe_manager_call(manager, method)
317
+ return nil unless manager.respond_to?(method, true)
318
+ manager.public_send(method)
319
+ rescue NoMethodError
320
+ nil
321
+ end
322
+
323
+ def collect_circuit_breaker_status
324
+ return unless @circuit_breaker_manager
325
+
326
+ @circuit_breaker_status = @circuit_breaker_manager.get_all_states
327
+ end
328
+
329
+ def collect_metrics_data
330
+ return unless @metrics_manager
331
+
332
+ @performance_metrics = @metrics_manager.get_realtime_metrics
333
+ end
334
+
335
+ def collect_error_data
336
+ return unless @error_logger
337
+
338
+ @error_summary = @error_logger.get_log_summary
339
+ end
340
+
341
+ def collect_performance_data
342
+ # Calculate performance metrics
343
+ @performance_metrics[:uptime] = @start_time ? Time.now - @start_time : 0
344
+ @performance_metrics[:step_duration] = calculate_step_duration
345
+ @performance_metrics[:provider_switch_count] = count_provider_switches
346
+ @performance_metrics[:error_rate] = calculate_error_rate
347
+ end
348
+
349
+ def display_status
350
+ return unless @running
351
+
352
+ clear_display
353
+
354
+ case @display_mode
355
+ when :compact
356
+ display_compact_status
357
+ when :detailed
358
+ display_detailed_status
359
+ when :minimal
360
+ display_minimal_status
361
+ when :full
362
+ display_full_status
363
+ else
364
+ display_compact_status
365
+ end
366
+ end
367
+
368
+ def display_compact_status
369
+ duration = @start_time ? Time.now - @start_time : 0
370
+
371
+ puts "\n🔄 Harness Status"
372
+ puts " Duration: #{format_duration(duration)}"
373
+ puts " Step: #{@current_step || "Starting..."}"
374
+ puts " Provider: #{@current_provider || "Initializing..."}"
375
+ puts " Model: #{@current_model || "N/A"}"
376
+ puts " Status: Running"
377
+
378
+ # Show key metrics
379
+ if @performance_metrics[:error_rate] && @performance_metrics[:error_rate] > 0
380
+ puts " Error Rate: #{format_percentage(@performance_metrics[:error_rate])}"
381
+ end
382
+
383
+ if @token_usage[@current_provider] && @token_usage[@current_provider][@current_model]
384
+ tokens = @token_usage[@current_provider][@current_model]
385
+ puts " Tokens: #{tokens[:used]} used"
386
+ puts " Remaining: #{tokens[:remaining]}" if tokens[:remaining]
387
+ end
388
+
389
+ puts " Press Ctrl+C to stop"
390
+ end
391
+
392
+ def display_detailed_status
393
+ duration = @start_time ? Time.now - @start_time : 0
394
+
395
+ puts "\n🔄 Harness Status - Detailed"
396
+ puts " Duration: #{format_duration(duration)}"
397
+ puts " Current Step: #{@current_step || "Starting..."}"
398
+ puts " Provider: #{@current_provider || "Initializing..."}"
399
+ puts " Model: #{@current_model || "N/A"}"
400
+ puts " Status: Running"
401
+
402
+ # Provider information
403
+ if @provider_status[:available_providers]
404
+ puts " Available Providers: #{@provider_status[:available_providers].join(", ")}"
405
+ end
406
+
407
+ # Circuit breaker status
408
+ if @circuit_breaker_status.any?
409
+ open_circuits = @circuit_breaker_status.select { |_, status| status[:state] == :open }
410
+ if open_circuits.any?
411
+ puts " Open Circuit Breakers: #{open_circuits.keys.join(", ")}"
412
+ end
413
+ end
414
+
415
+ # Token usage
416
+ display_token_usage
417
+
418
+ # Error summary
419
+ if @error_summary[:error_summary] && @error_summary[:error_summary][:total_errors] > 0
420
+ puts " Errors: #{@error_summary[:error_summary][:total_errors]} total"
421
+ end
422
+
423
+ puts " Press Ctrl+C to stop"
424
+ end
425
+
426
+ def display_minimal_status
427
+ duration = @start_time ? Time.now - @start_time : 0
428
+ puts "\r🔄 #{@current_step || "Starting"} | #{@current_provider || "Init"} | #{format_duration(duration)}"
429
+ end
430
+
431
+ def display_full_status
432
+ clear_display
433
+ puts "\n" + "=" * 80
434
+ puts "🔄 AIDP HARNESS - FULL STATUS REPORT"
435
+ puts "=" * 80
436
+
437
+ display_basic_info
438
+ display_provider_info
439
+ display_performance_info
440
+ display_error_info
441
+ display_circuit_breaker_info
442
+ display_token_info
443
+ display_rate_limit_info
444
+ display_recovery_info
445
+ display_user_feedback_info
446
+ display_work_completion_info
447
+ display_alerts
448
+
449
+ puts "=" * 80
450
+ puts "Press Ctrl+C to stop | Last updated: #{Time.now.strftime("%H:%M:%S")}"
451
+ end
452
+
453
+ def display_basic_info
454
+ duration = @start_time ? Time.now - @start_time : 0
455
+
456
+ puts "\n📊 BASIC INFORMATION"
457
+ puts " Duration: #{format_duration(duration)}"
458
+ puts " Current Step: #{@current_step || "Starting..."}"
459
+ puts " Provider: #{@current_provider || "Initializing..."}"
460
+ puts " Model: #{@current_model || "N/A"}"
461
+ puts " Status: Running"
462
+ puts " Update Interval: #{@update_interval}s"
463
+ end
464
+
465
+ def display_provider_info
466
+ return unless @provider_status.any?
467
+
468
+ puts "\n🔌 PROVIDER INFORMATION"
469
+ if @provider_status[:available_providers]
470
+ puts " Available Providers: #{@provider_status[:available_providers].join(", ")}"
471
+ end
472
+ if @provider_status[:provider_health]
473
+ puts " Provider Health:"
474
+ @provider_status[:provider_health].each do |provider, health|
475
+ puts " #{provider}: #{health[:status]} (#{format_percentage(health[:health_score])})"
476
+ end
477
+ end
478
+ end
479
+
480
+ def display_token_info
481
+ return unless @token_usage.any?
482
+
483
+ puts "\n🎫 TOKEN USAGE"
484
+ @token_usage.each do |provider, models|
485
+ puts " #{provider}:"
486
+ models.each do |model, usage|
487
+ puts " #{model}: #{usage[:used]} used, #{usage[:remaining]} remaining"
488
+ end
489
+ end
490
+ end
491
+
492
+ def display_performance_info
493
+ return unless @performance_metrics.any?
494
+
495
+ puts "\n⚡ PERFORMANCE METRICS"
496
+ puts " Uptime: #{format_duration(@performance_metrics[:uptime] || 0)}"
497
+ puts " Step Duration: #{format_duration(@performance_metrics[:step_duration] || 0)}"
498
+ puts " Provider Switches: #{@performance_metrics[:provider_switch_count] || 0}"
499
+ puts " Error Rate: #{format_percentage(@performance_metrics[:error_rate] || 0)}"
500
+
501
+ if @performance_metrics[:throughput]
502
+ puts " Throughput: #{@performance_metrics[:throughput]} requests/min"
503
+ end
504
+ end
505
+
506
+ def display_error_info
507
+ return unless @error_summary[:error_summary]
508
+
509
+ error_summary = @error_summary[:error_summary]
510
+ return if error_summary[:total_errors] == 0
511
+
512
+ puts "\n❌ ERROR INFORMATION"
513
+ puts " Total Errors: #{error_summary[:total_errors]}"
514
+ puts " Error Rate: #{format_percentage(error_summary[:error_rate] || 0)}"
515
+
516
+ if error_summary[:errors_by_severity].respond_to?(:any?) && error_summary[:errors_by_severity].any?
517
+ puts " By Severity:"
518
+ error_summary[:errors_by_severity].each do |severity, count|
519
+ puts " #{severity}: #{count}"
520
+ end
521
+ end
522
+
523
+ if error_summary[:errors_by_provider].respond_to?(:any?) && error_summary[:errors_by_provider].any?
524
+ puts " By Provider:"
525
+ error_summary[:errors_by_provider].each do |provider, count|
526
+ puts " #{provider}: #{count}"
527
+ end
528
+ end
529
+ end
530
+
531
+ def display_circuit_breaker_info
532
+ return unless @circuit_breaker_status.any?
533
+
534
+ puts "\n🔒 CIRCUIT BREAKER STATUS"
535
+ @circuit_breaker_status.each do |key, status|
536
+ state_icons = {closed: "🟢", open: "🔴", half_open: "🟡"}
537
+ state_icon = state_icons[status[:state]] || "⚪"
538
+ puts " #{state_icon} #{key}: #{status[:state]} (failures: #{status[:failure_count]})"
539
+ end
540
+ end
541
+
542
+ def display_token_usage
543
+ return unless @token_usage.any?
544
+
545
+ puts "\n🎫 TOKEN USAGE"
546
+ @token_usage.each do |provider, models|
547
+ puts " #{provider}:"
548
+ models.each do |model, usage|
549
+ puts " #{model}: #{usage[:used]} used"
550
+ puts " Remaining: #{usage[:remaining]}" if usage[:remaining]
551
+ end
552
+ end
553
+ end
554
+
555
+ def display_rate_limit_info
556
+ return unless @rate_limit_status.any?
557
+
558
+ puts "\n🚫 RATE LIMIT STATUS"
559
+ @rate_limit_status.each do |provider, models|
560
+ models.each do |model, status|
561
+ if status[:rate_limited]
562
+ puts " #{provider}:#{model}: Rate Limited"
563
+ puts " Reset Time: #{status[:reset_time]&.strftime("%H:%M:%S")}"
564
+ puts " Retry After: #{status[:retry_after]}s"
565
+ puts " Quota: #{status[:quota_remaining]}/#{status[:quota_limit]}" if status[:quota_remaining]
566
+ end
567
+ end
568
+ end
569
+ end
570
+
571
+ def display_recovery_info
572
+ return unless @recovery_status.any?
573
+
574
+ puts "\n🔄 RECOVERY STATUS"
575
+ @recovery_status.each do |type, status|
576
+ puts " #{type}: #{status[:status]}"
577
+ if status[:details].any?
578
+ status[:details].each do |key, value|
579
+ puts " #{key}: #{value}"
580
+ end
581
+ end
582
+ end
583
+ end
584
+
585
+ def display_user_feedback_info
586
+ return unless @user_feedback_status.any?
587
+
588
+ puts "\n💬 USER FEEDBACK STATUS"
589
+ @user_feedback_status.each do |type, status|
590
+ puts " #{type}: #{status[:status]}"
591
+ if status[:details].any?
592
+ status[:details].each do |key, value|
593
+ puts " #{key}: #{value}"
594
+ end
595
+ end
596
+ end
597
+ end
598
+
599
+ def display_work_completion_info
600
+ return unless @work_completion_status.any?
601
+
602
+ puts "\n✅ WORK COMPLETION STATUS"
603
+ if @work_completion_status[:is_complete]
604
+ puts " Status: Complete"
605
+ else
606
+ puts " Status: In Progress"
607
+ end
608
+ puts " Steps Completed: #{@work_completion_status[:completed_steps]}/#{@work_completion_status[:total_steps]}"
609
+ end
610
+
611
+ def display_alerts
612
+ alerts = get_alerts
613
+ return unless alerts.any?
614
+
615
+ puts "\n🚨 ALERTS"
616
+ alerts.each do |alert|
617
+ severity_icons = {critical: "🔴", warning: "🟡", info: "🔵"}
618
+ severity_icon = severity_icons[alert[:severity]] || "⚪"
619
+ puts " #{severity_icon} #{alert[:message]}"
620
+ end
621
+ end
622
+
623
+ def check_alerts
624
+ # Check for various alert conditions
625
+ alerts = []
626
+
627
+ # Check error rate
628
+ if @performance_metrics[:error_rate] && @performance_metrics[:error_rate] > 0.1
629
+ alerts << {
630
+ severity: :warning,
631
+ message: "High error rate: #{format_percentage(@performance_metrics[:error_rate])}",
632
+ timestamp: Time.now
633
+ }
634
+ end
635
+
636
+ # Check circuit breakers
637
+ if @circuit_breaker_status.any?
638
+ open_circuits = @circuit_breaker_status.select { |_, status| status[:state] == :open }
639
+ if open_circuits.any?
640
+ alerts << {
641
+ severity: :warning,
642
+ message: "Open circuit breakers: #{open_circuits.keys.join(", ")}",
643
+ timestamp: Time.now
644
+ }
645
+ end
646
+ end
647
+
648
+ # Check rate limits
649
+ if @rate_limit_status.any?
650
+ rate_limited = @rate_limit_status.any? { |_, models| models.any? { |_, status| status[:rate_limited] } }
651
+ if rate_limited
652
+ alerts << {
653
+ severity: :info,
654
+ message: "Rate limits active",
655
+ timestamp: Time.now
656
+ }
657
+ end
658
+ end
659
+
660
+ @alert_manager.process_alerts(alerts) if alerts.any?
661
+ end
662
+
663
+ def handle_display_error(error)
664
+ puts "\n❌ Display Error: #{error.message}"
665
+ puts " Continuing with status updates..."
666
+ end
667
+
668
+ def get_basic_status
669
+ {
670
+ duration: @start_time ? Time.now - @start_time : 0,
671
+ current_step: @current_step,
672
+ current_provider: @current_provider,
673
+ current_model: @current_model,
674
+ status: @running ? :running : :stopped,
675
+ start_time: @start_time,
676
+ last_update: @last_update
677
+ }
678
+ end
679
+
680
+ def get_provider_status
681
+ @provider_status
682
+ end
683
+
684
+ def get_performance_status
685
+ @performance_metrics
686
+ end
687
+
688
+ def get_error_status
689
+ @error_summary
690
+ end
691
+
692
+ def get_circuit_breaker_status
693
+ @circuit_breaker_status
694
+ end
695
+
696
+ def get_token_status
697
+ @token_usage
698
+ end
699
+
700
+ def get_rate_limit_status
701
+ @rate_limit_status
702
+ end
703
+
704
+ def get_recovery_status
705
+ @recovery_status
706
+ end
707
+
708
+ def get_user_feedback_status
709
+ @user_feedback_status
710
+ end
711
+
712
+ def get_work_completion_status
713
+ @work_completion_status
714
+ end
715
+
716
+ def get_alerts
717
+ @alert_manager.get_active_alerts
718
+ end
719
+
720
+ def calculate_step_duration
721
+ return 0 unless @status_data[:step_start_time]
722
+ Time.now - @status_data[:step_start_time]
723
+ end
724
+
725
+ def count_provider_switches
726
+ @status_data[:provider_switch_count] || 0
727
+ end
728
+
729
+ def calculate_error_rate
730
+ return 0 unless @error_summary[:error_summary]
731
+ @error_summary[:error_summary][:error_rate] || 0
732
+ end
733
+
734
+ def format_status_as_text
735
+ # Generate human-readable text format
736
+ "Status report would be generated here"
737
+ end
738
+
739
+ def clear_display
740
+ # Clear the current line and move cursor to beginning
741
+ print "\r" + " " * 80 + "\r"
742
+ $stdout.flush
743
+ end
744
+
745
+ def format_duration(seconds)
746
+ return "0s" if seconds <= 0
747
+
748
+ hours = (seconds / 3600).to_i
749
+ minutes = ((seconds % 3600) / 60).to_i
750
+ secs = (seconds % 60).to_i
751
+
752
+ parts = []
753
+ parts << "#{hours}h" if hours > 0
754
+ parts << "#{minutes}m" if minutes > 0
755
+ parts << "#{secs}s" if secs > 0 || parts.empty?
756
+
757
+ parts.join(" ")
758
+ end
759
+
760
+ def format_percentage(value)
761
+ return "0%" if value.nil? || value == 0
762
+ "#{(value * 100).round(1)}%"
763
+ end
764
+
765
+ # Helper classes
766
+ class StatusFormatter
767
+ def initialize
768
+ @formatters = {}
769
+ end
770
+
771
+ def format_status(status_data, format_type)
772
+ case format_type
773
+ when :compact
774
+ format_compact_status(status_data)
775
+ when :detailed
776
+ format_detailed_status(status_data)
777
+ when :json
778
+ JSON.pretty_generate(status_data)
779
+ else
780
+ status_data.to_s
781
+ end
782
+ end
783
+
784
+ private
785
+
786
+ def format_compact_status(_status_data)
787
+ "Compact status format"
788
+ end
789
+
790
+ def format_detailed_status(_status_data)
791
+ "Detailed status format"
792
+ end
793
+ end
794
+
795
+ class MetricsCalculator
796
+ def initialize
797
+ @calculators = {}
798
+ end
799
+
800
+ def calculate_metrics(raw_data)
801
+ {
802
+ throughput: calculate_throughput(raw_data),
803
+ error_rate: calculate_error_rate(raw_data),
804
+ availability: calculate_availability(raw_data),
805
+ performance_score: calculate_performance_score(raw_data)
806
+ }
807
+ end
808
+
809
+ private
810
+
811
+ def calculate_throughput(_data)
812
+ # Calculate requests per minute
813
+ 0
814
+ end
815
+
816
+ def calculate_error_rate(_data)
817
+ # Calculate error rate
818
+ 0.0
819
+ end
820
+
821
+ def calculate_availability(_data)
822
+ # Calculate availability percentage
823
+ 1.0
824
+ end
825
+
826
+ def calculate_performance_score(_data)
827
+ # Calculate overall performance score
828
+ 0.95
829
+ end
830
+ end
831
+
832
+ class AlertManager
833
+ def initialize
834
+ @alerts = []
835
+ @alert_history = []
836
+ end
837
+
838
+ def process_alerts(alerts)
839
+ alerts.each do |alert|
840
+ @alerts << alert
841
+ @alert_history << alert
842
+ end
843
+ end
844
+
845
+ def get_active_alerts
846
+ @alerts
847
+ end
848
+
849
+ def clear_alerts
850
+ @alerts.clear
851
+ end
852
+ end
853
+
854
+ class DisplayAnimator
855
+ def initialize
856
+ @animations = {}
857
+ end
858
+
859
+ def animate_status(status_type)
860
+ case status_type
861
+ when :loading
862
+ animate_loading
863
+ when :processing
864
+ animate_processing
865
+ when :waiting
866
+ animate_waiting
867
+ else
868
+ ""
869
+ end
870
+ end
871
+
872
+ private
873
+
874
+ def animate_loading
875
+ "Loading..."
876
+ end
877
+
878
+ def animate_processing
879
+ "Processing..."
880
+ end
881
+
882
+ def animate_waiting
883
+ "Waiting..."
884
+ end
885
+ end
886
+ end
887
+ end
888
+ end