aidp 0.10.0 → 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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +194 -25
  3. data/lib/aidp/analyze/kb_inspector.rb +2 -15
  4. data/lib/aidp/analyze/progress.rb +2 -1
  5. data/lib/aidp/analyze/ruby_maat_integration.rb +2 -15
  6. data/lib/aidp/analyze/runner.rb +64 -20
  7. data/lib/aidp/analyze/steps.rb +10 -8
  8. data/lib/aidp/analyze/tree_sitter_grammar_loader.rb +2 -13
  9. data/lib/aidp/analyze/tree_sitter_scan.rb +2 -13
  10. data/lib/aidp/cli/checkpoint_command.rb +98 -0
  11. data/lib/aidp/cli/first_run_wizard.rb +65 -94
  12. data/lib/aidp/cli/jobs_command.rb +249 -34
  13. data/lib/aidp/cli.rb +312 -38
  14. data/lib/aidp/config.rb +5 -8
  15. data/lib/aidp/debug_logger.rb +4 -4
  16. data/lib/aidp/debug_mixin.rb +11 -4
  17. data/lib/aidp/execute/checkpoint.rb +282 -0
  18. data/lib/aidp/execute/checkpoint_display.rb +221 -0
  19. data/lib/aidp/execute/progress.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +62 -0
  21. data/lib/aidp/execute/runner.rb +53 -24
  22. data/lib/aidp/execute/steps.rb +36 -27
  23. data/lib/aidp/execute/work_loop_runner.rb +308 -0
  24. data/lib/aidp/execute/workflow_selector.rb +26 -17
  25. data/lib/aidp/harness/condition_detector.rb +4 -4
  26. data/lib/aidp/harness/config_schema.rb +40 -0
  27. data/lib/aidp/harness/config_validator.rb +3 -6
  28. data/lib/aidp/harness/configuration.rb +35 -1
  29. data/lib/aidp/harness/enhanced_runner.rb +22 -1
  30. data/lib/aidp/harness/error_handler.rb +103 -28
  31. data/lib/aidp/harness/provider_factory.rb +4 -1
  32. data/lib/aidp/harness/provider_manager.rb +250 -15
  33. data/lib/aidp/harness/runner.rb +3 -14
  34. data/lib/aidp/harness/simple_user_interface.rb +2 -15
  35. data/lib/aidp/harness/status_display.rb +12 -17
  36. data/lib/aidp/harness/test_runner.rb +83 -0
  37. data/lib/aidp/harness/ui/enhanced_tui.rb +2 -0
  38. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -4
  39. data/lib/aidp/harness/ui/error_handler.rb +4 -0
  40. data/lib/aidp/harness/ui/frame_manager.rb +10 -8
  41. data/lib/aidp/harness/ui/job_monitor.rb +2 -0
  42. data/lib/aidp/harness/ui/navigation/main_menu.rb +4 -2
  43. data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
  44. data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
  45. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  46. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
  47. data/lib/aidp/harness/ui/progress_display.rb +8 -12
  48. data/lib/aidp/harness/ui/question_collector.rb +2 -0
  49. data/lib/aidp/harness/ui/spinner_group.rb +2 -0
  50. data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
  51. data/lib/aidp/harness/ui/status_manager.rb +4 -2
  52. data/lib/aidp/harness/ui/status_widget.rb +3 -1
  53. data/lib/aidp/harness/ui/workflow_controller.rb +2 -0
  54. data/lib/aidp/harness/user_interface.rb +12 -17
  55. data/lib/aidp/jobs/background_runner.rb +278 -0
  56. data/lib/aidp/message_display.rb +48 -0
  57. data/lib/aidp/provider_manager.rb +3 -1
  58. data/lib/aidp/providers/anthropic.rb +100 -17
  59. data/lib/aidp/providers/base.rb +42 -11
  60. data/lib/aidp/providers/codex.rb +248 -0
  61. data/lib/aidp/providers/cursor.rb +35 -42
  62. data/lib/aidp/providers/gemini.rb +25 -15
  63. data/lib/aidp/providers/github_copilot.rb +41 -42
  64. data/lib/aidp/providers/opencode.rb +34 -41
  65. data/lib/aidp/version.rb +1 -1
  66. data/lib/aidp/workflows/definitions.rb +357 -0
  67. data/lib/aidp/workflows/selector.rb +171 -0
  68. data/lib/aidp.rb +12 -0
  69. data/templates/planning/generate_llm_style_guide.md +119 -0
  70. metadata +38 -26
  71. /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
  72. /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
  73. /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
  74. /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
  75. /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
  76. /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
  77. /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
  78. /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
  79. /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
  80. /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
  81. /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
  82. /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
  83. /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
  84. /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
  85. /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
  86. /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
  87. /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
  88. /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
  89. /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
  90. /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
  91. /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
  92. /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
  93. /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
  94. /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
  95. /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
data/lib/aidp/cli.rb CHANGED
@@ -12,6 +12,8 @@ require_relative "cli/first_run_wizard"
12
12
  module Aidp
13
13
  # CLI interface for AIDP
14
14
  class CLI
15
+ include Aidp::MessageDisplay
16
+
15
17
  # Simple options holder for instance methods (used in specs)
16
18
  attr_accessor :options
17
19
 
@@ -20,21 +22,6 @@ module Aidp
20
22
  @prompt = prompt
21
23
  end
22
24
 
23
- # Helper method for consistent message display using TTY::Prompt
24
- def display_message(message, type: :info)
25
- color = case type
26
- when :error then :red
27
- when :success then :green
28
- when :warning then :yellow
29
- when :info then :blue
30
- when :highlight then :cyan
31
- when :muted then :bright_black
32
- else :white
33
- end
34
-
35
- @prompt.say(message, color: color)
36
- end
37
-
38
25
  # Instance version of harness status (used by specs; non-interactive)
39
26
  def harness_status
40
27
  modes = %i[analyze execute]
@@ -68,9 +55,9 @@ module Aidp
68
55
  status = if options[:expect_error] == true
69
56
  "error"
70
57
  elsif step.nil?
71
- "success" # Initial call without step
58
+ "success" # Initial call without step
72
59
  else
73
- "completed" # Subsequent calls with specific step
60
+ "completed" # Subsequent calls with specific step
74
61
  end
75
62
 
76
63
  {
@@ -154,20 +141,7 @@ module Aidp
154
141
  end
155
142
 
156
143
  class << self
157
- # Class-level display_message method for CLI output
158
- def display_message(message, type: :info)
159
- prompt = TTY::Prompt.new
160
- color = case type
161
- when :error then :red
162
- when :success then :green
163
- when :warning then :yellow
164
- when :info then :blue
165
- when :highlight then :cyan
166
- when :muted then :bright_black
167
- else :white
168
- end
169
- prompt.say(message, color: color)
170
- end
144
+ extend Aidp::MessageDisplay::ClassMethods
171
145
 
172
146
  def run(args = ARGV)
173
147
  # Handle subcommands first (status, jobs, kb, harness)
@@ -251,15 +225,58 @@ module Aidp
251
225
  options = {}
252
226
 
253
227
  parser = OptionParser.new do |opts|
254
- opts.banner = "Usage: aidp [options]"
228
+ opts.banner = "Usage: aidp [COMMAND] [options]"
229
+ opts.separator ""
230
+ opts.separator "AI Development Pipeline - Autonomous development workflow automation"
255
231
  opts.separator ""
256
- opts.separator "Start the interactive TUI (default)"
232
+ opts.separator "Commands:"
233
+ opts.separator " analyze [--background] Start analyze mode workflow"
234
+ opts.separator " execute [--background] Start execute mode workflow"
235
+ opts.separator " status Show current system status"
236
+ opts.separator " jobs Manage background jobs"
237
+ opts.separator " list - List all jobs"
238
+ opts.separator " status <id> [--follow] - Show job status"
239
+ opts.separator " logs <id> [--tail] - Show job logs"
240
+ opts.separator " stop <id> - Stop a running job"
241
+ opts.separator " checkpoint View progress checkpoints and metrics"
242
+ opts.separator " show - Show latest checkpoint"
243
+ opts.separator " summary [--watch] - Show progress summary with trends"
244
+ opts.separator " history [N] - Show last N checkpoints"
245
+ opts.separator " metrics - Show detailed metrics"
246
+ opts.separator " clear [--force] - Clear checkpoint data"
247
+ opts.separator " providers Show provider health dashboard"
248
+ opts.separator " harness Manage harness state"
249
+ opts.separator " status - Show harness status"
250
+ opts.separator " reset - Reset harness state"
251
+ opts.separator " kb Knowledge base commands"
252
+ opts.separator " show <topic> - Show knowledge base topic"
257
253
  opts.separator ""
258
254
  opts.separator "Options:"
259
255
 
260
256
  opts.on("-h", "--help", "Show this help message") { options[:help] = true }
261
257
  opts.on("-v", "--version", "Show version information") { options[:version] = true }
262
- opts.on("--setup-config", "Setup or reconfigure config file with current values as defaults") { options[:setup_config] = true }
258
+ opts.on("--setup-config", "Setup or reconfigure config file") { options[:setup_config] = true }
259
+
260
+ opts.separator ""
261
+ opts.separator "Examples:"
262
+ opts.separator " # Start background execution"
263
+ opts.separator " aidp execute --background"
264
+ opts.separator " aidp execute --background --follow # Start and follow logs"
265
+ opts.separator ""
266
+ opts.separator " # Monitor background jobs"
267
+ opts.separator " aidp jobs list # List all jobs"
268
+ opts.separator " aidp jobs status <job_id> # Show job status"
269
+ opts.separator " aidp jobs logs <job_id> --tail # Tail job logs"
270
+ opts.separator ""
271
+ opts.separator " # Watch progress in real-time"
272
+ opts.separator " aidp checkpoint summary --watch # Auto-refresh every 5s"
273
+ opts.separator " aidp checkpoint summary --watch --interval 10"
274
+ opts.separator ""
275
+ opts.separator " # Other commands"
276
+ opts.separator " aidp providers # Check provider health"
277
+ opts.separator " aidp checkpoint history 20 # Show last 20 checkpoints"
278
+ opts.separator ""
279
+ opts.separator "For more information, visit: https://github.com/viamin/aidp"
263
280
  end
264
281
 
265
282
  parser.parse!(args)
@@ -270,18 +287,20 @@ module Aidp
270
287
  # Determine if the invocation is a subcommand style call
271
288
  def subcommand?(args)
272
289
  return false if args.nil? || args.empty?
273
- %w[status jobs kb harness execute analyze].include?(args.first)
290
+ %w[status jobs kb harness execute analyze providers checkpoint].include?(args.first)
274
291
  end
275
292
 
276
293
  def run_subcommand(args)
277
294
  cmd = args.shift
278
295
  case cmd
279
296
  when "status" then run_status_command
280
- when "jobs" then run_jobs_command
297
+ when "jobs" then run_jobs_command(args)
281
298
  when "kb" then run_kb_command(args)
282
299
  when "harness" then run_harness_command(args)
283
300
  when "execute" then run_execute_command(args)
284
301
  when "analyze" then run_execute_command(args, mode: :analyze) # symmetry
302
+ when "providers" then run_providers_command(args)
303
+ when "checkpoint" then run_checkpoint_command(args)
285
304
  else
286
305
  display_message("Unknown command: #{cmd}", type: :info)
287
306
  return 1
@@ -298,10 +317,11 @@ module Aidp
298
317
  display_message("Use 'aidp analyze' or 'aidp execute' to start a workflow", type: :info)
299
318
  end
300
319
 
301
- def run_jobs_command
320
+ def run_jobs_command(args = [])
302
321
  require_relative "cli/jobs_command"
303
322
  jobs_cmd = Aidp::CLI::JobsCommand.new(prompt: TTY::Prompt.new)
304
- jobs_cmd.run
323
+ subcommand = args.shift
324
+ jobs_cmd.run(subcommand, args)
305
325
  end
306
326
 
307
327
  def run_kb_command(args)
@@ -336,6 +356,8 @@ module Aidp
336
356
  approve_step = nil
337
357
  reset = false
338
358
  no_harness = false
359
+ background = false
360
+ follow = false
339
361
 
340
362
  until flags.empty?
341
363
  token = flags.shift
@@ -343,6 +365,8 @@ module Aidp
343
365
  when "--no-harness" then no_harness = true
344
366
  when "--reset" then reset = true
345
367
  when "--approve" then approve_step = flags.shift
368
+ when "--background" then background = true
369
+ when "--follow" then follow = true
346
370
  else
347
371
  step ||= token unless token.start_with?("--")
348
372
  end
@@ -361,6 +385,35 @@ module Aidp
361
385
  display_message("Use 'aidp #{mode}' without arguments", type: :info)
362
386
  return
363
387
  end
388
+
389
+ # Handle background execution
390
+ if background
391
+ require_relative "jobs/background_runner"
392
+ runner = Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
393
+
394
+ display_message("Starting #{mode} mode in background...", type: :info)
395
+ job_id = runner.start(mode, {})
396
+
397
+ display_message("✓ Started background job: #{job_id}", type: :success)
398
+ display_message("", type: :info)
399
+ display_message("Monitor progress:", type: :info)
400
+ display_message(" aidp jobs status #{job_id}", type: :info)
401
+ display_message(" aidp jobs logs #{job_id} --tail", type: :info)
402
+ display_message(" aidp checkpoint summary", type: :info)
403
+ display_message("", type: :info)
404
+ display_message("Stop the job:", type: :info)
405
+ display_message(" aidp jobs stop #{job_id}", type: :info)
406
+
407
+ if follow
408
+ display_message("", type: :info)
409
+ display_message("Following logs (Ctrl+C to stop following)...", type: :info)
410
+ sleep 1 # Give daemon time to start writing logs
411
+ runner.follow_job_logs(job_id)
412
+ end
413
+
414
+ return
415
+ end
416
+
364
417
  if step
365
418
  display_message("Running #{mode} step '#{step}' with enhanced TUI harness", type: :highlight)
366
419
  display_message("progress indicators", type: :info)
@@ -386,6 +439,227 @@ module Aidp
386
439
  display_message("workflow selection options", type: :info)
387
440
  end
388
441
 
442
+ def run_checkpoint_command(args)
443
+ require_relative "execute/checkpoint"
444
+ require_relative "execute/checkpoint_display"
445
+
446
+ sub = args.shift || "summary"
447
+ checkpoint = Aidp::Execute::Checkpoint.new(Dir.pwd)
448
+ display = Aidp::Execute::CheckpointDisplay.new
449
+
450
+ case sub
451
+ when "show"
452
+ latest = checkpoint.latest_checkpoint
453
+ if latest
454
+ display.display_checkpoint(latest, show_details: true)
455
+ else
456
+ display_message("No checkpoint data found.", type: :info)
457
+ end
458
+
459
+ when "summary"
460
+ watch = args.include?("--watch")
461
+ interval = extract_interval_option(args) || 5
462
+
463
+ if watch
464
+ watch_checkpoint_summary(checkpoint, display, interval)
465
+ else
466
+ summary = checkpoint.progress_summary
467
+ if summary
468
+ display.display_progress_summary(summary)
469
+ else
470
+ display_message("No checkpoint data found.", type: :info)
471
+ end
472
+ end
473
+
474
+ when "history"
475
+ limit = args.shift || "10"
476
+ history = checkpoint.checkpoint_history(limit: limit.to_i)
477
+ if history.any?
478
+ display.display_checkpoint_history(history, limit: limit.to_i)
479
+ else
480
+ display_message("No checkpoint history found.", type: :info)
481
+ end
482
+
483
+ when "metrics"
484
+ latest = checkpoint.latest_checkpoint
485
+ unless latest
486
+ display_message("No checkpoint data found.", type: :info)
487
+ return
488
+ end
489
+
490
+ display_message("", type: :info)
491
+ display_message("📊 Detailed Metrics", type: :info)
492
+ display_message("=" * 60, type: :muted)
493
+
494
+ metrics = latest[:metrics]
495
+ display_message("Lines of Code: #{metrics[:lines_of_code]}", type: :info)
496
+ display_message("File Count: #{metrics[:file_count]}", type: :info)
497
+ display_message("Test Coverage: #{metrics[:test_coverage]}%", type: :info)
498
+ display_message("Code Quality: #{metrics[:code_quality]}%", type: :info)
499
+ display_message("PRD Task Progress: #{metrics[:prd_task_progress]}%", type: :info)
500
+
501
+ if metrics[:tests_passing]
502
+ status = metrics[:tests_passing] ? "✓ Passing" : "✗ Failing"
503
+ display_message("Tests: #{status}", type: :info)
504
+ end
505
+
506
+ if metrics[:linters_passing]
507
+ status = metrics[:linters_passing] ? "✓ Passing" : "✗ Failing"
508
+ display_message("Linters: #{status}", type: :info)
509
+ end
510
+
511
+ display_message("=" * 60, type: :muted)
512
+ display_message("", type: :info)
513
+
514
+ when "clear"
515
+ force = args.include?("--force")
516
+ unless force
517
+ prompt = TTY::Prompt.new
518
+ confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
519
+ return unless confirm
520
+ end
521
+
522
+ checkpoint.clear
523
+ display_message("✓ Checkpoint data cleared.", type: :success)
524
+
525
+ else
526
+ display_message("Usage: aidp checkpoint <show|summary|history|metrics|clear>", type: :info)
527
+ display_message(" show - Show the latest checkpoint data", type: :info)
528
+ display_message(" summary [--watch] - Show progress summary with trends", type: :info)
529
+ display_message(" history [N] - Show last N checkpoints", type: :info)
530
+ display_message(" metrics - Show detailed metrics", type: :info)
531
+ display_message(" clear [--force] - Clear all checkpoint data", type: :info)
532
+ end
533
+ end
534
+
535
+ def watch_checkpoint_summary(checkpoint, display, interval)
536
+ display_message("Watching checkpoint summary (refresh: #{interval}s, Ctrl+C to exit)...", type: :info)
537
+ display_message("", type: :info)
538
+
539
+ begin
540
+ loop do
541
+ # Clear screen
542
+ print "\e[2J\e[H"
543
+
544
+ summary = checkpoint.progress_summary
545
+ if summary
546
+ display.display_progress_summary(summary)
547
+
548
+ # Show last update time
549
+ if summary[:current] && summary[:current][:timestamp]
550
+ last_update = Time.parse(summary[:current][:timestamp])
551
+ age = Time.now - last_update
552
+ display_message("", type: :info)
553
+ display_message("Last update: #{format_time_ago_simple(age)} | Refreshing in #{interval}s...", type: :muted)
554
+ end
555
+ else
556
+ display_message("No checkpoint data found. Waiting for data...", type: :info)
557
+ end
558
+
559
+ sleep interval
560
+ end
561
+ rescue Interrupt
562
+ display_message("\nStopped watching checkpoint summary", type: :info)
563
+ end
564
+ end
565
+
566
+ def extract_interval_option(args)
567
+ args.each_with_index do |arg, i|
568
+ if arg == "--interval" && args[i + 1]
569
+ return args[i + 1].to_i
570
+ elsif arg.start_with?("--interval=")
571
+ return arg.split("=")[1].to_i
572
+ end
573
+ end
574
+ nil
575
+ end
576
+
577
+ def format_time_ago_simple(seconds)
578
+ if seconds < 60
579
+ "#{seconds.to_i}s ago"
580
+ elsif seconds < 3600
581
+ "#{(seconds / 60).to_i}m ago"
582
+ else
583
+ "#{(seconds / 3600).to_i}h ago"
584
+ end
585
+ end
586
+
587
+ def run_providers_command(args)
588
+ # Accept flags directly on `aidp providers` now (health is implicit)
589
+ no_color = false
590
+ args.reject! do |a|
591
+ if a == "--no-color"
592
+ no_color = true
593
+ true
594
+ else
595
+ false
596
+ end
597
+ end
598
+ configuration = Aidp::Harness::Configuration.new(Dir.pwd)
599
+ pm = Aidp::Harness::ProviderManager.new(configuration, prompt: TTY::Prompt.new)
600
+
601
+ # Use TTY::Spinner for progress indication
602
+ require "tty-spinner"
603
+ start_time = Time.now
604
+ spinner = TTY::Spinner.new(":spinner Gathering provider health...", format: :dots)
605
+ spinner.auto_spin
606
+
607
+ begin
608
+ rows = pm.health_dashboard
609
+ ensure
610
+ spinner.stop
611
+ elapsed = (Time.now - start_time).round(2)
612
+ display_message("Provider Health Dashboard (#{elapsed}s)", type: :highlight)
613
+ end
614
+ require "tty-table"
615
+ color = ->(text, code) { "\e[#{code}m#{text}\e[0m" }
616
+ status_color = lambda do |status|
617
+ case status
618
+ when /healthy/ then 32
619
+ when /unhealthy_auth/ then 31
620
+ when /unhealthy/ then 33
621
+ when /circuit/ then 35
622
+ else 37
623
+ end
624
+ end
625
+ availability_color = ->(avail) { (avail == "yes") ? 32 : 31 }
626
+ rate_color = ->(rl) { rl.start_with?("yes") ? 33 : 36 }
627
+ circuit_color = lambda do |c|
628
+ c.start_with?("open") ? 31 : 32
629
+ end
630
+ table_rows = rows.map do |r|
631
+ last_used = r[:last_used] ? r[:last_used].strftime("%H:%M:%S") : "-"
632
+ cb = r[:circuit_breaker]
633
+ cb += " (#{r[:circuit_breaker_remaining]}s)" if r[:circuit_breaker_remaining]
634
+ rl = if r[:rate_limited]
635
+ r[:rate_limit_reset_in] ? "yes (#{r[:rate_limit_reset_in]}s)" : "yes"
636
+ else
637
+ "no"
638
+ end
639
+ tokens = (r[:total_tokens].to_i > 0) ? r[:total_tokens].to_s : "0"
640
+ reason = r[:unhealthy_reason] || "-"
641
+ if no_color || !$stdout.tty?
642
+ [r[:provider], r[:status], (r[:available] ? "yes" : "no"), cb, rl, tokens, last_used, reason]
643
+ else
644
+ [
645
+ color.call(r[:provider], "1;97"),
646
+ color.call(r[:status], status_color.call(r[:status])),
647
+ color.call(r[:available] ? "yes" : "no", availability_color.call(r[:available] ? "yes" : "no")),
648
+ color.call(cb, circuit_color.call(cb)),
649
+ color.call(rl, rate_color.call(rl)),
650
+ color.call(tokens, 37),
651
+ color.call(last_used, 90),
652
+ ((reason == "-") ? reason : color.call(reason, 33))
653
+ ]
654
+ end
655
+ end
656
+ header = ["Provider", "Status", "Avail", "Circuit", "RateLimited", "Tokens", "LastUsed", "Reason"]
657
+ table = TTY::Table.new header, table_rows
658
+ display_message(table.render(:basic), type: :info)
659
+ rescue => e
660
+ display_message("Failed to display provider health: #{e.message}", type: :error)
661
+ end
662
+
389
663
  def extract_mode_option(args)
390
664
  mode = nil
391
665
  args.each do |arg|
data/lib/aidp/config.rb CHANGED
@@ -165,14 +165,10 @@ module Aidp
165
165
  }.freeze
166
166
 
167
167
  def self.load(project_dir = Dir.pwd)
168
- # Try new aidp.yml format first, then fall back to .aidp.yml
169
- config_file = File.join(project_dir, "aidp.yml")
170
- legacy_config_file = File.join(project_dir, ".aidp.yml")
168
+ config_file = File.join(project_dir, ".aidp", "aidp.yml")
171
169
 
172
170
  if File.exist?(config_file)
173
171
  load_yaml_config(config_file)
174
- elsif File.exist?(legacy_config_file)
175
- load_yaml_config(legacy_config_file)
176
172
  else
177
173
  {}
178
174
  end
@@ -248,15 +244,16 @@ module Aidp
248
244
 
249
245
  # Check if configuration file exists
250
246
  def self.config_exists?(project_dir = Dir.pwd)
251
- File.exist?(File.join(project_dir, "aidp.yml")) ||
252
- File.exist?(File.join(project_dir, ".aidp.yml"))
247
+ File.exist?(File.join(project_dir, ".aidp", "aidp.yml"))
253
248
  end
254
249
 
255
250
  # Create example configuration file
256
251
  def self.create_example_config(project_dir = Dir.pwd)
257
- config_path = File.join(project_dir, "aidp.yml")
252
+ config_path = File.join(project_dir, ".aidp", "aidp.yml")
258
253
  return false if File.exist?(config_path)
259
254
 
255
+ FileUtils.mkdir_p(File.dirname(config_path))
256
+
260
257
  example_config = {
261
258
  harness: {
262
259
  max_retries: 2,
@@ -94,7 +94,7 @@ module Aidp
94
94
 
95
95
  # Also output to console if debug is enabled
96
96
  if ENV["DEBUG"] && ENV["DEBUG"].to_i > 0
97
- puts "\e[36m#{banner}\e[0m" # Cyan color
97
+ puts "\e[36m#{banner}\e[0m" # Cyan color
98
98
  end
99
99
  end
100
100
 
@@ -127,11 +127,11 @@ module Aidp
127
127
  when :error
128
128
  warn message
129
129
  when :warn
130
- puts "\e[33m#{message}\e[0m" # Yellow
130
+ puts "\e[33m#{message}\e[0m" # Yellow
131
131
  when :info
132
- puts "\e[36m#{message}\e[0m" # Cyan
132
+ puts "\e[36m#{message}\e[0m" # Cyan
133
133
  when :debug
134
- puts "\e[90m#{message}\e[0m" # Gray
134
+ puts "\e[90m#{message}\e[0m" # Gray
135
135
  else
136
136
  puts message
137
137
  end
@@ -7,8 +7,8 @@ module Aidp
7
7
  module DebugMixin
8
8
  # Debug levels
9
9
  DEBUG_OFF = 0
10
- DEBUG_BASIC = 1 # Commands and stderr
11
- DEBUG_VERBOSE = 2 # Everything including prompts and stdout
10
+ DEBUG_BASIC = 1 # Commands and stderr
11
+ DEBUG_VERBOSE = 2 # Everything including prompts and stdout
12
12
 
13
13
  def self.included(base)
14
14
  base.extend(ClassMethods)
@@ -145,7 +145,7 @@ module Aidp
145
145
  end
146
146
 
147
147
  # Execute command with debug logging
148
- def debug_execute_command(cmd, args: [], input: nil, timeout: nil, **options)
148
+ def debug_execute_command(cmd, args: [], input: nil, timeout: nil, streaming: false, **options)
149
149
  require "tty-command"
150
150
 
151
151
  command_str = [cmd, *args].join(" ")
@@ -154,7 +154,14 @@ module Aidp
154
154
  debug_log("🚀 Starting command execution: #{command_str}", level: :info)
155
155
 
156
156
  begin
157
- cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
157
+ # Configure printer based on streaming mode
158
+ if streaming
159
+ # Use progress printer for real-time output
160
+ cmd_obj = TTY::Command.new(printer: :progress)
161
+ debug_log("📺 Streaming mode enabled - showing real-time output", level: :info)
162
+ else
163
+ cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
164
+ end
158
165
 
159
166
  # Prepare input
160
167
  input_data = nil