aidp 0.7.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 +60 -214
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +38 -23
- data/lib/aidp/analysis/seams.rb +2 -31
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +0 -13
- data/lib/aidp/analysis/tree_sitter_scan.rb +3 -20
- data/lib/aidp/analyze/error_handler.rb +2 -75
- 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/cli/jobs_command.rb +100 -432
- data/lib/aidp/cli.rb +309 -239
- 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 -103
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -181
- data/lib/aidp/providers/gemini.rb +24 -107
- 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 +54 -39
- 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 +93 -69
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -439
- data/lib/aidp/analyze/data_retention_manager.rb +0 -421
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -418
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -533
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -499
- data/lib/aidp/analyze/memory_manager.rb +0 -339
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -454
- data/lib/aidp/analyze/performance_optimizer.rb +0 -691
- data/lib/aidp/analyze/repository_chunker.rb +0 -697
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -655
- data/lib/aidp/analyze/tool_configuration.rb +0 -441
- 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/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -45
- data/lib/aidp/jobs/provider_execution_job.rb +0 -83
- 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,500 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tty-prompt"
|
4
|
+
require "pastel"
|
5
|
+
require_relative "base"
|
6
|
+
require_relative "status_manager"
|
7
|
+
require_relative "frame_manager"
|
8
|
+
|
9
|
+
module Aidp
|
10
|
+
module Harness
|
11
|
+
module UI
|
12
|
+
# Real-time job monitoring and status tracking
|
13
|
+
class JobMonitor < Base
|
14
|
+
class JobMonitorError < StandardError; end
|
15
|
+
class JobNotFoundError < JobMonitorError; end
|
16
|
+
class MonitorError < JobMonitorError; end
|
17
|
+
|
18
|
+
JOB_STATUSES = {
|
19
|
+
pending: "Pending",
|
20
|
+
running: "Running",
|
21
|
+
completed: "Completed",
|
22
|
+
failed: "Failed",
|
23
|
+
cancelled: "Cancelled",
|
24
|
+
retrying: "Retrying"
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
JOB_PRIORITIES = {
|
28
|
+
low: "Low",
|
29
|
+
normal: "Normal",
|
30
|
+
high: "High",
|
31
|
+
urgent: "Urgent"
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
def initialize(ui_components = {})
|
35
|
+
super()
|
36
|
+
@prompt = TTY::Prompt.new
|
37
|
+
@pastel = Pastel.new
|
38
|
+
@status_manager = ui_components[:status_manager] || StatusManager.new
|
39
|
+
@frame_manager = ui_components[:frame_manager] || FrameManager.new
|
40
|
+
@formatter = ui_components[:formatter] || JobMonitorFormatter.new
|
41
|
+
|
42
|
+
@jobs = {}
|
43
|
+
@job_history = []
|
44
|
+
@monitoring_active = false
|
45
|
+
@monitor_thread = nil
|
46
|
+
@monitor_mutex = Mutex.new
|
47
|
+
@update_callbacks = []
|
48
|
+
end
|
49
|
+
|
50
|
+
def register_job(job_id, job_data)
|
51
|
+
validate_job_id(job_id)
|
52
|
+
validate_job_data(job_data)
|
53
|
+
|
54
|
+
@monitor_mutex.synchronize do
|
55
|
+
job = create_job_entry(job_id, job_data)
|
56
|
+
@jobs[job_id] = job
|
57
|
+
record_job_event(job_id, :registered, job_data)
|
58
|
+
notify_callbacks(:job_registered, job)
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
raise MonitorError, "Failed to register job: #{e.message}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_job_status(job_id, status, additional_data = {})
|
65
|
+
validate_job_id(job_id)
|
66
|
+
validate_job_status(status)
|
67
|
+
|
68
|
+
@monitor_mutex.synchronize do
|
69
|
+
job = @jobs[job_id]
|
70
|
+
raise JobNotFoundError, "Job not found: #{job_id}" unless job
|
71
|
+
|
72
|
+
old_status = job[:status]
|
73
|
+
job[:status] = status
|
74
|
+
job[:last_updated] = Time.now
|
75
|
+
job.merge!(additional_data)
|
76
|
+
|
77
|
+
record_job_event(job_id, :status_changed, {from: old_status, to: status})
|
78
|
+
notify_callbacks(:job_status_changed, job, old_status)
|
79
|
+
end
|
80
|
+
rescue JobNotFoundError => e
|
81
|
+
raise e
|
82
|
+
rescue => e
|
83
|
+
raise MonitorError, "Failed to update job status: #{e.message}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_job_status(job_id)
|
87
|
+
validate_job_id(job_id)
|
88
|
+
|
89
|
+
@monitor_mutex.synchronize do
|
90
|
+
job = @jobs[job_id]
|
91
|
+
raise JobNotFoundError, "Job not found: #{job_id}" unless job
|
92
|
+
job.dup
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def has_job?(job_id)
|
97
|
+
validate_job_id(job_id)
|
98
|
+
@monitor_mutex.synchronize { @jobs.key?(job_id) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_all_jobs
|
102
|
+
@monitor_mutex.synchronize { @jobs.dup }
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_jobs_by_status(status)
|
106
|
+
validate_job_status(status)
|
107
|
+
|
108
|
+
@monitor_mutex.synchronize do
|
109
|
+
@jobs.select { |_, job| job[:status] == status }
|
110
|
+
end
|
111
|
+
rescue JobMonitorError => e
|
112
|
+
raise MonitorError, "Failed to get jobs by status: #{e.message}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_jobs_by_priority(priority)
|
116
|
+
validate_job_priority(priority)
|
117
|
+
|
118
|
+
@monitor_mutex.synchronize do
|
119
|
+
@jobs.select { |_, job| job[:priority] == priority }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def start_monitoring(interval_seconds = 1.0)
|
124
|
+
return if @monitoring_active
|
125
|
+
|
126
|
+
@monitoring_active = true
|
127
|
+
@monitor_thread = Thread.new do
|
128
|
+
monitoring_loop(interval_seconds)
|
129
|
+
end
|
130
|
+
|
131
|
+
@prompt.say(@formatter.format_monitoring_started(interval_seconds))
|
132
|
+
rescue => e
|
133
|
+
raise MonitorError, "Failed to start monitoring: #{e.message}"
|
134
|
+
end
|
135
|
+
|
136
|
+
def stop_monitoring
|
137
|
+
return unless @monitoring_active
|
138
|
+
|
139
|
+
@monitoring_active = false
|
140
|
+
@monitor_thread&.join
|
141
|
+
@monitor_thread = nil
|
142
|
+
|
143
|
+
@prompt.say(@formatter.format_monitoring_stopped)
|
144
|
+
rescue => e
|
145
|
+
raise MonitorError, "Failed to stop monitoring: #{e.message}"
|
146
|
+
end
|
147
|
+
|
148
|
+
def monitoring_active?
|
149
|
+
@monitoring_active
|
150
|
+
end
|
151
|
+
|
152
|
+
def add_update_callback(callback)
|
153
|
+
validate_callback(callback)
|
154
|
+
@update_callbacks << callback
|
155
|
+
rescue JobMonitorError => e
|
156
|
+
raise MonitorError, "Failed to add update callback: #{e.message}"
|
157
|
+
end
|
158
|
+
|
159
|
+
def remove_update_callback(callback)
|
160
|
+
@update_callbacks.delete(callback)
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_monitoring_summary
|
164
|
+
@monitor_mutex.synchronize do
|
165
|
+
{
|
166
|
+
total_jobs: @jobs.size,
|
167
|
+
jobs_by_status: @jobs.values.map { |job| job[:status] }.tally,
|
168
|
+
jobs_by_priority: @jobs.values.map { |job| job[:priority] }.tally,
|
169
|
+
monitoring_active: @monitoring_active,
|
170
|
+
total_events: @job_history.size,
|
171
|
+
last_update: @job_history.last&.dig(:timestamp)
|
172
|
+
}
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def display_job_status(job_id)
|
177
|
+
job = get_job_status(job_id)
|
178
|
+
@frame_manager.section("Job Status: #{job_id}") do
|
179
|
+
display_job_details(job)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def display_all_jobs
|
184
|
+
@frame_manager.section("All Jobs") do
|
185
|
+
display_jobs_table(@jobs)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def display_jobs_by_status(status)
|
190
|
+
jobs = get_jobs_by_status(status)
|
191
|
+
@frame_manager.section("Jobs with Status: #{status}") do
|
192
|
+
display_jobs_table(jobs)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
def validate_job_id(job_id)
|
199
|
+
raise JobMonitorError, "Job ID cannot be empty" if job_id.to_s.strip.empty?
|
200
|
+
end
|
201
|
+
|
202
|
+
def validate_job_data(job_data)
|
203
|
+
raise JobMonitorError, "Job data must be a hash" unless job_data.is_a?(Hash)
|
204
|
+
end
|
205
|
+
|
206
|
+
def validate_job_status(status)
|
207
|
+
unless JOB_STATUSES.key?(status)
|
208
|
+
raise JobMonitorError, "Invalid job status: #{status}. Must be one of: #{JOB_STATUSES.keys.join(", ")}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def validate_job_priority(priority)
|
213
|
+
unless JOB_PRIORITIES.key?(priority)
|
214
|
+
raise JobMonitorError, "Invalid job priority: #{priority}. Must be one of: #{JOB_PRIORITIES.keys.join(", ")}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def validate_callback(callback)
|
219
|
+
unless callback.respond_to?(:call)
|
220
|
+
raise JobMonitorError, "Callback must respond to :call"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def create_job_entry(job_id, job_data)
|
225
|
+
{
|
226
|
+
id: job_id,
|
227
|
+
status: job_data[:status] || :pending,
|
228
|
+
priority: job_data[:priority] || :normal,
|
229
|
+
created_at: Time.now,
|
230
|
+
last_updated: Time.now,
|
231
|
+
progress: job_data[:progress] || 0,
|
232
|
+
total_steps: job_data[:total_steps] || 1,
|
233
|
+
current_step: job_data[:current_step] || 0,
|
234
|
+
error_message: job_data[:error_message],
|
235
|
+
retry_count: job_data[:retry_count] || 0,
|
236
|
+
max_retries: job_data[:max_retries] || 3,
|
237
|
+
estimated_completion: job_data[:estimated_completion],
|
238
|
+
metadata: job_data[:metadata] || {}
|
239
|
+
}
|
240
|
+
end
|
241
|
+
|
242
|
+
def record_job_event(job_id, event_type, event_data)
|
243
|
+
event = {
|
244
|
+
job_id: job_id,
|
245
|
+
event_type: event_type,
|
246
|
+
timestamp: Time.now,
|
247
|
+
data: event_data
|
248
|
+
}
|
249
|
+
|
250
|
+
@job_history << event
|
251
|
+
end
|
252
|
+
|
253
|
+
def notify_callbacks(event_type, job, additional_data = nil)
|
254
|
+
@update_callbacks.each do |callback|
|
255
|
+
callback.call(event_type, job, additional_data)
|
256
|
+
rescue => e
|
257
|
+
@prompt.say(@formatter.format_callback_error(callback, e.message))
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def monitoring_loop(interval_seconds)
|
262
|
+
loop do
|
263
|
+
break unless @monitoring_active
|
264
|
+
|
265
|
+
begin
|
266
|
+
perform_monitoring_cycle
|
267
|
+
sleep(interval_seconds)
|
268
|
+
rescue => e
|
269
|
+
@prompt.say(@formatter.format_monitoring_error(e.message))
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def perform_monitoring_cycle
|
275
|
+
# Perform monitoring tasks like checking for stuck jobs, updating progress, etc.
|
276
|
+
check_for_stuck_jobs
|
277
|
+
update_job_progress
|
278
|
+
cleanup_completed_jobs
|
279
|
+
end
|
280
|
+
|
281
|
+
def check_for_stuck_jobs
|
282
|
+
stuck_threshold = 300 # 5 minutes
|
283
|
+
current_time = Time.now
|
284
|
+
|
285
|
+
@jobs.each do |job_id, job|
|
286
|
+
if job[:status] == :running && (current_time - job[:last_updated]) > stuck_threshold
|
287
|
+
update_job_status(job_id, :failed, {error_message: "Job appears to be stuck"})
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def update_job_progress
|
293
|
+
# Update progress for running jobs
|
294
|
+
@jobs.each do |job_id, job|
|
295
|
+
if job[:status] == :running && job[:current_step] < job[:total_steps]
|
296
|
+
# Simulate progress update - in real implementation, this would come from the job
|
297
|
+
new_progress = [(job[:current_step] + 1).to_f / job[:total_steps] * 100, 100].min
|
298
|
+
update_job_status(job_id, :running, {progress: new_progress})
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def cleanup_completed_jobs
|
304
|
+
# Clean up old completed jobs
|
305
|
+
cleanup_threshold = 3600 # 1 hour
|
306
|
+
current_time = Time.now
|
307
|
+
|
308
|
+
jobs_to_remove = @jobs.select do |job_id, job|
|
309
|
+
(job[:status] == :completed || job[:status] == :failed) &&
|
310
|
+
(current_time - job[:last_updated]) > cleanup_threshold
|
311
|
+
end
|
312
|
+
|
313
|
+
jobs_to_remove.each do |job_id, _|
|
314
|
+
@jobs.delete(job_id)
|
315
|
+
record_job_event(job_id, :cleaned_up, {reason: "Old completed job"})
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def display_job_details(job)
|
320
|
+
details = []
|
321
|
+
details << "Job ID: #{job[:id]}"
|
322
|
+
details << "Status: #{@formatter.format_job_status(job[:status])}"
|
323
|
+
details << "Priority: #{@formatter.format_job_priority(job[:priority])}"
|
324
|
+
details << "Progress: #{@formatter.format_job_progress(job[:progress])}"
|
325
|
+
details << "Created: #{job[:created_at]}"
|
326
|
+
details << "Last Updated: #{job[:last_updated]}"
|
327
|
+
|
328
|
+
if job[:error_message]
|
329
|
+
details << "Error: #{@formatter.format_job_error(job[:error_message])}"
|
330
|
+
end
|
331
|
+
|
332
|
+
if job[:retry_count] > 0
|
333
|
+
details << "Retries: #{job[:retry_count]}/#{job[:max_retries]}"
|
334
|
+
end
|
335
|
+
|
336
|
+
details.join("\n")
|
337
|
+
end
|
338
|
+
|
339
|
+
def display_jobs_table(jobs)
|
340
|
+
if jobs.empty?
|
341
|
+
return "No jobs found."
|
342
|
+
end
|
343
|
+
|
344
|
+
lines = []
|
345
|
+
lines << "ID".ljust(20) + "Status".ljust(12) + "Priority".ljust(10) + "Progress".ljust(10) + "Created"
|
346
|
+
lines << "-" * 70
|
347
|
+
|
348
|
+
jobs.each do |job_id, job|
|
349
|
+
status = @formatter.format_job_status_short(job[:status])
|
350
|
+
priority = @formatter.format_job_priority_short(job[:priority])
|
351
|
+
progress = @formatter.format_job_progress_short(job[:progress])
|
352
|
+
created = job[:created_at].strftime("%H:%M:%S")
|
353
|
+
|
354
|
+
lines << job_id.to_s.ljust(20) + status.to_s.ljust(12) + priority.to_s.ljust(10) + progress.to_s.ljust(10) + created.to_s
|
355
|
+
end
|
356
|
+
|
357
|
+
lines.join("\n")
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Formats job monitor display
|
362
|
+
class JobMonitorFormatter
|
363
|
+
def initialize
|
364
|
+
@pastel = Pastel.new
|
365
|
+
end
|
366
|
+
|
367
|
+
def format_job_status(status)
|
368
|
+
case status
|
369
|
+
when :pending
|
370
|
+
@pastel.yellow("⏳ Pending")
|
371
|
+
when :running
|
372
|
+
@pastel.blue("🔄 Running")
|
373
|
+
when :completed
|
374
|
+
@pastel.green("✅ Completed")
|
375
|
+
when :failed
|
376
|
+
@pastel.red("❌ Failed")
|
377
|
+
when :cancelled
|
378
|
+
@pastel.red("🚫 Cancelled")
|
379
|
+
when :retrying
|
380
|
+
@pastel.yellow("🔄 Retrying")
|
381
|
+
else
|
382
|
+
@pastel.blue("❓ #{status.to_s.capitalize}")
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def format_job_status_short(status)
|
387
|
+
case status
|
388
|
+
when :pending
|
389
|
+
@pastel.yellow("⏳")
|
390
|
+
when :running
|
391
|
+
@pastel.blue("🔄")
|
392
|
+
when :completed
|
393
|
+
@pastel.green("✅")
|
394
|
+
when :failed
|
395
|
+
@pastel.red("❌")
|
396
|
+
when :cancelled
|
397
|
+
@pastel.red("🚫")
|
398
|
+
when :retrying
|
399
|
+
@pastel.yellow("🔄")
|
400
|
+
else
|
401
|
+
@pastel.blue("❓")
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def format_job_priority(priority)
|
406
|
+
case priority
|
407
|
+
when :low
|
408
|
+
@pastel.blue("🔽 Low")
|
409
|
+
when :normal
|
410
|
+
@pastel.blue("➡️ Normal")
|
411
|
+
when :high
|
412
|
+
@pastel.yellow("🔼 High")
|
413
|
+
when :urgent
|
414
|
+
@pastel.red("🚨 Urgent")
|
415
|
+
else
|
416
|
+
@pastel.blue("❓ #{priority.to_s.capitalize}")
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def format_job_priority_short(priority)
|
421
|
+
case priority
|
422
|
+
when :low
|
423
|
+
@pastel.blue("🔽")
|
424
|
+
when :normal
|
425
|
+
@pastel.blue("➡️")
|
426
|
+
when :high
|
427
|
+
@pastel.yellow("🔼")
|
428
|
+
when :urgent
|
429
|
+
@pastel.red("🚨")
|
430
|
+
else
|
431
|
+
@pastel.blue("❓")
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
def format_job_progress(progress)
|
436
|
+
progress_int = progress.to_i
|
437
|
+
if progress_int >= 100
|
438
|
+
@pastel.green("100%")
|
439
|
+
elsif progress_int >= 75
|
440
|
+
@pastel.blue("#{progress_int}%")
|
441
|
+
elsif progress_int >= 50
|
442
|
+
@pastel.yellow("#{progress_int}%")
|
443
|
+
else
|
444
|
+
@pastel.blue("#{progress_int}%")
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
def format_job_progress_short(progress)
|
449
|
+
progress_int = progress.to_i
|
450
|
+
if progress_int >= 100
|
451
|
+
@pastel.green("100%")
|
452
|
+
elsif progress_int >= 75
|
453
|
+
@pastel.blue("#{progress_int}%")
|
454
|
+
elsif progress_int >= 50
|
455
|
+
@pastel.yellow("#{progress_int}%")
|
456
|
+
else
|
457
|
+
@pastel.blue("#{progress_int}%")
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
def format_job_error(error_message)
|
462
|
+
@pastel.red("❌ #{error_message}")
|
463
|
+
end
|
464
|
+
|
465
|
+
def format_monitoring_started(interval_seconds)
|
466
|
+
@pastel.green("✅ Job monitoring started (interval: #{interval_seconds}s)")
|
467
|
+
end
|
468
|
+
|
469
|
+
def format_monitoring_stopped
|
470
|
+
@pastel.red("❌ Job monitoring stopped")
|
471
|
+
end
|
472
|
+
|
473
|
+
def format_monitoring_error(error_message)
|
474
|
+
@pastel.red("❌ Monitoring error: #{error_message}")
|
475
|
+
end
|
476
|
+
|
477
|
+
def format_callback_error(callback, error_message)
|
478
|
+
@pastel.red("❌ Callback error: #{error_message}")
|
479
|
+
end
|
480
|
+
|
481
|
+
def format_monitoring_summary(summary)
|
482
|
+
result = []
|
483
|
+
result << @pastel.bold(@pastel.blue("📊 Job Monitoring Summary"))
|
484
|
+
result << "Total jobs: #{@pastel.bold(summary[:total_jobs])}"
|
485
|
+
result << "Monitoring: #{summary[:monitoring_active] ? "Active" : "Inactive"}"
|
486
|
+
result << "Total events: #{@pastel.blue(summary[:total_events])}"
|
487
|
+
|
488
|
+
if summary[:jobs_by_status].any?
|
489
|
+
result << "Jobs by status:"
|
490
|
+
summary[:jobs_by_status].each do |status, count|
|
491
|
+
result << " #{@pastel.blue("#{status}: #{count}")}"
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
result.join("\n")
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
end
|