aidp 0.10.0 → 0.12.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 (98) 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/mcp_dashboard.rb +205 -0
  14. data/lib/aidp/cli.rb +517 -43
  15. data/lib/aidp/config.rb +5 -8
  16. data/lib/aidp/debug_logger.rb +4 -4
  17. data/lib/aidp/debug_mixin.rb +11 -4
  18. data/lib/aidp/execute/checkpoint.rb +282 -0
  19. data/lib/aidp/execute/checkpoint_display.rb +221 -0
  20. data/lib/aidp/execute/progress.rb +2 -1
  21. data/lib/aidp/execute/prompt_manager.rb +62 -0
  22. data/lib/aidp/execute/runner.rb +53 -24
  23. data/lib/aidp/execute/steps.rb +36 -27
  24. data/lib/aidp/execute/work_loop_runner.rb +308 -0
  25. data/lib/aidp/execute/workflow_selector.rb +26 -17
  26. data/lib/aidp/harness/condition_detector.rb +4 -4
  27. data/lib/aidp/harness/config_schema.rb +40 -0
  28. data/lib/aidp/harness/config_validator.rb +3 -6
  29. data/lib/aidp/harness/configuration.rb +35 -1
  30. data/lib/aidp/harness/enhanced_runner.rb +22 -1
  31. data/lib/aidp/harness/error_handler.rb +103 -28
  32. data/lib/aidp/harness/provider_factory.rb +4 -1
  33. data/lib/aidp/harness/provider_info.rb +366 -0
  34. data/lib/aidp/harness/provider_manager.rb +250 -15
  35. data/lib/aidp/harness/runner.rb +3 -14
  36. data/lib/aidp/harness/simple_user_interface.rb +2 -15
  37. data/lib/aidp/harness/status_display.rb +12 -17
  38. data/lib/aidp/harness/test_runner.rb +83 -0
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +2 -0
  40. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +44 -5
  41. data/lib/aidp/harness/ui/error_handler.rb +4 -0
  42. data/lib/aidp/harness/ui/frame_manager.rb +10 -8
  43. data/lib/aidp/harness/ui/job_monitor.rb +2 -0
  44. data/lib/aidp/harness/ui/navigation/main_menu.rb +4 -2
  45. data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
  46. data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
  47. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  48. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
  49. data/lib/aidp/harness/ui/progress_display.rb +8 -12
  50. data/lib/aidp/harness/ui/question_collector.rb +2 -0
  51. data/lib/aidp/harness/ui/spinner_group.rb +2 -0
  52. data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
  53. data/lib/aidp/harness/ui/status_manager.rb +4 -2
  54. data/lib/aidp/harness/ui/status_widget.rb +3 -1
  55. data/lib/aidp/harness/ui/workflow_controller.rb +2 -0
  56. data/lib/aidp/harness/user_interface.rb +12 -17
  57. data/lib/aidp/jobs/background_runner.rb +278 -0
  58. data/lib/aidp/message_display.rb +48 -0
  59. data/lib/aidp/provider_manager.rb +3 -1
  60. data/lib/aidp/providers/anthropic.rb +100 -17
  61. data/lib/aidp/providers/base.rb +42 -11
  62. data/lib/aidp/providers/codex.rb +248 -0
  63. data/lib/aidp/providers/cursor.rb +35 -42
  64. data/lib/aidp/providers/gemini.rb +25 -15
  65. data/lib/aidp/providers/github_copilot.rb +41 -42
  66. data/lib/aidp/providers/opencode.rb +34 -41
  67. data/lib/aidp/version.rb +1 -1
  68. data/lib/aidp/workflows/definitions.rb +357 -0
  69. data/lib/aidp/workflows/guided_agent.rb +400 -0
  70. data/lib/aidp/workflows/selector.rb +171 -0
  71. data/lib/aidp.rb +12 -0
  72. data/templates/planning/generate_llm_style_guide.md +119 -0
  73. metadata +41 -26
  74. /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
  75. /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
  76. /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
  77. /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
  78. /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
  79. /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
  80. /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
  81. /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
  82. /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
  83. /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
  84. /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
  85. /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
  86. /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
  87. /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
  88. /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
  89. /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
  90. /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
  91. /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
  92. /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
  93. /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
  94. /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
  95. /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
  96. /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
  97. /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
  98. /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)
@@ -209,7 +183,7 @@ module Aidp
209
183
 
210
184
  # Initialize the enhanced TUI
211
185
  tui = Aidp::Harness::UI::EnhancedTUI.new
212
- workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui)
186
+ workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
213
187
 
214
188
  # Start TUI display loop
215
189
  tui.start_display_loop
@@ -251,15 +225,67 @@ 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 " info <name> - Show detailed provider information"
249
+ opts.separator " refresh [name] - Refresh provider capabilities info"
250
+ opts.separator " mcp MCP server dashboard and management"
251
+ opts.separator " dashboard - Show all MCP servers across providers"
252
+ opts.separator " check <servers...> - Check provider eligibility for servers"
253
+ opts.separator " harness Manage harness state"
254
+ opts.separator " status - Show harness status"
255
+ opts.separator " reset - Reset harness state"
256
+ opts.separator " kb Knowledge base commands"
257
+ opts.separator " show <topic> - Show knowledge base topic"
257
258
  opts.separator ""
258
259
  opts.separator "Options:"
259
260
 
260
261
  opts.on("-h", "--help", "Show this help message") { options[:help] = true }
261
262
  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 }
263
+ opts.on("--setup-config", "Setup or reconfigure config file") { options[:setup_config] = true }
264
+
265
+ opts.separator ""
266
+ opts.separator "Examples:"
267
+ opts.separator " # Start background execution"
268
+ opts.separator " aidp execute --background"
269
+ opts.separator " aidp execute --background --follow # Start and follow logs"
270
+ opts.separator ""
271
+ opts.separator " # Monitor background jobs"
272
+ opts.separator " aidp jobs list # List all jobs"
273
+ opts.separator " aidp jobs status <job_id> # Show job status"
274
+ opts.separator " aidp jobs logs <job_id> --tail # Tail job logs"
275
+ opts.separator ""
276
+ opts.separator " # Watch progress in real-time"
277
+ opts.separator " aidp checkpoint summary --watch # Auto-refresh every 5s"
278
+ opts.separator " aidp checkpoint summary --watch --interval 10"
279
+ opts.separator ""
280
+ opts.separator " # Other commands"
281
+ opts.separator " aidp providers # Check provider health"
282
+ opts.separator " aidp providers info claude # Show detailed provider info"
283
+ opts.separator " aidp providers refresh # Refresh all provider info"
284
+ opts.separator " aidp mcp # Show MCP server dashboard"
285
+ opts.separator " aidp mcp check dash-api filesystem # Check provider eligibility"
286
+ opts.separator " aidp checkpoint history 20 # Show last 20 checkpoints"
287
+ opts.separator ""
288
+ opts.separator "For more information, visit: https://github.com/viamin/aidp"
263
289
  end
264
290
 
265
291
  parser.parse!(args)
@@ -270,18 +296,21 @@ module Aidp
270
296
  # Determine if the invocation is a subcommand style call
271
297
  def subcommand?(args)
272
298
  return false if args.nil? || args.empty?
273
- %w[status jobs kb harness execute analyze].include?(args.first)
299
+ %w[status jobs kb harness execute analyze providers checkpoint mcp].include?(args.first)
274
300
  end
275
301
 
276
302
  def run_subcommand(args)
277
303
  cmd = args.shift
278
304
  case cmd
279
305
  when "status" then run_status_command
280
- when "jobs" then run_jobs_command
306
+ when "jobs" then run_jobs_command(args)
281
307
  when "kb" then run_kb_command(args)
282
308
  when "harness" then run_harness_command(args)
283
309
  when "execute" then run_execute_command(args)
284
310
  when "analyze" then run_execute_command(args, mode: :analyze) # symmetry
311
+ when "providers" then run_providers_command(args)
312
+ when "checkpoint" then run_checkpoint_command(args)
313
+ when "mcp" then run_mcp_command(args)
285
314
  else
286
315
  display_message("Unknown command: #{cmd}", type: :info)
287
316
  return 1
@@ -298,10 +327,11 @@ module Aidp
298
327
  display_message("Use 'aidp analyze' or 'aidp execute' to start a workflow", type: :info)
299
328
  end
300
329
 
301
- def run_jobs_command
330
+ def run_jobs_command(args = [])
302
331
  require_relative "cli/jobs_command"
303
332
  jobs_cmd = Aidp::CLI::JobsCommand.new(prompt: TTY::Prompt.new)
304
- jobs_cmd.run
333
+ subcommand = args.shift
334
+ jobs_cmd.run(subcommand, args)
305
335
  end
306
336
 
307
337
  def run_kb_command(args)
@@ -336,6 +366,8 @@ module Aidp
336
366
  approve_step = nil
337
367
  reset = false
338
368
  no_harness = false
369
+ background = false
370
+ follow = false
339
371
 
340
372
  until flags.empty?
341
373
  token = flags.shift
@@ -343,6 +375,8 @@ module Aidp
343
375
  when "--no-harness" then no_harness = true
344
376
  when "--reset" then reset = true
345
377
  when "--approve" then approve_step = flags.shift
378
+ when "--background" then background = true
379
+ when "--follow" then follow = true
346
380
  else
347
381
  step ||= token unless token.start_with?("--")
348
382
  end
@@ -361,6 +395,35 @@ module Aidp
361
395
  display_message("Use 'aidp #{mode}' without arguments", type: :info)
362
396
  return
363
397
  end
398
+
399
+ # Handle background execution
400
+ if background
401
+ require_relative "jobs/background_runner"
402
+ runner = Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
403
+
404
+ display_message("Starting #{mode} mode in background...", type: :info)
405
+ job_id = runner.start(mode, {})
406
+
407
+ display_message("✓ Started background job: #{job_id}", type: :success)
408
+ display_message("", type: :info)
409
+ display_message("Monitor progress:", type: :info)
410
+ display_message(" aidp jobs status #{job_id}", type: :info)
411
+ display_message(" aidp jobs logs #{job_id} --tail", type: :info)
412
+ display_message(" aidp checkpoint summary", type: :info)
413
+ display_message("", type: :info)
414
+ display_message("Stop the job:", type: :info)
415
+ display_message(" aidp jobs stop #{job_id}", type: :info)
416
+
417
+ if follow
418
+ display_message("", type: :info)
419
+ display_message("Following logs (Ctrl+C to stop following)...", type: :info)
420
+ sleep 1 # Give daemon time to start writing logs
421
+ runner.follow_job_logs(job_id)
422
+ end
423
+
424
+ return
425
+ end
426
+
364
427
  if step
365
428
  display_message("Running #{mode} step '#{step}' with enhanced TUI harness", type: :highlight)
366
429
  display_message("progress indicators", type: :info)
@@ -386,6 +449,414 @@ module Aidp
386
449
  display_message("workflow selection options", type: :info)
387
450
  end
388
451
 
452
+ def run_checkpoint_command(args)
453
+ require_relative "execute/checkpoint"
454
+ require_relative "execute/checkpoint_display"
455
+
456
+ sub = args.shift || "summary"
457
+ checkpoint = Aidp::Execute::Checkpoint.new(Dir.pwd)
458
+ display = Aidp::Execute::CheckpointDisplay.new
459
+
460
+ case sub
461
+ when "show"
462
+ latest = checkpoint.latest_checkpoint
463
+ if latest
464
+ display.display_checkpoint(latest, show_details: true)
465
+ else
466
+ display_message("No checkpoint data found.", type: :info)
467
+ end
468
+
469
+ when "summary"
470
+ watch = args.include?("--watch")
471
+ interval = extract_interval_option(args) || 5
472
+
473
+ if watch
474
+ watch_checkpoint_summary(checkpoint, display, interval)
475
+ else
476
+ summary = checkpoint.progress_summary
477
+ if summary
478
+ display.display_progress_summary(summary)
479
+ else
480
+ display_message("No checkpoint data found.", type: :info)
481
+ end
482
+ end
483
+
484
+ when "history"
485
+ limit = args.shift || "10"
486
+ history = checkpoint.checkpoint_history(limit: limit.to_i)
487
+ if history.any?
488
+ display.display_checkpoint_history(history, limit: limit.to_i)
489
+ else
490
+ display_message("No checkpoint history found.", type: :info)
491
+ end
492
+
493
+ when "metrics"
494
+ latest = checkpoint.latest_checkpoint
495
+ unless latest
496
+ display_message("No checkpoint data found.", type: :info)
497
+ return
498
+ end
499
+
500
+ display_message("", type: :info)
501
+ display_message("📊 Detailed Metrics", type: :info)
502
+ display_message("=" * 60, type: :muted)
503
+
504
+ metrics = latest[:metrics]
505
+ display_message("Lines of Code: #{metrics[:lines_of_code]}", type: :info)
506
+ display_message("File Count: #{metrics[:file_count]}", type: :info)
507
+ display_message("Test Coverage: #{metrics[:test_coverage]}%", type: :info)
508
+ display_message("Code Quality: #{metrics[:code_quality]}%", type: :info)
509
+ display_message("PRD Task Progress: #{metrics[:prd_task_progress]}%", type: :info)
510
+
511
+ if metrics[:tests_passing]
512
+ status = metrics[:tests_passing] ? "✓ Passing" : "✗ Failing"
513
+ display_message("Tests: #{status}", type: :info)
514
+ end
515
+
516
+ if metrics[:linters_passing]
517
+ status = metrics[:linters_passing] ? "✓ Passing" : "✗ Failing"
518
+ display_message("Linters: #{status}", type: :info)
519
+ end
520
+
521
+ display_message("=" * 60, type: :muted)
522
+ display_message("", type: :info)
523
+
524
+ when "clear"
525
+ force = args.include?("--force")
526
+ unless force
527
+ prompt = TTY::Prompt.new
528
+ confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
529
+ return unless confirm
530
+ end
531
+
532
+ checkpoint.clear
533
+ display_message("✓ Checkpoint data cleared.", type: :success)
534
+
535
+ else
536
+ display_message("Usage: aidp checkpoint <show|summary|history|metrics|clear>", type: :info)
537
+ display_message(" show - Show the latest checkpoint data", type: :info)
538
+ display_message(" summary [--watch] - Show progress summary with trends", type: :info)
539
+ display_message(" history [N] - Show last N checkpoints", type: :info)
540
+ display_message(" metrics - Show detailed metrics", type: :info)
541
+ display_message(" clear [--force] - Clear all checkpoint data", type: :info)
542
+ end
543
+ end
544
+
545
+ def watch_checkpoint_summary(checkpoint, display, interval)
546
+ display_message("Watching checkpoint summary (refresh: #{interval}s, Ctrl+C to exit)...", type: :info)
547
+ display_message("", type: :info)
548
+
549
+ begin
550
+ loop do
551
+ # Clear screen
552
+ print "\e[2J\e[H"
553
+
554
+ summary = checkpoint.progress_summary
555
+ if summary
556
+ display.display_progress_summary(summary)
557
+
558
+ # Show last update time
559
+ if summary[:current] && summary[:current][:timestamp]
560
+ last_update = Time.parse(summary[:current][:timestamp])
561
+ age = Time.now - last_update
562
+ display_message("", type: :info)
563
+ display_message("Last update: #{format_time_ago_simple(age)} | Refreshing in #{interval}s...", type: :muted)
564
+ end
565
+ else
566
+ display_message("No checkpoint data found. Waiting for data...", type: :info)
567
+ end
568
+
569
+ sleep interval
570
+ end
571
+ rescue Interrupt
572
+ display_message("\nStopped watching checkpoint summary", type: :info)
573
+ end
574
+ end
575
+
576
+ def extract_interval_option(args)
577
+ args.each_with_index do |arg, i|
578
+ if arg == "--interval" && args[i + 1]
579
+ return args[i + 1].to_i
580
+ elsif arg.start_with?("--interval=")
581
+ return arg.split("=")[1].to_i
582
+ end
583
+ end
584
+ nil
585
+ end
586
+
587
+ def format_time_ago_simple(seconds)
588
+ if seconds < 60
589
+ "#{seconds.to_i}s ago"
590
+ elsif seconds < 3600
591
+ "#{(seconds / 60).to_i}m ago"
592
+ else
593
+ "#{(seconds / 3600).to_i}h ago"
594
+ end
595
+ end
596
+
597
+ def run_providers_command(args)
598
+ subcommand = args.first if args.first && !args.first.start_with?("--")
599
+
600
+ case subcommand
601
+ when "info"
602
+ args.shift # Remove 'info'
603
+ run_providers_info_command(args)
604
+ return
605
+ when "refresh"
606
+ args.shift # Remove 'refresh'
607
+ run_providers_refresh_command(args)
608
+ return
609
+ end
610
+
611
+ # Accept flags directly on `aidp providers` now (health is implicit)
612
+ no_color = false
613
+ args.reject! do |a|
614
+ if a == "--no-color"
615
+ no_color = true
616
+ true
617
+ else
618
+ false
619
+ end
620
+ end
621
+ configuration = Aidp::Harness::Configuration.new(Dir.pwd)
622
+ pm = Aidp::Harness::ProviderManager.new(configuration, prompt: TTY::Prompt.new)
623
+
624
+ # Use TTY::Spinner for progress indication
625
+ require "tty-spinner"
626
+ start_time = Time.now
627
+ spinner = TTY::Spinner.new(":spinner Gathering provider health...", format: :dots)
628
+ spinner.auto_spin
629
+
630
+ begin
631
+ rows = pm.health_dashboard
632
+ ensure
633
+ spinner.stop
634
+ elapsed = (Time.now - start_time).round(2)
635
+ display_message("Provider Health Dashboard (#{elapsed}s)", type: :highlight)
636
+ end
637
+ require "tty-table"
638
+ color = ->(text, code) { "\e[#{code}m#{text}\e[0m" }
639
+ status_color = lambda do |status|
640
+ case status
641
+ when /healthy/ then 32
642
+ when /unhealthy_auth/ then 31
643
+ when /unhealthy/ then 33
644
+ when /circuit/ then 35
645
+ else 37
646
+ end
647
+ end
648
+ availability_color = ->(avail) { (avail == "yes") ? 32 : 31 }
649
+ rate_color = ->(rl) { rl.start_with?("yes") ? 33 : 36 }
650
+ circuit_color = lambda do |c|
651
+ c.start_with?("open") ? 31 : 32
652
+ end
653
+ table_rows = rows.map do |r|
654
+ last_used = r[:last_used] ? r[:last_used].strftime("%H:%M:%S") : "-"
655
+ cb = r[:circuit_breaker]
656
+ cb += " (#{r[:circuit_breaker_remaining]}s)" if r[:circuit_breaker_remaining]
657
+ rl = if r[:rate_limited]
658
+ r[:rate_limit_reset_in] ? "yes (#{r[:rate_limit_reset_in]}s)" : "yes"
659
+ else
660
+ "no"
661
+ end
662
+ tokens = (r[:total_tokens].to_i > 0) ? r[:total_tokens].to_s : "0"
663
+ reason = r[:unhealthy_reason] || "-"
664
+ if no_color || !$stdout.tty?
665
+ [r[:provider], r[:status], (r[:available] ? "yes" : "no"), cb, rl, tokens, last_used, reason]
666
+ else
667
+ [
668
+ color.call(r[:provider], "1;97"),
669
+ color.call(r[:status], status_color.call(r[:status])),
670
+ color.call(r[:available] ? "yes" : "no", availability_color.call(r[:available] ? "yes" : "no")),
671
+ color.call(cb, circuit_color.call(cb)),
672
+ color.call(rl, rate_color.call(rl)),
673
+ color.call(tokens, 37),
674
+ color.call(last_used, 90),
675
+ ((reason == "-") ? reason : color.call(reason, 33))
676
+ ]
677
+ end
678
+ end
679
+ header = ["Provider", "Status", "Avail", "Circuit", "RateLimited", "Tokens", "LastUsed", "Reason"]
680
+ table = TTY::Table.new header, table_rows
681
+ display_message(table.render(:basic), type: :info)
682
+ rescue => e
683
+ display_message("Failed to display provider health: #{e.message}", type: :error)
684
+ end
685
+
686
+ def run_providers_info_command(args)
687
+ require_relative "harness/provider_info"
688
+
689
+ provider_name = args.shift
690
+ unless provider_name
691
+ display_message("Usage: aidp providers info <provider_name>", type: :info)
692
+ display_message("Example: aidp providers info claude", type: :info)
693
+ return
694
+ end
695
+
696
+ force_refresh = args.include?("--refresh")
697
+
698
+ display_message("Provider Information: #{provider_name}", type: :highlight)
699
+ display_message("=" * 60, type: :muted)
700
+
701
+ provider_info = Aidp::Harness::ProviderInfo.new(provider_name, Dir.pwd)
702
+ info = provider_info.get_info(force_refresh: force_refresh)
703
+
704
+ if info.nil?
705
+ display_message("No information available for provider: #{provider_name}", type: :error)
706
+ return
707
+ end
708
+
709
+ # Display basic info
710
+ display_message("Last Checked: #{info[:last_checked]}", type: :info)
711
+ display_message("CLI Available: #{info[:cli_available] ? "Yes" : "No"}", type: info[:cli_available] ? :success : :error)
712
+
713
+ # Display authentication
714
+ if info[:auth_method]
715
+ display_message("\nAuthentication Method: #{info[:auth_method]}", type: :info)
716
+ end
717
+
718
+ # Display MCP support
719
+ display_message("\nMCP Support: #{info[:mcp_support] ? "Yes" : "No"}", type: info[:mcp_support] ? :success : :info)
720
+
721
+ # Display MCP servers if available
722
+ if info[:mcp_servers]&.any?
723
+ display_message("\nMCP Servers: (#{info[:mcp_servers].size} configured)", type: :highlight)
724
+ info[:mcp_servers].each do |server|
725
+ status_symbol = server[:enabled] ? "✓" : "○"
726
+ display_message(" #{status_symbol} #{server[:name]} (#{server[:status]})", type: server[:enabled] ? :success : :muted)
727
+ display_message(" #{server[:description]}", type: :muted) if server[:description]
728
+ end
729
+ elsif info[:mcp_support]
730
+ display_message("\nMCP Servers: None configured", type: :muted)
731
+ end
732
+
733
+ # Display permission modes
734
+ if info[:permission_modes]&.any?
735
+ display_message("\nPermission Modes:", type: :highlight)
736
+ info[:permission_modes].each do |mode|
737
+ display_message(" - #{mode}", type: :info)
738
+ end
739
+ end
740
+
741
+ # Display capabilities
742
+ if info[:capabilities]&.any?
743
+ display_message("\nCapabilities:", type: :highlight)
744
+ info[:capabilities].each do |cap, value|
745
+ next unless value
746
+
747
+ display_message(" ✓ #{cap.to_s.split("_").map(&:capitalize).join(" ")}", type: :success)
748
+ end
749
+ end
750
+
751
+ # Display notable flags
752
+ if info[:flags]&.any?
753
+ display_message("\nNotable Flags: (#{info[:flags].size} total)", type: :highlight)
754
+ # Show first 10 flags
755
+ info[:flags].take(10).each do |name, flag_info|
756
+ display_message(" #{flag_info[:flag]}", type: :info)
757
+ display_message(" #{flag_info[:description][0..80]}...", type: :muted) if flag_info[:description]
758
+ end
759
+
760
+ if info[:flags].size > 10
761
+ display_message("\n ... and #{info[:flags].size - 10} more flags", type: :muted)
762
+ display_message(" Run '#{get_binary_name(provider_name)} --help' for full details", type: :muted)
763
+ end
764
+ end
765
+
766
+ display_message("\n" + "=" * 60, type: :muted)
767
+ display_message("Tip: Use --refresh to update this information", type: :muted)
768
+ end
769
+
770
+ def run_providers_refresh_command(args)
771
+ require_relative "harness/provider_info"
772
+ require "tty-spinner"
773
+
774
+ provider_name = args.shift
775
+ configuration = Aidp::Harness::Configuration.new(Dir.pwd)
776
+ providers_to_refresh = if provider_name
777
+ [provider_name]
778
+ else
779
+ configuration.configured_providers
780
+ end
781
+
782
+ display_message("Refreshing provider information...", type: :info)
783
+ display_message("", type: :info)
784
+
785
+ providers_to_refresh.each do |prov|
786
+ spinner = TTY::Spinner.new("[:spinner] #{prov}...", format: :dots)
787
+ spinner.auto_spin
788
+
789
+ provider_info = Aidp::Harness::ProviderInfo.new(prov, Dir.pwd)
790
+ info = provider_info.gather_info
791
+
792
+ if info[:cli_available]
793
+ spinner.success("(available)")
794
+ else
795
+ spinner.error("(unavailable)")
796
+ end
797
+ end
798
+
799
+ display_message("\n✓ Provider information refreshed", type: :success)
800
+ display_message("Use 'aidp providers info <name>' to view details", type: :muted)
801
+ end
802
+
803
+ def run_mcp_command(args)
804
+ require_relative "cli/mcp_dashboard"
805
+
806
+ subcommand = args.shift
807
+
808
+ dashboard = Aidp::CLI::McpDashboard.new(Dir.pwd)
809
+
810
+ case subcommand
811
+ when "dashboard", "list", nil
812
+ # Extract flags
813
+ no_color = args.include?("--no-color")
814
+ dashboard.display_dashboard(no_color: no_color)
815
+
816
+ when "check"
817
+ # Check eligibility for specific servers
818
+ required_servers = args
819
+ if required_servers.empty?
820
+ display_message("Usage: aidp mcp check <server1> [server2] ...", type: :info)
821
+ display_message("Example: aidp mcp check filesystem brave-search", type: :info)
822
+ return
823
+ end
824
+
825
+ dashboard.display_task_eligibility(required_servers)
826
+
827
+ else
828
+ display_message("Usage: aidp mcp <command>", type: :info)
829
+ display_message("", type: :info)
830
+ display_message("Commands:", type: :info)
831
+ display_message(" dashboard, list Show MCP servers across all providers (default)", type: :info)
832
+ display_message(" check <servers...> Check which providers have required MCP servers", type: :info)
833
+ display_message("", type: :info)
834
+ display_message("Examples:", type: :info)
835
+ display_message(" aidp mcp # Show dashboard", type: :info)
836
+ display_message(" aidp mcp dashboard --no-color # Show without colors", type: :info)
837
+ display_message(" aidp mcp check filesystem dash-api # Check provider eligibility", type: :info)
838
+ end
839
+ end
840
+
841
+ def get_binary_name(provider_name)
842
+ case provider_name
843
+ when "claude", "anthropic"
844
+ "claude"
845
+ when "cursor"
846
+ "cursor"
847
+ when "gemini"
848
+ "gemini"
849
+ when "codex"
850
+ "codex"
851
+ when "github_copilot"
852
+ "gh"
853
+ when "opencode"
854
+ "opencode"
855
+ else
856
+ provider_name
857
+ end
858
+ end
859
+
389
860
  def extract_mode_option(args)
390
861
  mode = nil
391
862
  args.each do |arg|
@@ -403,17 +874,20 @@ module Aidp
403
874
 
404
875
  def select_mode_interactive(tui)
405
876
  mode_options = [
877
+ "🤖 Guided Workflow (Copilot) - AI helps you choose the right workflow",
406
878
  "🔬 Analyze Mode - Analyze your codebase for insights and recommendations",
407
879
  "🏗️ Execute Mode - Build new features with guided development workflow"
408
880
  ]
409
881
  selected = tui.single_select("Welcome to AI Dev Pipeline! Choose your mode", mode_options, default: 1)
410
882
  # Announce mode explicitly in headless contexts (handled internally otherwise)
411
883
  if (defined?(RSpec) || ENV["RSPEC_RUNNING"]) && tui.respond_to?(:announce_mode)
412
- tui.announce_mode(:analyze) if selected == mode_options[0]
413
- tui.announce_mode(:execute) if selected == mode_options[1]
884
+ tui.announce_mode(:guided) if selected == mode_options[0]
885
+ tui.announce_mode(:analyze) if selected == mode_options[1]
886
+ tui.announce_mode(:execute) if selected == mode_options[2]
414
887
  end
415
- return :analyze if selected == mode_options[0]
416
- return :execute if selected == mode_options[1]
888
+ return :guided if selected == mode_options[0]
889
+ return :analyze if selected == mode_options[1]
890
+ return :execute if selected == mode_options[2]
417
891
  :analyze
418
892
  end
419
893