aidp 0.26.0 → 0.28.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -0
  3. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  4. data/lib/aidp/cli/config_command.rb +71 -0
  5. data/lib/aidp/cli/enhanced_input.rb +2 -0
  6. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  7. data/lib/aidp/cli/harness_command.rb +102 -0
  8. data/lib/aidp/cli/jobs_command.rb +3 -3
  9. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  10. data/lib/aidp/cli/models_command.rb +661 -0
  11. data/lib/aidp/cli/providers_command.rb +223 -0
  12. data/lib/aidp/cli.rb +45 -464
  13. data/lib/aidp/config.rb +54 -0
  14. data/lib/aidp/daemon/runner.rb +2 -2
  15. data/lib/aidp/debug_mixin.rb +25 -10
  16. data/lib/aidp/execute/agent_signal_parser.rb +22 -0
  17. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  18. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  19. data/lib/aidp/execute/interactive_repl.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +4 -4
  21. data/lib/aidp/execute/repl_macros.rb +2 -2
  22. data/lib/aidp/execute/steps.rb +94 -1
  23. data/lib/aidp/execute/work_loop_runner.rb +238 -19
  24. data/lib/aidp/execute/workflow_selector.rb +4 -27
  25. data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
  26. data/lib/aidp/harness/ai_decision_engine.rb +35 -2
  27. data/lib/aidp/harness/config_manager.rb +5 -10
  28. data/lib/aidp/harness/config_schema.rb +8 -0
  29. data/lib/aidp/harness/configuration.rb +40 -2
  30. data/lib/aidp/harness/enhanced_runner.rb +25 -19
  31. data/lib/aidp/harness/error_handler.rb +23 -73
  32. data/lib/aidp/harness/model_cache.rb +269 -0
  33. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  34. data/lib/aidp/harness/model_registry.rb +201 -0
  35. data/lib/aidp/harness/provider_factory.rb +11 -2
  36. data/lib/aidp/harness/runner.rb +5 -0
  37. data/lib/aidp/harness/state_manager.rb +0 -7
  38. data/lib/aidp/harness/thinking_depth_manager.rb +202 -7
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
  40. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
  41. data/lib/aidp/harness/ui/progress_display.rb +6 -2
  42. data/lib/aidp/harness/user_interface.rb +0 -58
  43. data/lib/aidp/init/runner.rb +7 -2
  44. data/lib/aidp/message_display.rb +0 -46
  45. data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
  46. data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
  47. data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
  48. data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
  49. data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
  50. data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
  51. data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
  52. data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
  53. data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
  54. data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
  55. data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
  56. data/lib/aidp/planning/parsers/document_parser.rb +141 -0
  57. data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
  58. data/lib/aidp/provider_manager.rb +8 -32
  59. data/lib/aidp/providers/adapter.rb +2 -4
  60. data/lib/aidp/providers/aider.rb +264 -0
  61. data/lib/aidp/providers/anthropic.rb +206 -121
  62. data/lib/aidp/providers/base.rb +123 -3
  63. data/lib/aidp/providers/capability_registry.rb +0 -1
  64. data/lib/aidp/providers/codex.rb +75 -70
  65. data/lib/aidp/providers/cursor.rb +87 -59
  66. data/lib/aidp/providers/gemini.rb +57 -60
  67. data/lib/aidp/providers/github_copilot.rb +19 -66
  68. data/lib/aidp/providers/kilocode.rb +35 -80
  69. data/lib/aidp/providers/opencode.rb +35 -80
  70. data/lib/aidp/setup/wizard.rb +555 -8
  71. data/lib/aidp/version.rb +1 -1
  72. data/lib/aidp/watch/build_processor.rb +211 -30
  73. data/lib/aidp/watch/change_request_processor.rb +128 -14
  74. data/lib/aidp/watch/ci_fix_processor.rb +103 -37
  75. data/lib/aidp/watch/ci_log_extractor.rb +258 -0
  76. data/lib/aidp/watch/github_state_extractor.rb +177 -0
  77. data/lib/aidp/watch/implementation_verifier.rb +284 -0
  78. data/lib/aidp/watch/plan_generator.rb +95 -52
  79. data/lib/aidp/watch/plan_processor.rb +7 -6
  80. data/lib/aidp/watch/repository_client.rb +245 -17
  81. data/lib/aidp/watch/review_processor.rb +100 -19
  82. data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
  83. data/lib/aidp/watch/runner.rb +181 -29
  84. data/lib/aidp/watch/state_store.rb +22 -1
  85. data/lib/aidp/workflows/definitions.rb +147 -0
  86. data/lib/aidp/workflows/guided_agent.rb +3 -3
  87. data/lib/aidp/workstream_cleanup.rb +245 -0
  88. data/lib/aidp/worktree.rb +19 -0
  89. data/templates/aidp-development.yml.example +2 -2
  90. data/templates/aidp-production.yml.example +3 -3
  91. data/templates/aidp.yml.example +57 -0
  92. data/templates/implementation/generate_tdd_specs.md +213 -0
  93. data/templates/implementation/iterative_implementation.md +122 -0
  94. data/templates/planning/agile/analyze_feedback.md +183 -0
  95. data/templates/planning/agile/generate_iteration_plan.md +179 -0
  96. data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
  97. data/templates/planning/agile/generate_marketing_report.md +162 -0
  98. data/templates/planning/agile/generate_mvp_scope.md +127 -0
  99. data/templates/planning/agile/generate_user_test_plan.md +143 -0
  100. data/templates/planning/agile/ingest_feedback.md +174 -0
  101. data/templates/planning/assemble_project_plan.md +113 -0
  102. data/templates/planning/assign_personas.md +108 -0
  103. data/templates/planning/create_tasks.md +52 -6
  104. data/templates/planning/generate_gantt.md +86 -0
  105. data/templates/planning/generate_wbs.md +85 -0
  106. data/templates/planning/initialize_planning_mode.md +70 -0
  107. data/templates/skills/README.md +2 -2
  108. data/templates/skills/marketing_strategist/SKILL.md +279 -0
  109. data/templates/skills/product_manager/SKILL.md +177 -0
  110. data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
  111. data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
  112. data/templates/skills/ux_researcher/SKILL.md +222 -0
  113. metadata +47 -1
data/lib/aidp/cli.rb CHANGED
@@ -17,133 +17,10 @@ module Aidp
17
17
  include Aidp::MessageDisplay
18
18
  include Aidp::RescueLogging
19
19
 
20
- # Simple options holder for instance methods (used in specs)
21
- attr_accessor :options
22
-
23
20
  def initialize(prompt: TTY::Prompt.new)
24
- @options = {}
25
21
  @prompt = prompt
26
22
  end
27
23
 
28
- # Instance version of harness status (used by specs; non-interactive)
29
- def harness_status
30
- modes = %i[analyze execute]
31
- display_message("🔧 Harness Status", type: :highlight)
32
- modes.each do |mode|
33
- status = fetch_harness_status(mode)
34
- print_harness_mode_status(mode, status)
35
- end
36
- end
37
-
38
- # Instance version of harness reset (used by specs)
39
- def harness_reset
40
- # Use accessor so specs that stub #options work
41
- mode = (options[:mode] || "analyze").to_s
42
- unless %w[analyze execute].include?(mode)
43
- display_message("❌ Invalid mode. Use 'analyze' or 'execute'", type: :error)
44
- return
45
- end
46
-
47
- # Build a runner to access state manager; keep light for spec
48
- runner = Aidp::Harness::Runner.new(Dir.pwd, mode.to_sym, {})
49
- state_manager = runner.instance_variable_get(:@state_manager)
50
- state_manager.reset_all if state_manager.respond_to?(:reset_all)
51
- display_message("✅ Reset harness state for #{mode} mode", type: :success)
52
- end
53
-
54
- # Instance version of analyze command (used by specs)
55
- def analyze(project_dir, step = nil, options = {})
56
- # Simple implementation for spec compatibility
57
- # Different statuses based on whether a step is provided
58
- status = if options[:expect_error] == true
59
- "error"
60
- elsif step.nil?
61
- "success" # Initial call without step
62
- else
63
- "completed" # Subsequent calls with specific step
64
- end
65
-
66
- {
67
- status: status,
68
- provider: "cursor",
69
- message: step ? "Step executed successfully" : "Analysis completed",
70
- output: "Analysis results",
71
- next_step: step ? nil : "01_REPOSITORY_ANALYSIS"
72
- }
73
- end
74
-
75
- # Instance version of execute command (used by specs)
76
- def execute(project_dir, step = nil, options = {})
77
- # Simple implementation for spec compatibility
78
- # Some specs expect "success", others expect "completed" - check context
79
- status = (options[:expect_error] == true) ? "error" : "success"
80
- {
81
- status: status,
82
- provider: "cursor",
83
- message: "Execution completed",
84
- output: "Execution results",
85
- next_step: step ? nil : "00_PRD"
86
- }
87
- end
88
-
89
- private
90
-
91
- # Format durations (duplicated logic kept simple for spec expectations)
92
- def format_duration(seconds)
93
- return "0s" if seconds.nil? || seconds <= 0
94
- h = (seconds / 3600).to_i
95
- m = ((seconds % 3600) / 60).to_i
96
- s = (seconds % 60).to_i
97
- parts = []
98
- parts << "#{h}h" if h.positive?
99
- parts << "#{m}m" if m.positive?
100
- parts << "#{s}s" if s.positive? || parts.empty?
101
- parts.join(" ")
102
- end
103
-
104
- # Instance variant of display_harness_result (specs call via send)
105
- def display_harness_result(result)
106
- case result[:status]
107
- when "completed"
108
- display_message("\n✅ Harness completed successfully!", type: :success)
109
- display_message(" All steps finished automatically", type: :success)
110
- when "stopped"
111
- display_message("\n⏹️ Harness stopped by user", type: :info)
112
- display_message(" Execution terminated manually", type: :info)
113
- when "error"
114
- # Harness already outputs its own error message
115
- # Intentionally no output here to satisfy spec expecting empty string
116
- nil
117
- else
118
- display_message("\n🔄 Harness finished", type: :success)
119
- display_message(" Status: #{result[:status]}", type: :info)
120
- display_message(" Message: #{result[:message]}", type: :info) if result[:message]
121
- end
122
- end
123
-
124
- def fetch_harness_status(mode)
125
- runner = Aidp::Harness::Runner.new(Dir.pwd, mode, {})
126
- if runner.respond_to?(:detailed_status)
127
- runner.detailed_status
128
- else
129
- {harness: {state: "unknown"}}
130
- end
131
- rescue => e
132
- log_rescue(e, component: "cli", action: "fetch_harness_status", fallback: {harness: {state: "error"}}, mode: mode)
133
- {harness: {state: "error", error: e.message}}
134
- end
135
-
136
- def print_harness_mode_status(mode, status)
137
- harness = status[:harness] || {}
138
- display_message("\n📋 #{mode.to_s.capitalize} Mode:", type: :info)
139
- display_message(" State: #{harness[:state]}", type: :info)
140
- if harness[:progress]
141
- prog = harness[:progress]
142
- display_message(" Progress: #{prog[:completed_steps]}/#{prog[:total_steps]}", type: :success)
143
- display_message(" Current Step: #{harness[:current_step]}", type: :info) if harness[:current_step]
144
- end
145
- end
146
-
147
24
  class << self
148
25
  extend Aidp::MessageDisplay::ClassMethods
149
26
  extend Aidp::RescueLogging
@@ -208,9 +85,6 @@ module Aidp
208
85
  tui = Aidp::Harness::UI::EnhancedTUI.new
209
86
  workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
210
87
 
211
- # Start TUI display loop
212
- tui.start_display_loop
213
-
214
88
  begin
215
89
  # Copilot is now the default mode - no menu selection
216
90
  # The guided workflow selector will internally choose appropriate mode
@@ -243,7 +117,7 @@ module Aidp
243
117
  display_message("\n❌ Error: #{e.message}", type: :error)
244
118
  1
245
119
  ensure
246
- tui.stop_display_loop
120
+ tui.restore_screen
247
121
  end
248
122
  end
249
123
 
@@ -314,6 +188,7 @@ module Aidp
314
188
  opts.separator " pause-all - Pause all active workstreams"
315
189
  opts.separator " resume-all - Resume all paused workstreams"
316
190
  opts.separator " stop-all - Stop all active workstreams"
191
+ opts.separator " cleanup - Interactive cleanup of workstreams"
317
192
  opts.separator " work Execute workflow in workstream context"
318
193
  opts.separator " --workstream <slug> - Required: workstream to run in"
319
194
  opts.separator " --mode <mode> - analyze or execute (default: execute)"
@@ -381,7 +256,7 @@ module Aidp
381
256
  # Determine if the invocation is a subcommand style call
382
257
  def subcommand?(args)
383
258
  return false if args.nil? || args.empty?
384
- %w[status jobs kb harness providers checkpoint mcp issue config init watch ws work skill settings].include?(args.first)
259
+ %w[status jobs kb harness providers checkpoint mcp issue config init watch ws work skill settings models].include?(args.first)
385
260
  end
386
261
 
387
262
  def run_subcommand(args)
@@ -403,6 +278,7 @@ module Aidp
403
278
  when "work" then run_work_command(args)
404
279
  when "skill" then run_skill_command(args)
405
280
  when "settings" then run_settings_command(args)
281
+ when "models" then run_models_command(args)
406
282
  else
407
283
  display_message("Unknown command: #{cmd}", type: :info)
408
284
  return 1
@@ -439,17 +315,21 @@ module Aidp
439
315
 
440
316
  def run_harness_command(args)
441
317
  sub = args.shift
442
- case sub
443
- when "status"
444
- display_message("Harness Status", type: :info)
445
- display_message("Mode: (unknown)", type: :info)
446
- display_message("State: idle", type: :info)
447
- when "reset"
448
- mode = extract_mode_option(args)
449
- display_message("Harness state reset for mode: #{mode || "default"}", type: :info)
318
+
319
+ # Delegate to HarnessCommand
320
+ require_relative "cli/harness_command"
321
+
322
+ options = {}
323
+ if args.include?("--mode")
324
+ args.delete("--mode")
325
+ options[:mode] = args.shift
450
326
  else
451
- display_message("Usage: aidp harness <status|reset> [--mode MODE]", type: :info)
327
+ mode = extract_mode_option(args)
328
+ options[:mode] = mode if mode
452
329
  end
330
+
331
+ command = HarnessCommand.new(prompt: create_prompt)
332
+ command.run(args, subcommand: sub, options: options)
453
333
  end
454
334
 
455
335
  def run_execute_command(args, mode: :execute)
@@ -535,138 +415,10 @@ module Aidp
535
415
  end
536
416
 
537
417
  def run_checkpoint_command(args)
538
- require_relative "execute/checkpoint"
539
- require_relative "execute/checkpoint_display"
540
-
541
- sub = args.shift || "summary"
542
- checkpoint = Aidp::Execute::Checkpoint.new(Dir.pwd)
543
- display = Aidp::Execute::CheckpointDisplay.new
544
-
545
- case sub
546
- when "show"
547
- latest = checkpoint.latest_checkpoint
548
- if latest
549
- display.display_checkpoint(latest, show_details: true)
550
- else
551
- display_message("No checkpoint data found.", type: :info)
552
- end
553
-
554
- when "summary"
555
- watch = args.include?("--watch")
556
- interval = extract_interval_option(args) || 5
557
-
558
- if watch
559
- watch_checkpoint_summary(checkpoint, display, interval)
560
- else
561
- summary = checkpoint.progress_summary
562
- if summary
563
- display.display_progress_summary(summary)
564
- else
565
- display_message("No checkpoint data found.", type: :info)
566
- end
567
- end
568
-
569
- when "history"
570
- limit = args.shift || "10"
571
- history = checkpoint.checkpoint_history(limit: limit.to_i)
572
- if history.any?
573
- display.display_checkpoint_history(history, limit: limit.to_i)
574
- else
575
- display_message("No checkpoint history found.", type: :info)
576
- end
577
-
578
- when "metrics"
579
- latest = checkpoint.latest_checkpoint
580
- unless latest
581
- display_message("No checkpoint data found.", type: :info)
582
- return
583
- end
584
-
585
- display_message("", type: :info)
586
- display_message("📊 Detailed Metrics", type: :info)
587
- display_message("=" * 60, type: :muted)
588
-
589
- metrics = latest[:metrics]
590
- display_message("Lines of Code: #{metrics[:lines_of_code]}", type: :info)
591
- display_message("File Count: #{metrics[:file_count]}", type: :info)
592
- display_message("Test Coverage: #{metrics[:test_coverage]}%", type: :info)
593
- display_message("Code Quality: #{metrics[:code_quality]}%", type: :info)
594
- display_message("PRD Task Progress: #{metrics[:prd_task_progress]}%", type: :info)
595
-
596
- if metrics[:tests_passing]
597
- status = metrics[:tests_passing] ? "✓ Passing" : "✗ Failing"
598
- display_message("Tests: #{status}", type: :info)
599
- end
600
-
601
- if metrics[:linters_passing]
602
- status = metrics[:linters_passing] ? "✓ Passing" : "✗ Failing"
603
- display_message("Linters: #{status}", type: :info)
604
- end
605
-
606
- display_message("=" * 60, type: :muted)
607
- display_message("", type: :info)
608
-
609
- when "clear"
610
- force = args.include?("--force")
611
- unless force
612
- prompt = create_prompt
613
- confirm = prompt.yes?("Are you sure you want to clear all checkpoint data?")
614
- return unless confirm
615
- end
616
-
617
- checkpoint.clear
618
- display_message("✓ Checkpoint data cleared.", type: :success)
619
-
620
- else
621
- display_message("Usage: aidp checkpoint <show|summary|history|metrics|clear>", type: :info)
622
- display_message(" show - Show the latest checkpoint data", type: :info)
623
- display_message(" summary [--watch] - Show progress summary with trends", type: :info)
624
- display_message(" history [N] - Show last N checkpoints", type: :info)
625
- display_message(" metrics - Show detailed metrics", type: :info)
626
- display_message(" clear [--force] - Clear all checkpoint data", type: :info)
627
- end
628
- end
629
-
630
- def watch_checkpoint_summary(checkpoint, display, interval)
631
- display_message("Watching checkpoint summary (refresh: #{interval}s, Ctrl+C to exit)...", type: :info)
632
- display_message("", type: :info)
633
-
634
- begin
635
- loop do
636
- # Clear screen
637
- print "\e[2J\e[H"
638
-
639
- summary = checkpoint.progress_summary
640
- if summary
641
- display.display_progress_summary(summary)
642
-
643
- # Show last update time
644
- if summary[:current] && summary[:current][:timestamp]
645
- last_update = Time.parse(summary[:current][:timestamp])
646
- age = Time.now - last_update
647
- display_message("", type: :info)
648
- display_message("Last update: #{format_time_ago_simple(age)} | Refreshing in #{interval}s...", type: :muted)
649
- end
650
- else
651
- display_message("No checkpoint data found. Waiting for data...", type: :info)
652
- end
653
-
654
- sleep interval
655
- end
656
- rescue Interrupt
657
- display_message("\nStopped watching checkpoint summary", type: :info)
658
- end
659
- end
660
-
661
- def extract_interval_option(args)
662
- args.each_with_index do |arg, i|
663
- if arg == "--interval" && args[i + 1]
664
- return args[i + 1].to_i
665
- elsif arg.start_with?("--interval=")
666
- return arg.split("=")[1].to_i
667
- end
668
- end
669
- nil
418
+ # Delegate to CheckpointCommand
419
+ require_relative "cli/checkpoint_command"
420
+ command = CheckpointCommand.new(prompt: create_prompt)
421
+ command.run(args)
670
422
  end
671
423
 
672
424
  def format_time_ago_simple(seconds)
@@ -685,11 +437,15 @@ module Aidp
685
437
  case subcommand
686
438
  when "info"
687
439
  args.shift # Remove 'info'
688
- run_providers_info_command(args)
440
+ require_relative "cli/providers_command"
441
+ command = ProvidersCommand.new(prompt: create_prompt)
442
+ command.run(args, subcommand: "info")
689
443
  return
690
444
  when "refresh"
691
445
  args.shift # Remove 'refresh'
692
- run_providers_refresh_command(args)
446
+ require_relative "cli/providers_command"
447
+ command = ProvidersCommand.new(prompt: create_prompt)
448
+ command.run(args, subcommand: "refresh")
693
449
  return
694
450
  end
695
451
 
@@ -774,164 +530,6 @@ module Aidp
774
530
  display_message("Failed to display provider health: #{e.message}", type: :error)
775
531
  end
776
532
 
777
- def run_providers_info_command(args)
778
- require_relative "harness/provider_info"
779
-
780
- provider_name = args.shift
781
-
782
- # If no provider specified, show models catalog table
783
- unless provider_name
784
- run_providers_models_catalog
785
- return
786
- end
787
-
788
- force_refresh = args.include?("--refresh")
789
-
790
- display_message("Provider Information: #{provider_name}", type: :highlight)
791
- display_message("=" * 60, type: :muted)
792
-
793
- provider_info = Aidp::Harness::ProviderInfo.new(provider_name, Dir.pwd)
794
- info = provider_info.info(force_refresh: force_refresh)
795
-
796
- if info.nil?
797
- display_message("No information available for provider: #{provider_name}", type: :error)
798
- return
799
- end
800
-
801
- # Display basic info
802
- display_message("Last Checked: #{info[:last_checked]}", type: :info)
803
- display_message("CLI Available: #{info[:cli_available] ? "Yes" : "No"}", type: info[:cli_available] ? :success : :error)
804
-
805
- # Display authentication
806
- if info[:auth_method]
807
- display_message("\nAuthentication Method: #{info[:auth_method]}", type: :info)
808
- end
809
-
810
- # Display MCP support
811
- display_message("\nMCP Support: #{info[:mcp_support] ? "Yes" : "No"}", type: info[:mcp_support] ? :success : :info)
812
-
813
- # Display MCP servers if available
814
- if info[:mcp_servers]&.any?
815
- display_message("\nMCP Servers: (#{info[:mcp_servers].size} configured)", type: :highlight)
816
- info[:mcp_servers].each do |server|
817
- status_symbol = server[:enabled] ? "✓" : "○"
818
- display_message(" #{status_symbol} #{server[:name]} (#{server[:status]})", type: server[:enabled] ? :success : :muted)
819
- display_message(" #{server[:description]}", type: :muted) if server[:description]
820
- end
821
- elsif info[:mcp_support]
822
- display_message("\nMCP Servers: None configured", type: :muted)
823
- end
824
-
825
- # Display permission modes
826
- if info[:permission_modes]&.any?
827
- display_message("\nPermission Modes:", type: :highlight)
828
- info[:permission_modes].each do |mode|
829
- display_message(" - #{mode}", type: :info)
830
- end
831
- end
832
-
833
- # Display capabilities
834
- if info[:capabilities]&.any?
835
- display_message("\nCapabilities:", type: :highlight)
836
- info[:capabilities].each do |cap, value|
837
- next unless value
838
-
839
- display_message(" ✓ #{cap.to_s.split("_").map(&:capitalize).join(" ")}", type: :success)
840
- end
841
- end
842
-
843
- # Display notable flags
844
- if info[:flags]&.any?
845
- display_message("\nNotable Flags: (#{info[:flags].size} total)", type: :highlight)
846
- # Show first 10 flags
847
- info[:flags].take(10).each do |name, flag_info|
848
- display_message(" #{flag_info[:flag]}", type: :info)
849
- display_message(" #{flag_info[:description][0..80]}...", type: :muted) if flag_info[:description]
850
- end
851
-
852
- if info[:flags].size > 10
853
- display_message("\n ... and #{info[:flags].size - 10} more flags", type: :muted)
854
- display_message(" Run '#{get_binary_name(provider_name)} --help' for full details", type: :muted)
855
- end
856
- end
857
-
858
- display_message("\n" + "=" * 60, type: :muted)
859
- display_message("Tip: Use --refresh to update this information", type: :muted)
860
- end
861
-
862
- def run_providers_models_catalog
863
- require_relative "harness/capability_registry"
864
- require "tty-table"
865
-
866
- display_message("Models Catalog - Thinking Depth Tiers", type: :highlight)
867
- display_message("=" * 80, type: :muted)
868
-
869
- registry = Aidp::Harness::CapabilityRegistry.new
870
- unless registry.load_catalog
871
- display_message("No models catalog found. Create .aidp/models_catalog.yml first.", type: :error)
872
- return
873
- end
874
-
875
- rows = []
876
- registry.provider_names.sort.each do |provider|
877
- models = registry.models_for_provider(provider)
878
- models.each do |model_name, model_data|
879
- tier = model_data["tier"] || "-"
880
- context = model_data["context_window"] ? "#{model_data["context_window"] / 1000}k" : "-"
881
- tools = model_data["supports_tools"] ? "yes" : "no"
882
- cost_input = model_data["cost_per_mtok_input"]
883
- cost = cost_input ? "$#{cost_input}/MTok" : "-"
884
-
885
- rows << [provider, model_name, tier, context, tools, cost]
886
- end
887
- end
888
-
889
- if rows.empty?
890
- display_message("No models found in catalog", type: :info)
891
- return
892
- end
893
-
894
- header = ["Provider", "Model", "Tier", "Context", "Tools", "Cost"]
895
- table = TTY::Table.new(header, rows)
896
- display_message(table.render(:basic), type: :info)
897
-
898
- display_message("\n" + "=" * 80, type: :muted)
899
- display_message("Use '/thinking show' in REPL to see current tier configuration", type: :muted)
900
- end
901
-
902
- def run_providers_refresh_command(args)
903
- require_relative "harness/provider_info"
904
- require "tty-spinner"
905
-
906
- provider_name = args.shift
907
- config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
908
- providers_to_refresh = if provider_name
909
- [provider_name]
910
- else
911
- config_manager.provider_names
912
- end
913
-
914
- display_message("Refreshing provider information...", type: :info)
915
- display_message("", type: :info)
916
-
917
- providers_to_refresh.each do |prov|
918
- spinner = TTY::Spinner.new("[:spinner] #{prov}...", format: :dots)
919
- spinner.auto_spin
920
-
921
- provider_info = Aidp::Harness::ProviderInfo.new(prov, Dir.pwd)
922
- info = provider_info.gather_info
923
-
924
- if info[:cli_available]
925
- spinner.success("(available)")
926
- else
927
- spinner.error("(unavailable)")
928
- end
929
- end
930
-
931
- display_message("\n✓ Provider information refreshed", type: :success)
932
- display_message("Use 'aidp providers info <name>' to view details", type: :muted)
933
- end
934
-
935
533
  def run_mcp_command(args)
936
534
  require_relative "cli/mcp_dashboard"
937
535
 
@@ -1021,6 +619,12 @@ module Aidp
1021
619
  end
1022
620
  end
1023
621
 
622
+ def run_models_command(args)
623
+ require_relative "cli/models_command"
624
+ models_cmd = Aidp::CLI::ModelsCommand.new(prompt: create_prompt)
625
+ models_cmd.run(args)
626
+ end
627
+
1024
628
  def run_issue_command(args)
1025
629
  require_relative "cli/issue_importer"
1026
630
 
@@ -1073,33 +677,10 @@ module Aidp
1073
677
  end
1074
678
 
1075
679
  def run_config_command(args)
1076
- interactive = false
1077
- dry_run = false
1078
-
1079
- until args.empty?
1080
- token = args.shift
1081
- case token
1082
- when "--interactive"
1083
- interactive = true
1084
- when "--dry-run"
1085
- dry_run = true
1086
- when "-h", "--help"
1087
- display_config_usage
1088
- return
1089
- else
1090
- display_message("Unknown option: #{token}", type: :error)
1091
- display_config_usage
1092
- return
1093
- end
1094
- end
1095
-
1096
- unless interactive
1097
- display_config_usage
1098
- return
1099
- end
1100
-
1101
- wizard = Aidp::Setup::Wizard.new(Dir.pwd, prompt: create_prompt, dry_run: dry_run)
1102
- wizard.run
680
+ # Delegate to ConfigCommand
681
+ require_relative "cli/config_command"
682
+ command = ConfigCommand.new(prompt: create_prompt)
683
+ command.run(args)
1103
684
  end
1104
685
 
1105
686
  def run_devcontainer_command(args)
@@ -1720,6 +1301,12 @@ module Aidp
1720
1301
  end
1721
1302
  display_message("⏹️ Stopped #{stopped_count} workstream(s)", type: :success)
1722
1303
 
1304
+ when "cleanup"
1305
+ # Interactive cleanup of workstreams
1306
+ require_relative "workstream_cleanup"
1307
+ cleanup = Aidp::WorkstreamCleanup.new(project_dir: Dir.pwd, prompt: create_prompt)
1308
+ cleanup.run
1309
+
1723
1310
  else
1724
1311
  display_message("Usage: aidp ws <command>", type: :info)
1725
1312
  display_message("", type: :info)
@@ -1734,6 +1321,7 @@ module Aidp
1734
1321
  display_message(" pause <slug> Pause workstream execution", type: :info)
1735
1322
  display_message(" resume <slug> Resume paused workstream", type: :info)
1736
1323
  display_message(" complete <slug> Mark workstream as completed", type: :info)
1324
+ display_message(" cleanup Interactive cleanup of workstreams", type: :info)
1737
1325
  display_message("", type: :info)
1738
1326
  display_message("Options:", type: :info)
1739
1327
  display_message(" --base-branch <branch> Branch to create from (for 'new')", type: :info)
@@ -1828,9 +1416,6 @@ module Aidp
1828
1416
  tui = Aidp::Harness::UI::EnhancedTUI.new
1829
1417
  workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
1830
1418
 
1831
- # Start TUI display loop
1832
- tui.start_display_loop
1833
-
1834
1419
  begin
1835
1420
  # Get workflow configuration
1836
1421
  workflow_config = workflow_selector.select_workflow(harness_mode: false, mode: mode)
@@ -1851,15 +1436,11 @@ module Aidp
1851
1436
  rescue Interrupt
1852
1437
  display_message("\n\n⏹️ Interrupted by user", type: :warning)
1853
1438
  ensure
1854
- tui.stop_display_loop
1439
+ tui.restore_screen
1855
1440
  end
1856
1441
  end
1857
1442
  end
1858
1443
 
1859
- def display_config_usage
1860
- display_message("Usage: aidp config --interactive [--dry-run]", type: :info)
1861
- end
1862
-
1863
1444
  def run_settings_command(args)
1864
1445
  require_relative "auto_update"
1865
1446
  require "yaml"