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.
- checksums.yaml +4 -4
- data/README.md +128 -151
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +471 -0
- data/lib/aidp/analysis/seams.rb +159 -0
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
- data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
- data/lib/aidp/analyze/error_handler.rb +2 -78
- data/lib/aidp/analyze/json_file_storage.rb +292 -0
- data/lib/aidp/analyze/progress.rb +12 -0
- data/lib/aidp/analyze/progress_visualizer.rb +12 -17
- data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
- data/lib/aidp/analyze/runner.rb +256 -87
- data/lib/aidp/analyze/steps.rb +6 -0
- data/lib/aidp/cli/jobs_command.rb +103 -435
- data/lib/aidp/cli.rb +317 -191
- data/lib/aidp/config.rb +298 -10
- data/lib/aidp/debug_logger.rb +195 -0
- data/lib/aidp/debug_mixin.rb +187 -0
- data/lib/aidp/execute/progress.rb +9 -0
- data/lib/aidp/execute/runner.rb +221 -40
- data/lib/aidp/execute/steps.rb +17 -7
- data/lib/aidp/execute/workflow_selector.rb +211 -0
- data/lib/aidp/harness/completion_checker.rb +268 -0
- data/lib/aidp/harness/condition_detector.rb +1526 -0
- data/lib/aidp/harness/config_loader.rb +373 -0
- data/lib/aidp/harness/config_manager.rb +382 -0
- data/lib/aidp/harness/config_schema.rb +1006 -0
- data/lib/aidp/harness/config_validator.rb +355 -0
- data/lib/aidp/harness/configuration.rb +477 -0
- data/lib/aidp/harness/enhanced_runner.rb +494 -0
- data/lib/aidp/harness/error_handler.rb +616 -0
- data/lib/aidp/harness/provider_config.rb +423 -0
- data/lib/aidp/harness/provider_factory.rb +306 -0
- data/lib/aidp/harness/provider_manager.rb +1269 -0
- data/lib/aidp/harness/provider_type_checker.rb +88 -0
- data/lib/aidp/harness/runner.rb +411 -0
- data/lib/aidp/harness/state/errors.rb +28 -0
- data/lib/aidp/harness/state/metrics.rb +219 -0
- data/lib/aidp/harness/state/persistence.rb +128 -0
- data/lib/aidp/harness/state/provider_state.rb +132 -0
- data/lib/aidp/harness/state/ui_state.rb +68 -0
- data/lib/aidp/harness/state/workflow_state.rb +123 -0
- data/lib/aidp/harness/state_manager.rb +586 -0
- data/lib/aidp/harness/status_display.rb +888 -0
- data/lib/aidp/harness/ui/base.rb +16 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
- data/lib/aidp/harness/ui/error_handler.rb +132 -0
- data/lib/aidp/harness/ui/frame_manager.rb +361 -0
- data/lib/aidp/harness/ui/job_monitor.rb +500 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
- data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
- data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
- data/lib/aidp/harness/ui/progress_display.rb +280 -0
- data/lib/aidp/harness/ui/question_collector.rb +141 -0
- data/lib/aidp/harness/ui/spinner_group.rb +184 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
- data/lib/aidp/harness/ui/status_manager.rb +312 -0
- data/lib/aidp/harness/ui/status_widget.rb +280 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
- data/lib/aidp/harness/user_interface.rb +2381 -0
- data/lib/aidp/provider_manager.rb +131 -7
- data/lib/aidp/providers/anthropic.rb +28 -109
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -183
- data/lib/aidp/providers/gemini.rb +24 -109
- data/lib/aidp/providers/macos_ui.rb +99 -5
- data/lib/aidp/providers/opencode.rb +194 -0
- data/lib/aidp/storage/csv_storage.rb +172 -0
- data/lib/aidp/storage/file_manager.rb +214 -0
- data/lib/aidp/storage/json_storage.rb +140 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +56 -35
- data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
- data/templates/COMMON/AGENT_BASE.md +11 -0
- data/templates/EXECUTE/00_PRD.md +4 -4
- data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
- data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
- data/templates/EXECUTE/08_TASKS.md +4 -4
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
- data/templates/README.md +279 -0
- data/templates/aidp-development.yml.example +373 -0
- data/templates/aidp-minimal.yml.example +48 -0
- data/templates/aidp-production.yml.example +475 -0
- data/templates/aidp.yml.example +598 -0
- metadata +106 -64
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
- data/lib/aidp/analyze/data_retention_manager.rb +0 -426
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -425
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
- data/lib/aidp/analyze/memory_manager.rb +0 -365
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -460
- data/lib/aidp/analyze/performance_optimizer.rb +0 -694
- data/lib/aidp/analyze/repository_chunker.rb +0 -704
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -662
- data/lib/aidp/analyze/tool_configuration.rb +0 -456
- data/lib/aidp/analyze/tool_modernization.rb +0 -750
- data/lib/aidp/database/pg_adapter.rb +0 -148
- data/lib/aidp/database_config.rb +0 -69
- data/lib/aidp/database_connection.rb +0 -72
- data/lib/aidp/database_migration.rb +0 -158
- data/lib/aidp/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -47
- data/lib/aidp/jobs/provider_execution_job.rb +0 -96
- data/lib/aidp/project_detector.rb +0 -117
- data/lib/aidp/providers/agent_supervisor.rb +0 -348
- data/lib/aidp/providers/supervised_base.rb +0 -317
- data/lib/aidp/providers/supervised_cursor.rb +0 -22
- data/lib/aidp/sync.rb +0 -13
- 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
|