aidp 0.25.0 → 0.27.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -6
  3. data/lib/aidp/analyze/error_handler.rb +11 -0
  4. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  5. data/lib/aidp/cli/config_command.rb +71 -0
  6. data/lib/aidp/cli/enhanced_input.rb +2 -0
  7. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  8. data/lib/aidp/cli/harness_command.rb +102 -0
  9. data/lib/aidp/cli/jobs_command.rb +3 -3
  10. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  11. data/lib/aidp/cli/models_command.rb +662 -0
  12. data/lib/aidp/cli/providers_command.rb +223 -0
  13. data/lib/aidp/cli.rb +35 -456
  14. data/lib/aidp/daemon/runner.rb +2 -2
  15. data/lib/aidp/debug_mixin.rb +2 -9
  16. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  17. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  18. data/lib/aidp/execute/interactive_repl.rb +2 -1
  19. data/lib/aidp/execute/prompt_manager.rb +4 -4
  20. data/lib/aidp/execute/work_loop_runner.rb +253 -56
  21. data/lib/aidp/execute/workflow_selector.rb +2 -2
  22. data/lib/aidp/harness/config_loader.rb +20 -11
  23. data/lib/aidp/harness/config_manager.rb +5 -5
  24. data/lib/aidp/harness/config_schema.rb +30 -8
  25. data/lib/aidp/harness/configuration.rb +105 -4
  26. data/lib/aidp/harness/enhanced_runner.rb +24 -15
  27. data/lib/aidp/harness/error_handler.rb +26 -5
  28. data/lib/aidp/harness/filter_strategy.rb +45 -0
  29. data/lib/aidp/harness/generic_filter_strategy.rb +63 -0
  30. data/lib/aidp/harness/model_cache.rb +269 -0
  31. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  32. data/lib/aidp/harness/model_registry.rb +201 -0
  33. data/lib/aidp/harness/output_filter.rb +136 -0
  34. data/lib/aidp/harness/provider_manager.rb +18 -3
  35. data/lib/aidp/harness/rspec_filter_strategy.rb +82 -0
  36. data/lib/aidp/harness/runner.rb +5 -0
  37. data/lib/aidp/harness/test_runner.rb +165 -27
  38. data/lib/aidp/harness/thinking_depth_manager.rb +223 -7
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +4 -1
  40. data/lib/aidp/logger.rb +35 -5
  41. data/lib/aidp/providers/adapter.rb +2 -4
  42. data/lib/aidp/providers/anthropic.rb +141 -128
  43. data/lib/aidp/providers/base.rb +98 -2
  44. data/lib/aidp/providers/capability_registry.rb +0 -1
  45. data/lib/aidp/providers/codex.rb +49 -67
  46. data/lib/aidp/providers/cursor.rb +71 -59
  47. data/lib/aidp/providers/gemini.rb +44 -60
  48. data/lib/aidp/providers/github_copilot.rb +2 -66
  49. data/lib/aidp/providers/kilocode.rb +24 -80
  50. data/lib/aidp/providers/opencode.rb +24 -80
  51. data/lib/aidp/safe_directory.rb +10 -3
  52. data/lib/aidp/setup/wizard.rb +345 -8
  53. data/lib/aidp/storage/csv_storage.rb +9 -3
  54. data/lib/aidp/storage/file_manager.rb +8 -2
  55. data/lib/aidp/storage/json_storage.rb +9 -3
  56. data/lib/aidp/version.rb +1 -1
  57. data/lib/aidp/watch/build_processor.rb +40 -1
  58. data/lib/aidp/watch/change_request_processor.rb +659 -0
  59. data/lib/aidp/watch/plan_generator.rb +93 -14
  60. data/lib/aidp/watch/plan_processor.rb +71 -8
  61. data/lib/aidp/watch/repository_client.rb +85 -20
  62. data/lib/aidp/watch/review_processor.rb +3 -3
  63. data/lib/aidp/watch/runner.rb +37 -0
  64. data/lib/aidp/watch/state_store.rb +46 -1
  65. data/lib/aidp/workflows/guided_agent.rb +3 -3
  66. data/lib/aidp/workstream_executor.rb +5 -2
  67. data/lib/aidp.rb +4 -0
  68. data/templates/aidp-development.yml.example +2 -2
  69. data/templates/aidp-production.yml.example +3 -3
  70. data/templates/aidp.yml.example +53 -0
  71. metadata +14 -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
@@ -381,7 +258,7 @@ module Aidp
381
258
  # Determine if the invocation is a subcommand style call
382
259
  def subcommand?(args)
383
260
  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)
261
+ %w[status jobs kb harness providers checkpoint mcp issue config init watch ws work skill settings models].include?(args.first)
385
262
  end
386
263
 
387
264
  def run_subcommand(args)
@@ -403,6 +280,7 @@ module Aidp
403
280
  when "work" then run_work_command(args)
404
281
  when "skill" then run_skill_command(args)
405
282
  when "settings" then run_settings_command(args)
283
+ when "models" then run_models_command(args)
406
284
  else
407
285
  display_message("Unknown command: #{cmd}", type: :info)
408
286
  return 1
@@ -439,17 +317,21 @@ module Aidp
439
317
 
440
318
  def run_harness_command(args)
441
319
  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)
320
+
321
+ # Delegate to HarnessCommand
322
+ require_relative "cli/harness_command"
323
+
324
+ options = {}
325
+ if args.include?("--mode")
326
+ args.delete("--mode")
327
+ options[:mode] = args.shift
450
328
  else
451
- display_message("Usage: aidp harness <status|reset> [--mode MODE]", type: :info)
329
+ mode = extract_mode_option(args)
330
+ options[:mode] = mode if mode
452
331
  end
332
+
333
+ command = HarnessCommand.new(prompt: create_prompt)
334
+ command.run(args, subcommand: sub, options: options)
453
335
  end
454
336
 
455
337
  def run_execute_command(args, mode: :execute)
@@ -535,138 +417,10 @@ module Aidp
535
417
  end
536
418
 
537
419
  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
420
+ # Delegate to CheckpointCommand
421
+ require_relative "cli/checkpoint_command"
422
+ command = CheckpointCommand.new(prompt: create_prompt)
423
+ command.run(args)
670
424
  end
671
425
 
672
426
  def format_time_ago_simple(seconds)
@@ -685,11 +439,15 @@ module Aidp
685
439
  case subcommand
686
440
  when "info"
687
441
  args.shift # Remove 'info'
688
- run_providers_info_command(args)
442
+ require_relative "cli/providers_command"
443
+ command = ProvidersCommand.new(prompt: create_prompt)
444
+ command.run(args, subcommand: "info")
689
445
  return
690
446
  when "refresh"
691
447
  args.shift # Remove 'refresh'
692
- run_providers_refresh_command(args)
448
+ require_relative "cli/providers_command"
449
+ command = ProvidersCommand.new(prompt: create_prompt)
450
+ command.run(args, subcommand: "refresh")
693
451
  return
694
452
  end
695
453
 
@@ -774,164 +532,6 @@ module Aidp
774
532
  display_message("Failed to display provider health: #{e.message}", type: :error)
775
533
  end
776
534
 
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
535
  def run_mcp_command(args)
936
536
  require_relative "cli/mcp_dashboard"
937
537
 
@@ -1021,6 +621,12 @@ module Aidp
1021
621
  end
1022
622
  end
1023
623
 
624
+ def run_models_command(args)
625
+ require_relative "cli/models_command"
626
+ models_cmd = Aidp::CLI::ModelsCommand.new(prompt: create_prompt)
627
+ models_cmd.run(args)
628
+ end
629
+
1024
630
  def run_issue_command(args)
1025
631
  require_relative "cli/issue_importer"
1026
632
 
@@ -1073,33 +679,10 @@ module Aidp
1073
679
  end
1074
680
 
1075
681
  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
682
+ # Delegate to ConfigCommand
683
+ require_relative "cli/config_command"
684
+ command = ConfigCommand.new(prompt: create_prompt)
685
+ command.run(args)
1103
686
  end
1104
687
 
1105
688
  def run_devcontainer_command(args)
@@ -1856,10 +1439,6 @@ module Aidp
1856
1439
  end
1857
1440
  end
1858
1441
 
1859
- def display_config_usage
1860
- display_message("Usage: aidp config --interactive [--dry-run]", type: :info)
1861
- end
1862
-
1863
1442
  def run_settings_command(args)
1864
1443
  require_relative "auto_update"
1865
1444
  require "yaml"
@@ -10,11 +10,11 @@ module Aidp
10
10
  # Main daemon runner for background mode execution
11
11
  # Manages work loops, watch mode, and IPC communication
12
12
  class Runner
13
- def initialize(project_dir, config, options = {})
13
+ def initialize(project_dir, config, options = {}, process_manager: nil)
14
14
  @project_dir = project_dir
15
15
  @config = config
16
16
  @options = options
17
- @process_manager = ProcessManager.new(project_dir)
17
+ @process_manager = process_manager || ProcessManager.new(project_dir)
18
18
  @running = false
19
19
  @work_loop_runner = nil
20
20
  @watch_runner = nil
@@ -130,7 +130,7 @@ module Aidp
130
130
  end
131
131
 
132
132
  # Execute command with debug logging
133
- def debug_execute_command(cmd, args: [], input: nil, timeout: nil, streaming: false, **options)
133
+ def debug_execute_command(cmd, args: [], input: nil, timeout: nil, **options)
134
134
  require "tty-command"
135
135
 
136
136
  command_str = [cmd, *args].join(" ")
@@ -139,14 +139,7 @@ module Aidp
139
139
  debug_logger.info(component_name, "šŸš€ Starting command execution: #{command_str}")
140
140
 
141
141
  begin
142
- # Configure printer based on streaming mode
143
- if streaming
144
- # Use progress printer for real-time output
145
- cmd_obj = TTY::Command.new(printer: :progress)
146
- debug_log("šŸ“ŗ Streaming mode enabled - showing real-time output", level: :info)
147
- else
148
- cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
149
- end
142
+ cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
150
143
 
151
144
  # Prepare input
152
145
  input_data = nil
@@ -27,6 +27,7 @@ module Aidp
27
27
  @config = config
28
28
  @options = options
29
29
  @cancel_timeout = options[:cancel_timeout] || 5 # seconds to wait for graceful shutdown
30
+ @sync_runner_class = options[:sync_runner_class] || WorkLoopRunner
30
31
  @state = WorkLoopState.new
31
32
  @instruction_queue = InstructionQueue.new
32
33
  @work_thread = nil
@@ -133,7 +134,7 @@ module Aidp
133
134
  # Main async execution loop
134
135
  def run_async_loop
135
136
  # Create synchronous runner (runs in this thread)
136
- @sync_runner = WorkLoopRunner.new(
137
+ @sync_runner = @sync_runner_class.new(
137
138
  @project_dir,
138
139
  @provider_manager,
139
140
  @config,