aidp 0.9.6 → 0.11.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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +194 -25
  3. data/lib/aidp/analyze/error_handler.rb +4 -2
  4. data/lib/aidp/{analysis → analyze}/kb_inspector.rb +93 -89
  5. data/lib/aidp/analyze/prioritizer.rb +3 -2
  6. data/lib/aidp/analyze/progress.rb +2 -1
  7. data/lib/aidp/analyze/ruby_maat_integration.rb +7 -3
  8. data/lib/aidp/analyze/runner.rb +73 -11
  9. data/lib/aidp/{analysis → analyze}/seams.rb +1 -1
  10. data/lib/aidp/analyze/steps.rb +10 -8
  11. data/lib/aidp/{analysis → analyze}/tree_sitter_grammar_loader.rb +11 -5
  12. data/lib/aidp/{analysis → analyze}/tree_sitter_scan.rb +21 -15
  13. data/lib/aidp/cli/checkpoint_command.rb +98 -0
  14. data/lib/aidp/cli/first_run_wizard.rb +83 -103
  15. data/lib/aidp/cli/jobs_command.rb +270 -36
  16. data/lib/aidp/cli/terminal_io.rb +3 -3
  17. data/lib/aidp/cli.rb +411 -69
  18. data/lib/aidp/config.rb +5 -8
  19. data/lib/aidp/debug_logger.rb +4 -4
  20. data/lib/aidp/debug_mixin.rb +11 -4
  21. data/lib/aidp/execute/checkpoint.rb +282 -0
  22. data/lib/aidp/execute/checkpoint_display.rb +221 -0
  23. data/lib/aidp/execute/progress.rb +2 -1
  24. data/lib/aidp/execute/prompt_manager.rb +62 -0
  25. data/lib/aidp/execute/runner.rb +67 -20
  26. data/lib/aidp/execute/steps.rb +36 -27
  27. data/lib/aidp/execute/work_loop_runner.rb +308 -0
  28. data/lib/aidp/execute/workflow_selector.rb +50 -26
  29. data/lib/aidp/harness/condition_detector.rb +4 -4
  30. data/lib/aidp/harness/config_schema.rb +40 -0
  31. data/lib/aidp/harness/config_validator.rb +3 -6
  32. data/lib/aidp/harness/configuration.rb +35 -1
  33. data/lib/aidp/harness/enhanced_runner.rb +25 -4
  34. data/lib/aidp/harness/error_handler.rb +103 -28
  35. data/lib/aidp/harness/provider_factory.rb +6 -1
  36. data/lib/aidp/harness/provider_manager.rb +273 -19
  37. data/lib/aidp/harness/runner.rb +14 -6
  38. data/lib/aidp/harness/simple_user_interface.rb +6 -4
  39. data/lib/aidp/harness/status_display.rb +118 -106
  40. data/lib/aidp/harness/test_runner.rb +83 -0
  41. data/lib/aidp/harness/ui/enhanced_tui.rb +7 -5
  42. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -4
  43. data/lib/aidp/harness/ui/error_handler.rb +7 -2
  44. data/lib/aidp/harness/ui/frame_manager.rb +61 -39
  45. data/lib/aidp/harness/ui/job_monitor.rb +2 -0
  46. data/lib/aidp/harness/ui/navigation/main_menu.rb +27 -16
  47. data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
  48. data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
  49. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  50. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
  51. data/lib/aidp/harness/ui/progress_display.rb +26 -7
  52. data/lib/aidp/harness/ui/question_collector.rb +2 -0
  53. data/lib/aidp/harness/ui/spinner_group.rb +2 -0
  54. data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
  55. data/lib/aidp/harness/ui/status_manager.rb +4 -2
  56. data/lib/aidp/harness/ui/status_widget.rb +20 -9
  57. data/lib/aidp/harness/ui/workflow_controller.rb +27 -9
  58. data/lib/aidp/harness/user_interface.rb +338 -330
  59. data/lib/aidp/jobs/background_runner.rb +278 -0
  60. data/lib/aidp/message_display.rb +48 -0
  61. data/lib/aidp/provider_manager.rb +13 -7
  62. data/lib/aidp/providers/anthropic.rb +101 -18
  63. data/lib/aidp/providers/base.rb +51 -1
  64. data/lib/aidp/providers/codex.rb +248 -0
  65. data/lib/aidp/providers/cursor.rb +39 -48
  66. data/lib/aidp/providers/gemini.rb +26 -16
  67. data/lib/aidp/providers/github_copilot.rb +263 -0
  68. data/lib/aidp/providers/opencode.rb +38 -47
  69. data/lib/aidp/version.rb +1 -1
  70. data/lib/aidp/workflows/definitions.rb +357 -0
  71. data/lib/aidp/workflows/selector.rb +171 -0
  72. data/lib/aidp.rb +16 -4
  73. data/templates/planning/generate_llm_style_guide.md +119 -0
  74. metadata +43 -31
  75. data/lib/aidp/analyze/progress_visualizer.rb +0 -314
  76. /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
  77. /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
  78. /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
  79. /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
  80. /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
  81. /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
  82. /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
  83. /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
  84. /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
  85. /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
  86. /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
  87. /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
  88. /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
  89. /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
  90. /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
  91. /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
  92. /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
  93. /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
  94. /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
  95. /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
  96. /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
  97. /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
  98. /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
  99. /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
  100. /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
@@ -1,14 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tty-prompt"
4
+ require "tty-cursor"
5
+
3
6
  module Aidp
4
7
  module Harness
5
8
  # Real-time status updates and monitoring interface
6
9
  class StatusDisplay
7
- def initialize(provider_manager = nil, metrics_manager = nil, circuit_breaker_manager = nil, error_logger = nil)
10
+ include Aidp::MessageDisplay
11
+
12
+ def initialize(provider_manager = nil, metrics_manager = nil, circuit_breaker_manager = nil, error_logger = nil, prompt: TTY::Prompt.new)
8
13
  @provider_manager = provider_manager
9
14
  @metrics_manager = metrics_manager
10
15
  @circuit_breaker_manager = circuit_breaker_manager
11
16
  @error_logger = error_logger
17
+ @prompt = prompt
18
+ @cursor = TTY::Cursor
12
19
 
13
20
  @start_time = nil
14
21
  @current_step = nil
@@ -177,61 +184,61 @@ module Aidp
177
184
  # Show paused status
178
185
  def show_paused_status
179
186
  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
187
+ display_message("\n⏸️ Harness PAUSED", type: :warning)
188
+ display_message(" Press 'r' to resume, 's' to stop", type: :info)
189
+ display_message(" Current step: #{@current_step}", type: :info) if @current_step
190
+ display_message(" Current provider: #{@current_provider}", type: :info) if @current_provider
191
+ display_message(" Current model: #{@current_model}", type: :info) if @current_model
192
+ display_message(" Duration: #{format_duration(Time.now - @start_time)}", type: :info) if @start_time
186
193
  end
187
194
 
188
195
  # Show resumed status
189
196
  def show_resumed_status
190
197
  clear_display
191
- puts "\n▶️ Harness RESUMED"
192
- puts " Continuing execution..."
198
+ display_message("\n▶️ Harness RESUMED", type: :success)
199
+ display_message(" Continuing execution...", type: :info)
193
200
  end
194
201
 
195
202
  # Show stopped status
196
203
  def show_stopped_status
197
204
  clear_display
198
- puts "\n⏹️ Harness STOPPED"
199
- puts " Execution terminated by user"
205
+ display_message("\n⏹️ Harness STOPPED", type: :error)
206
+ display_message(" Execution terminated by user", type: :info)
200
207
  end
201
208
 
202
209
  # Show rate limit wait
203
210
  def show_rate_limit_wait(reset_time)
204
211
  clear_display
205
212
  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"
213
+ display_message("\n🚫 Rate limit reached", type: :error)
214
+ display_message(" Waiting for reset at #{reset_time.strftime("%H:%M:%S")}", type: :info)
215
+ display_message(" Remaining: #{format_duration(remaining)}", type: :info)
216
+ display_message(" Press Ctrl+C to cancel", type: :info)
210
217
  end
211
218
 
212
219
  # Update rate limit countdown
213
220
  def update_rate_limit_countdown(remaining_seconds)
214
221
  clear_display
215
- puts "\n🚫 Rate limit - waiting..."
216
- puts " Resets in: #{format_duration(remaining_seconds)}"
217
- puts " Press Ctrl+C to cancel"
222
+ display_message("\n🚫 Rate limit - waiting...", type: :warning)
223
+ display_message(" Resets in: #{format_duration(remaining_seconds)}", type: :info)
224
+ display_message(" Press Ctrl+C to cancel", type: :info)
218
225
  end
219
226
 
220
227
  # Show completion status
221
228
  def show_completion_status(duration, steps_completed, total_steps)
222
229
  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!"
230
+ display_message("\n✅ Harness COMPLETED", type: :success)
231
+ display_message(" Duration: #{format_duration(duration)}", type: :info)
232
+ display_message(" Steps completed: #{steps_completed}/#{total_steps}", type: :info)
233
+ display_message(" All workflows finished successfully!", type: :success)
227
234
  end
228
235
 
229
236
  # Show error status
230
237
  def show_error_status(error_message)
231
238
  clear_display
232
- puts "\n❌ Harness ERROR"
233
- puts " Error: #{error_message}"
234
- puts " Check logs for details"
239
+ display_message("\n❌ Harness ERROR", type: :error)
240
+ display_message(" Error: #{error_message}", type: :error)
241
+ display_message(" Check logs for details", type: :info)
235
242
  end
236
243
 
237
244
  # Cleanup display
@@ -368,47 +375,47 @@ module Aidp
368
375
  def display_compact_status
369
376
  duration = @start_time ? Time.now - @start_time : 0
370
377
 
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"
378
+ display_message("\n🔄 Harness Status", type: :info)
379
+ display_message(" Duration: #{format_duration(duration)}", type: :info)
380
+ display_message(" Step: #{@current_step || "Starting..."}", type: :info)
381
+ display_message(" Provider: #{@current_provider || "Initializing..."}", type: :info)
382
+ display_message(" Model: #{@current_model || "N/A"}", type: :info)
383
+ display_message(" Status: Running", type: :info)
377
384
 
378
385
  # Show key metrics
379
386
  if @performance_metrics[:error_rate] && @performance_metrics[:error_rate] > 0
380
- puts " Error Rate: #{format_percentage(@performance_metrics[:error_rate])}"
387
+ display_message(" Error Rate: #{format_percentage(@performance_metrics[:error_rate])}", type: :warning)
381
388
  end
382
389
 
383
390
  if @token_usage[@current_provider] && @token_usage[@current_provider][@current_model]
384
391
  tokens = @token_usage[@current_provider][@current_model]
385
- puts " Tokens: #{tokens[:used]} used"
386
- puts " Remaining: #{tokens[:remaining]}" if tokens[:remaining]
392
+ display_message(" Tokens: #{tokens[:used]} used", type: :info)
393
+ display_message(" Remaining: #{tokens[:remaining]}", type: :info) if tokens[:remaining]
387
394
  end
388
395
 
389
- puts " Press Ctrl+C to stop"
396
+ display_message(" Press Ctrl+C to stop", type: :info)
390
397
  end
391
398
 
392
399
  def display_detailed_status
393
400
  duration = @start_time ? Time.now - @start_time : 0
394
401
 
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"
402
+ display_message("\n🔄 Harness Status - Detailed", type: :info)
403
+ display_message(" Duration: #{format_duration(duration)}", type: :info)
404
+ display_message(" Current Step: #{@current_step || "Starting..."}", type: :info)
405
+ display_message(" Provider: #{@current_provider || "Initializing..."}", type: :info)
406
+ display_message(" Model: #{@current_model || "N/A"}", type: :info)
407
+ display_message(" Status: Running", type: :info)
401
408
 
402
409
  # Provider information
403
410
  if @provider_status[:available_providers]
404
- puts " Available Providers: #{@provider_status[:available_providers].join(", ")}"
411
+ display_message(" Available Providers: #{@provider_status[:available_providers].join(", ")}", type: :info)
405
412
  end
406
413
 
407
414
  # Circuit breaker status
408
415
  if @circuit_breaker_status.any?
409
416
  open_circuits = @circuit_breaker_status.select { |_, status| status[:state] == :open }
410
417
  if open_circuits.any?
411
- puts " Open Circuit Breakers: #{open_circuits.keys.join(", ")}"
418
+ display_message(" Open Circuit Breakers: #{open_circuits.keys.join(", ")}", type: :warning)
412
419
  end
413
420
  end
414
421
 
@@ -417,22 +424,22 @@ module Aidp
417
424
 
418
425
  # Error summary
419
426
  if @error_summary[:error_summary] && @error_summary[:error_summary][:total_errors] > 0
420
- puts " Errors: #{@error_summary[:error_summary][:total_errors]} total"
427
+ display_message(" Errors: #{@error_summary[:error_summary][:total_errors]} total", type: :warning)
421
428
  end
422
429
 
423
- puts " Press Ctrl+C to stop"
430
+ display_message(" Press Ctrl+C to stop", type: :info)
424
431
  end
425
432
 
426
433
  def display_minimal_status
427
434
  duration = @start_time ? Time.now - @start_time : 0
428
- puts "\r🔄 #{@current_step || "Starting"} | #{@current_provider || "Init"} | #{format_duration(duration)}"
435
+ display_message("\r🔄 #{@current_step || "Starting"} | #{@current_provider || "Init"} | #{format_duration(duration)}", type: :info)
429
436
  end
430
437
 
431
438
  def display_full_status
432
439
  clear_display
433
- puts "\n" + "=" * 80
434
- puts "🔄 AIDP HARNESS - FULL STATUS REPORT"
435
- puts "=" * 80
440
+ display_message("\n" + "=" * 80, type: :info)
441
+ display_message("🔄 AIDP HARNESS - FULL STATUS REPORT", type: :highlight)
442
+ display_message("=" * 80, type: :info)
436
443
 
437
444
  display_basic_info
438
445
  display_provider_info
@@ -446,33 +453,33 @@ module Aidp
446
453
  display_work_completion_info
447
454
  display_alerts
448
455
 
449
- puts "=" * 80
450
- puts "Press Ctrl+C to stop | Last updated: #{Time.now.strftime("%H:%M:%S")}"
456
+ display_message("=" * 80, type: :info)
457
+ display_message("Press Ctrl+C to stop | Last updated: #{Time.now.strftime("%H:%M:%S")}", type: :muted)
451
458
  end
452
459
 
453
460
  def display_basic_info
454
461
  duration = @start_time ? Time.now - @start_time : 0
455
462
 
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
+ display_message("\n📊 BASIC INFORMATION", type: :info)
464
+ display_message(" Duration: #{format_duration(duration)}", type: :info)
465
+ display_message(" Current Step: #{@current_step || "Starting..."}", type: :info)
466
+ display_message(" Provider: #{@current_provider || "Initializing..."}", type: :info)
467
+ display_message(" Model: #{@current_model || "N/A"}", type: :info)
468
+ display_message(" Status: Running", type: :info)
469
+ display_message(" Update Interval: #{@update_interval}s", type: :info)
463
470
  end
464
471
 
465
472
  def display_provider_info
466
473
  return unless @provider_status.any?
467
474
 
468
- puts "\n🔌 PROVIDER INFORMATION"
475
+ display_message("\n🔌 PROVIDER INFORMATION", type: :info)
469
476
  if @provider_status[:available_providers]
470
- puts " Available Providers: #{@provider_status[:available_providers].join(", ")}"
477
+ display_message(" Available Providers: #{@provider_status[:available_providers].join(", ")}", type: :info)
471
478
  end
472
479
  if @provider_status[:provider_health]
473
- puts " Provider Health:"
480
+ display_message(" Provider Health:", type: :info)
474
481
  @provider_status[:provider_health].each do |provider, health|
475
- puts " #{provider}: #{health[:status]} (#{format_percentage(health[:health_score])})"
482
+ display_message(" #{provider}: #{health[:status]} (#{format_percentage(health[:health_score])})", type: :info)
476
483
  end
477
484
  end
478
485
  end
@@ -480,11 +487,11 @@ module Aidp
480
487
  def display_token_info
481
488
  return unless @token_usage.any?
482
489
 
483
- puts "\n🎫 TOKEN USAGE"
490
+ display_message("\n🎫 TOKEN USAGE", type: :info)
484
491
  @token_usage.each do |provider, models|
485
- puts " #{provider}:"
492
+ display_message(" #{provider}:", type: :info)
486
493
  models.each do |model, usage|
487
- puts " #{model}: #{usage[:used]} used, #{usage[:remaining]} remaining"
494
+ display_message(" #{model}: #{usage[:used]} used, #{usage[:remaining]} remaining", type: :info)
488
495
  end
489
496
  end
490
497
  end
@@ -492,14 +499,14 @@ module Aidp
492
499
  def display_performance_info
493
500
  return unless @performance_metrics.any?
494
501
 
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)}"
502
+ display_message("\n⚡ PERFORMANCE METRICS", type: :info)
503
+ display_message(" Uptime: #{format_duration(@performance_metrics[:uptime] || 0)}", type: :info)
504
+ display_message(" Step Duration: #{format_duration(@performance_metrics[:step_duration] || 0)}", type: :info)
505
+ display_message(" Provider Switches: #{@performance_metrics[:provider_switch_count] || 0}", type: :info)
506
+ display_message(" Error Rate: #{format_percentage(@performance_metrics[:error_rate] || 0)}", type: :info)
500
507
 
501
508
  if @performance_metrics[:throughput]
502
- puts " Throughput: #{@performance_metrics[:throughput]} requests/min"
509
+ display_message(" Throughput: #{@performance_metrics[:throughput]} requests/min", type: :info)
503
510
  end
504
511
  end
505
512
 
@@ -509,21 +516,21 @@ module Aidp
509
516
  error_summary = @error_summary[:error_summary]
510
517
  return if error_summary[:total_errors] == 0
511
518
 
512
- puts "\n❌ ERROR INFORMATION"
513
- puts " Total Errors: #{error_summary[:total_errors]}"
514
- puts " Error Rate: #{format_percentage(error_summary[:error_rate] || 0)}"
519
+ display_message("\n❌ ERROR INFORMATION", type: :error)
520
+ display_message(" Total Errors: #{error_summary[:total_errors]}", type: :error)
521
+ display_message(" Error Rate: #{format_percentage(error_summary[:error_rate] || 0)}", type: :error)
515
522
 
516
523
  if error_summary[:errors_by_severity].respond_to?(:any?) && error_summary[:errors_by_severity].any?
517
- puts " By Severity:"
524
+ display_message(" By Severity:", type: :info)
518
525
  error_summary[:errors_by_severity].each do |severity, count|
519
- puts " #{severity}: #{count}"
526
+ display_message(" #{severity}: #{count}", type: :info)
520
527
  end
521
528
  end
522
529
 
523
530
  if error_summary[:errors_by_provider].respond_to?(:any?) && error_summary[:errors_by_provider].any?
524
- puts " By Provider:"
531
+ display_message(" By Provider:", type: :info)
525
532
  error_summary[:errors_by_provider].each do |provider, count|
526
- puts " #{provider}: #{count}"
533
+ display_message(" #{provider}: #{count}", type: :info)
527
534
  end
528
535
  end
529
536
  end
@@ -531,23 +538,23 @@ module Aidp
531
538
  def display_circuit_breaker_info
532
539
  return unless @circuit_breaker_status.any?
533
540
 
534
- puts "\n🔒 CIRCUIT BREAKER STATUS"
541
+ display_message("\n🔒 CIRCUIT BREAKER STATUS", type: :info)
535
542
  @circuit_breaker_status.each do |key, status|
536
543
  state_icons = {closed: "🟢", open: "🔴", half_open: "🟡"}
537
544
  state_icon = state_icons[status[:state]] || "⚪"
538
- puts " #{state_icon} #{key}: #{status[:state]} (failures: #{status[:failure_count]})"
545
+ display_message(" #{state_icon} #{key}: #{status[:state]} (failures: #{status[:failure_count]})", type: :info)
539
546
  end
540
547
  end
541
548
 
542
549
  def display_token_usage
543
550
  return unless @token_usage.any?
544
551
 
545
- puts "\n🎫 TOKEN USAGE"
552
+ display_message("\n🎫 TOKEN USAGE", type: :info)
546
553
  @token_usage.each do |provider, models|
547
- puts " #{provider}:"
554
+ display_message(" #{provider}:", type: :info)
548
555
  models.each do |model, usage|
549
- puts " #{model}: #{usage[:used]} used"
550
- puts " Remaining: #{usage[:remaining]}" if usage[:remaining]
556
+ display_message(" #{model}: #{usage[:used]} used", type: :info)
557
+ display_message(" Remaining: #{usage[:remaining]}", type: :info) if usage[:remaining]
551
558
  end
552
559
  end
553
560
  end
@@ -555,14 +562,14 @@ module Aidp
555
562
  def display_rate_limit_info
556
563
  return unless @rate_limit_status.any?
557
564
 
558
- puts "\n🚫 RATE LIMIT STATUS"
565
+ display_message("\n🚫 RATE LIMIT STATUS", type: :warning)
559
566
  @rate_limit_status.each do |provider, models|
560
567
  models.each do |model, status|
561
568
  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]
569
+ display_message(" #{provider}:#{model}: Rate Limited", type: :warning)
570
+ display_message(" Reset Time: #{status[:reset_time]&.strftime("%H:%M:%S")}", type: :info)
571
+ display_message(" Retry After: #{status[:retry_after]}s", type: :info)
572
+ display_message(" Quota: #{status[:quota_remaining]}/#{status[:quota_limit]}", type: :info) if status[:quota_remaining]
566
573
  end
567
574
  end
568
575
  end
@@ -571,12 +578,12 @@ module Aidp
571
578
  def display_recovery_info
572
579
  return unless @recovery_status.any?
573
580
 
574
- puts "\n🔄 RECOVERY STATUS"
581
+ display_message("\n🔄 RECOVERY STATUS", type: :info)
575
582
  @recovery_status.each do |type, status|
576
- puts " #{type}: #{status[:status]}"
583
+ display_message(" #{type}: #{status[:status]}", type: :info)
577
584
  if status[:details].any?
578
585
  status[:details].each do |key, value|
579
- puts " #{key}: #{value}"
586
+ display_message(" #{key}: #{value}", type: :info)
580
587
  end
581
588
  end
582
589
  end
@@ -585,12 +592,12 @@ module Aidp
585
592
  def display_user_feedback_info
586
593
  return unless @user_feedback_status.any?
587
594
 
588
- puts "\n💬 USER FEEDBACK STATUS"
595
+ display_message("\n💬 USER FEEDBACK STATUS", type: :info)
589
596
  @user_feedback_status.each do |type, status|
590
- puts " #{type}: #{status[:status]}"
597
+ display_message(" #{type}: #{status[:status]}", type: :info)
591
598
  if status[:details].any?
592
599
  status[:details].each do |key, value|
593
- puts " #{key}: #{value}"
600
+ display_message(" #{key}: #{value}", type: :info)
594
601
  end
595
602
  end
596
603
  end
@@ -599,24 +606,24 @@ module Aidp
599
606
  def display_work_completion_info
600
607
  return unless @work_completion_status.any?
601
608
 
602
- puts "\n✅ WORK COMPLETION STATUS"
609
+ display_message("\n✅ WORK COMPLETION STATUS", type: :info)
603
610
  if @work_completion_status[:is_complete]
604
- puts " Status: Complete"
611
+ display_message(" Status: Complete", type: :success)
605
612
  else
606
- puts " Status: In Progress"
613
+ display_message(" Status: In Progress", type: :info)
607
614
  end
608
- puts " Steps Completed: #{@work_completion_status[:completed_steps]}/#{@work_completion_status[:total_steps]}"
615
+ display_message(" Steps Completed: #{@work_completion_status[:completed_steps]}/#{@work_completion_status[:total_steps]}", type: :info)
609
616
  end
610
617
 
611
618
  def display_alerts
612
619
  alerts = get_alerts
613
620
  return unless alerts.any?
614
621
 
615
- puts "\n🚨 ALERTS"
622
+ display_message("\n🚨 ALERTS", type: :warning)
616
623
  alerts.each do |alert|
617
624
  severity_icons = {critical: "🔴", warning: "🟡", info: "🔵"}
618
625
  severity_icon = severity_icons[alert[:severity]] || "⚪"
619
- puts " #{severity_icon} #{alert[:message]}"
626
+ display_message(" #{severity_icon} #{alert[:message]}", type: :warning)
620
627
  end
621
628
  end
622
629
 
@@ -661,8 +668,8 @@ module Aidp
661
668
  end
662
669
 
663
670
  def handle_display_error(error)
664
- puts "\n❌ Display Error: #{error.message}"
665
- puts " Continuing with status updates..."
671
+ display_message("\n❌ Display Error: #{error.message}", type: :error)
672
+ display_message(" Continuing with status updates...", type: :info)
666
673
  end
667
674
 
668
675
  def get_basic_status
@@ -737,9 +744,14 @@ module Aidp
737
744
  end
738
745
 
739
746
  def clear_display
740
- # Clear the current line and move cursor to beginning
741
- print "\r" + " " * 80 + "\r"
742
- $stdout.flush
747
+ # Clear the current line using TTY::Cursor
748
+ print_to_stderr(@cursor.clear_line, @cursor.column(1))
749
+ end
750
+
751
+ # Helper method to print to stderr with flush
752
+ def print_to_stderr(*parts)
753
+ parts.each { |part| $stderr.print part }
754
+ $stderr.flush
743
755
  end
744
756
 
745
757
  def format_duration(seconds)
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "open3"
4
+
5
+ module Aidp
6
+ module Harness
7
+ # Executes test and linter commands configured in aidp.yml
8
+ # Returns results with exit status and output
9
+ class TestRunner
10
+ def initialize(project_dir, config)
11
+ @project_dir = project_dir
12
+ @config = config
13
+ end
14
+
15
+ # Run all configured tests
16
+ # Returns: { success: boolean, output: string, failures: array }
17
+ def run_tests
18
+ test_commands = @config.test_commands || []
19
+ return {success: true, output: "", failures: []} if test_commands.empty?
20
+
21
+ results = test_commands.map { |cmd| execute_command(cmd, "test") }
22
+ aggregate_results(results, "Tests")
23
+ end
24
+
25
+ # Run all configured linters
26
+ # Returns: { success: boolean, output: string, failures: array }
27
+ def run_linters
28
+ lint_commands = @config.lint_commands || []
29
+ return {success: true, output: "", failures: []} if lint_commands.empty?
30
+
31
+ results = lint_commands.map { |cmd| execute_command(cmd, "linter") }
32
+ aggregate_results(results, "Linters")
33
+ end
34
+
35
+ private
36
+
37
+ def execute_command(command, type)
38
+ stdout, stderr, status = Open3.capture3(command, chdir: @project_dir)
39
+
40
+ {
41
+ command: command,
42
+ type: type,
43
+ success: status.success?,
44
+ exit_code: status.exitstatus,
45
+ stdout: stdout,
46
+ stderr: stderr
47
+ }
48
+ end
49
+
50
+ def aggregate_results(results, category)
51
+ failures = results.reject { |r| r[:success] }
52
+ success = failures.empty?
53
+
54
+ output = if success
55
+ "#{category}: All passed"
56
+ else
57
+ format_failures(failures, category)
58
+ end
59
+
60
+ {
61
+ success: success,
62
+ output: output,
63
+ failures: failures
64
+ }
65
+ end
66
+
67
+ def format_failures(failures, category)
68
+ output = ["#{category} Failures:", ""]
69
+
70
+ failures.each do |failure|
71
+ output << "Command: #{failure[:command]}"
72
+ output << "Exit Code: #{failure[:exit_code]}"
73
+ output << "--- Output ---"
74
+ output << failure[:stdout] unless failure[:stdout].strip.empty?
75
+ output << failure[:stderr] unless failure[:stderr].strip.empty?
76
+ output << ""
77
+ end
78
+
79
+ output.join("\n")
80
+ end
81
+ end
82
+ end
83
+ end
@@ -15,7 +15,9 @@ module Aidp
15
15
  # Enhanced TUI system using TTY libraries, inspired by Claude Code and modern LLM agents
16
16
  class EnhancedTUI
17
17
  class TUIError < StandardError; end
18
+
18
19
  class InputError < TUIError; end
20
+
19
21
  class DisplayError < TUIError; end
20
22
 
21
23
  def initialize(prompt: TTY::Prompt.new)
@@ -129,7 +131,7 @@ module Aidp
129
131
  border: :thick,
130
132
  padding: [1, 2]
131
133
  )
132
- puts box
134
+ @prompt.say(box)
133
135
  end
134
136
 
135
137
  # Enhanced step execution display
@@ -149,7 +151,7 @@ module Aidp
149
151
  padding: [1, 2],
150
152
  style: {border: {fg: :blue}}
151
153
  )
152
- puts box
154
+ @prompt.say(box)
153
155
 
154
156
  when :running
155
157
  content = []
@@ -165,7 +167,7 @@ module Aidp
165
167
  padding: [1, 2],
166
168
  style: {border: {fg: :yellow}}
167
169
  )
168
- puts box
170
+ @prompt.say(box)
169
171
 
170
172
  when :completed
171
173
  content = []
@@ -181,7 +183,7 @@ module Aidp
181
183
  padding: [1, 2],
182
184
  style: {border: {fg: :green}}
183
185
  )
184
- puts box
186
+ @prompt.say(box)
185
187
 
186
188
  when :failed
187
189
  content = []
@@ -222,7 +224,7 @@ module Aidp
222
224
  style: {border: {fg: :red}},
223
225
  width: 80
224
226
  )
225
- puts box
227
+ @prompt.say(box)
226
228
  end
227
229
  end
228
230
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "enhanced_tui"
4
+ require_relative "../../workflows/selector"
4
5
 
5
6
  module Aidp
6
7
  module Harness
@@ -12,6 +13,7 @@ module Aidp
12
13
  def initialize(tui = nil)
13
14
  @tui = tui || EnhancedTUI.new
14
15
  @user_input = {}
16
+ @workflow_selector = Aidp::Workflows::Selector.new
15
17
  end
16
18
 
17
19
  def select_workflow(harness_mode: false, mode: :analyze)
@@ -29,7 +31,7 @@ module Aidp
29
31
  when :analyze
30
32
  select_analyze_workflow_interactive
31
33
  when :execute
32
- select_execute_workflow_interactive
34
+ select_execute_workflow_interactive_new(mode)
33
35
  else
34
36
  raise ArgumentError, "Unknown mode: #{mode}"
35
37
  end
@@ -65,6 +67,22 @@ module Aidp
65
67
  }
66
68
  end
67
69
 
70
+ def select_execute_workflow_interactive_new(mode)
71
+ # Step 1: Collect project information
72
+ collect_project_info_interactive
73
+
74
+ # Step 2: Use new workflow selector
75
+ result = @workflow_selector.select_workflow(mode)
76
+
77
+ {
78
+ workflow_type: result[:workflow_key],
79
+ steps: result[:steps],
80
+ user_input: @user_input,
81
+ workflow: result[:workflow]
82
+ }
83
+ end
84
+
85
+ # Legacy method - kept for backward compatibility if needed
68
86
  def select_execute_workflow_interactive
69
87
  # Step 1: Collect project information
70
88
  collect_project_info_interactive
@@ -160,10 +178,10 @@ module Aidp
160
178
 
161
179
  def generate_exploration_steps
162
180
  [
163
- "00_PRD", # Generate PRD from user input (no manual gate)
181
+ "00_PRD", # Generate PRD from user input (no manual gate)
164
182
  "10_TESTING_STRATEGY", # Ensure we have tests
165
- "11_STATIC_ANALYSIS", # Code quality
166
- "16_IMPLEMENTATION" # Special step for actual development work
183
+ "11_STATIC_ANALYSIS", # Code quality
184
+ "16_IMPLEMENTATION" # Special step for actual development work
167
185
  ]
168
186
  end
169
187