aidp 0.11.0 → 0.12.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ffba477d4ca2e297b4fe81d7ecb32ccf37d161df6bcd2dceaa91e94ccbc8ea8f
4
- data.tar.gz: 851fdc47c7a12bd8f6f8304badf60ffdb6855787d0982645d80a309d9caee40b
3
+ metadata.gz: e672ad3f8f54f1b3ad0cc4a804a63d1fa048709d47112497d5a4871d046dfb8b
4
+ data.tar.gz: 665d0b12e5dff272a0da11fd20d80299eaa4657ab7a87fe4e8b0f80e039e437d
5
5
  SHA512:
6
- metadata.gz: ef68650e5a8c55d8b219a00a42ba7fbff2c87bdd5cacf5f450c8bf406c481d6d1e4c00cd7edabbf41b07928bb28c2b5246c54392e67f52ee8381bac1668d9463
7
- data.tar.gz: d2ae0a1bc041b0f8c0cdf1ccb3c328fc7bb1c4ba3cbb0599c983f4937056a8ad1f47e0bcb475a96dd8d4d936cfc6d7452a0333121ed36e29a14708b8e4bd15ec
6
+ metadata.gz: 384734aed97d697dce2c06ac6bf6f52b161b6436081ff4feb3b8e99e8038bef2b62a9aa3613b21dc92705569741598b98dcbf72805868f6cbdf82a1a19c3c6dd
7
+ data.tar.gz: 3bd5a990c925f10bb1ad863097b1f0a1e84b682e2489b0793242c37cc4034eb6ac8777559cecb6acaef915391dfeb60337074267e52a1cc4c35aee87c793d672
@@ -4,6 +4,7 @@
4
4
  require "yaml"
5
5
  require "tty-prompt"
6
6
  require_relative "../harness/provider_factory"
7
+ require_relative "../config/paths"
7
8
 
8
9
  module Aidp
9
10
  class CLI
@@ -96,13 +97,14 @@ module Aidp
96
97
  display_message("Template not found: #{filename}", type: :error)
97
98
  return nil
98
99
  end
99
- dest = File.join(@project_dir, "aidp.yml")
100
+ dest = Aidp::ConfigPaths.config_file(@project_dir)
101
+ Aidp::ConfigPaths.ensure_config_dir(@project_dir)
100
102
  File.write(dest, File.read(src))
101
103
  dest
102
104
  end
103
105
 
104
106
  def write_minimal_config(project_dir)
105
- dest = File.join(project_dir, ".aidp", "aidp.yml")
107
+ dest = Aidp::ConfigPaths.config_file(project_dir)
106
108
  return dest if File.exist?(dest)
107
109
  data = {
108
110
  "harness" => {
@@ -118,18 +120,18 @@ module Aidp
118
120
  }
119
121
  }
120
122
  }
121
- FileUtils.mkdir_p(File.dirname(dest))
123
+ Aidp::ConfigPaths.ensure_config_dir(project_dir)
122
124
  File.write(dest, YAML.dump(data))
123
125
  dest
124
126
  end
125
127
 
126
128
  def write_example_config(project_dir)
127
129
  Aidp::Config.create_example_config(project_dir)
128
- File.join(project_dir, "aidp.yml")
130
+ Aidp::ConfigPaths.config_file(project_dir)
129
131
  end
130
132
 
131
133
  def run_custom
132
- dest = File.join(@project_dir, "aidp.yml")
134
+ dest = Aidp::ConfigPaths.config_file(@project_dir)
133
135
  return dest if File.exist?(dest)
134
136
 
135
137
  @prompt.say("Interactive custom configuration: press Enter to accept defaults shown in [brackets].")
@@ -169,12 +171,13 @@ module Aidp
169
171
  },
170
172
  "providers" => provider_section
171
173
  }
174
+ Aidp::ConfigPaths.ensure_config_dir(@project_dir)
172
175
  File.write(dest, YAML.dump(data))
173
176
  dest
174
177
  end
175
178
 
176
179
  def run_custom_with_defaults(existing_config)
177
- dest = File.join(@project_dir, "aidp.yml")
180
+ dest = Aidp::ConfigPaths.config_file(@project_dir)
178
181
 
179
182
  # Extract current values from existing config
180
183
  harness_config = existing_config[:harness] || existing_config["harness"] || {}
@@ -240,12 +243,13 @@ module Aidp
240
243
  "providers" => provider_section
241
244
  }
242
245
 
246
+ Aidp::ConfigPaths.ensure_config_dir(@project_dir)
243
247
  File.write(dest, YAML.dump(data))
244
248
  dest
245
249
  end
246
250
 
247
251
  def load_existing_config
248
- config_file = File.join(@project_dir, "aidp.yml")
252
+ config_file = Aidp::ConfigPaths.config_file(@project_dir)
249
253
  return nil unless File.exist?(config_file)
250
254
 
251
255
  begin
@@ -0,0 +1,205 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-table"
4
+ require_relative "../harness/provider_info"
5
+ require_relative "../harness/configuration"
6
+
7
+ module Aidp
8
+ class CLI
9
+ # Dashboard for viewing MCP servers across all providers
10
+ class McpDashboard
11
+ include Aidp::MessageDisplay
12
+
13
+ def initialize(root_dir = nil)
14
+ @root_dir = root_dir || Dir.pwd
15
+ @configuration = Aidp::Harness::Configuration.new(@root_dir)
16
+ end
17
+
18
+ # Display MCP dashboard showing all servers across all providers
19
+ def display_dashboard(options = {})
20
+ no_color = options[:no_color] || false
21
+
22
+ # Gather MCP server information from all providers
23
+ server_matrix = build_server_matrix
24
+
25
+ # Display summary
26
+ display_message("MCP Server Dashboard", type: :highlight)
27
+ display_message("=" * 80, type: :muted)
28
+ display_message("", type: :info)
29
+
30
+ # Check if there are any MCP-capable providers
31
+ if server_matrix[:providers].empty?
32
+ display_message("No providers with MCP support configured.", type: :info)
33
+ display_message("MCP is supported by providers like: claude", type: :muted)
34
+ display_message("\n" + "=" * 80, type: :muted)
35
+ return
36
+ end
37
+
38
+ # Check if there are any MCP servers configured
39
+ if server_matrix[:servers].empty?
40
+ display_message("No MCP servers configured across any providers.", type: :info)
41
+ display_message("Add MCP servers with: claude mcp add <name> -- <command>", type: :muted)
42
+ display_message("\n" + "=" * 80, type: :muted)
43
+ return
44
+ end
45
+
46
+ # Display the main table
47
+ display_server_table(server_matrix, no_color)
48
+
49
+ # Display eligibility warnings
50
+ display_eligibility_warnings(server_matrix)
51
+
52
+ display_message("\n" + "=" * 80, type: :muted)
53
+ end
54
+
55
+ # Get MCP server availability for a specific task requirement
56
+ def check_task_eligibility(required_servers)
57
+ server_matrix = build_server_matrix
58
+ eligible_providers = []
59
+
60
+ @configuration.configured_providers.each do |provider|
61
+ provider_servers = server_matrix[:provider_servers][provider] || []
62
+ enabled_servers = provider_servers.select { |s| s[:enabled] }.map { |s| s[:name] }
63
+
64
+ # Check if provider has all required servers
65
+ if required_servers.all? { |req| enabled_servers.include?(req) }
66
+ eligible_providers << provider
67
+ end
68
+ end
69
+
70
+ {
71
+ required_servers: required_servers,
72
+ eligible_providers: eligible_providers,
73
+ total_providers: @configuration.configured_providers.size
74
+ }
75
+ end
76
+
77
+ # Display eligibility check for specific servers
78
+ def display_task_eligibility(required_servers)
79
+ result = check_task_eligibility(required_servers)
80
+
81
+ display_message("\nTask Eligibility Check", type: :highlight)
82
+ display_message("Required MCP Servers: #{required_servers.join(", ")}", type: :info)
83
+ display_message("", type: :info)
84
+
85
+ if result[:eligible_providers].any?
86
+ display_message("✓ Eligible Providers (#{result[:eligible_providers].size}/#{result[:total_providers]}):", type: :success)
87
+ result[:eligible_providers].each do |provider|
88
+ display_message(" • #{provider}", type: :success)
89
+ end
90
+ else
91
+ display_message("✗ No providers have all required MCP servers", type: :error)
92
+ display_message(" Consider configuring MCP servers for at least one provider", type: :warning)
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def build_server_matrix
99
+ providers = @configuration.configured_providers
100
+ all_servers = {} # server_name => {providers: {provider_name => server_info}}
101
+ provider_servers = {} # provider_name => [server_info]
102
+
103
+ providers.each do |provider|
104
+ provider_info = Aidp::Harness::ProviderInfo.new(provider, @root_dir)
105
+ info = provider_info.get_info
106
+
107
+ next unless info[:mcp_support]
108
+
109
+ servers = info[:mcp_servers] || []
110
+ provider_servers[provider] = servers
111
+
112
+ servers.each do |server|
113
+ server_name = server[:name]
114
+ all_servers[server_name] ||= {providers: {}}
115
+ all_servers[server_name][:providers][provider] = server
116
+ end
117
+ end
118
+
119
+ {
120
+ servers: all_servers,
121
+ provider_servers: provider_servers,
122
+ providers: providers.select { |p| provider_servers.key?(p) }
123
+ }
124
+ end
125
+
126
+ def display_server_table(matrix, no_color)
127
+ # Check if we have any providers with MCP support
128
+ if matrix[:providers].empty?
129
+ display_message("No providers with MCP support configured.", type: :info)
130
+ return
131
+ end
132
+
133
+ # Build table rows
134
+ headers = ["MCP Server"] + matrix[:providers].map { |p| normalize_provider_name(p) }
135
+ rows = []
136
+
137
+ matrix[:servers].keys.sort.each do |server_name|
138
+ row = [server_name]
139
+
140
+ matrix[:providers].each do |provider|
141
+ server = matrix[:servers][server_name][:providers][provider]
142
+ cell = if server
143
+ format_server_status(server, no_color)
144
+ else
145
+ (no_color || !$stdout.tty?) ? "-" : "\e[90m-\e[0m"
146
+ end
147
+ row << cell
148
+ end
149
+
150
+ rows << row
151
+ end
152
+
153
+ # Create and display table
154
+ table = TTY::Table.new(headers, rows)
155
+ display_message(table.render(:basic), type: :info)
156
+ display_message("", type: :info)
157
+
158
+ # Legend
159
+ display_legend(no_color)
160
+ end
161
+
162
+ def format_server_status(server, no_color)
163
+ if no_color || !$stdout.tty?
164
+ server[:enabled] ? "✓" : "✗"
165
+ elsif server[:enabled]
166
+ "\e[32m✓\e[0m" # Green checkmark
167
+ else
168
+ "\e[31m✗\e[0m" # Red X
169
+ end
170
+ end
171
+
172
+ def display_legend(no_color)
173
+ if no_color || !$stdout.tty?
174
+ display_message("Legend: ✓ = Enabled ✗ = Error/Disabled - = Not configured", type: :muted)
175
+ else
176
+ display_message("Legend: \e[32m✓\e[0m = Enabled \e[31m✗\e[0m = Error/Disabled \e[90m-\e[0m = Not configured", type: :muted)
177
+ end
178
+ end
179
+
180
+ def display_eligibility_warnings(matrix)
181
+ # Find servers that are only configured on some providers
182
+ partially_configured = matrix[:servers].select do |_name, info|
183
+ configured_count = info[:providers].size
184
+ configured_count > 0 && configured_count < matrix[:providers].size
185
+ end
186
+
187
+ return if partially_configured.empty?
188
+
189
+ display_message("\n⚠ Eligibility Warnings:", type: :warning)
190
+ partially_configured.each do |server_name, info|
191
+ missing_providers = matrix[:providers] - info[:providers].keys
192
+ if missing_providers.any?
193
+ display_message(" • '#{server_name}' not configured on: #{missing_providers.join(", ")}", type: :warning)
194
+ display_message(" These providers won't be eligible for tasks requiring this MCP server", type: :muted)
195
+ end
196
+ end
197
+ end
198
+
199
+ def normalize_provider_name(name)
200
+ return "claude" if name == "anthropic"
201
+ name
202
+ end
203
+ end
204
+ end
205
+ end
data/lib/aidp/cli.rb CHANGED
@@ -183,7 +183,7 @@ module Aidp
183
183
 
184
184
  # Initialize the enhanced TUI
185
185
  tui = Aidp::Harness::UI::EnhancedTUI.new
186
- workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui)
186
+ workflow_selector = Aidp::Harness::UI::EnhancedWorkflowSelector.new(tui, project_dir: Dir.pwd)
187
187
 
188
188
  # Start TUI display loop
189
189
  tui.start_display_loop
@@ -245,6 +245,11 @@ module Aidp
245
245
  opts.separator " metrics - Show detailed metrics"
246
246
  opts.separator " clear [--force] - Clear checkpoint data"
247
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"
248
253
  opts.separator " harness Manage harness state"
249
254
  opts.separator " status - Show harness status"
250
255
  opts.separator " reset - Reset harness state"
@@ -274,6 +279,10 @@ module Aidp
274
279
  opts.separator ""
275
280
  opts.separator " # Other commands"
276
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"
277
286
  opts.separator " aidp checkpoint history 20 # Show last 20 checkpoints"
278
287
  opts.separator ""
279
288
  opts.separator "For more information, visit: https://github.com/viamin/aidp"
@@ -287,7 +296,7 @@ module Aidp
287
296
  # Determine if the invocation is a subcommand style call
288
297
  def subcommand?(args)
289
298
  return false if args.nil? || args.empty?
290
- %w[status jobs kb harness execute analyze providers checkpoint].include?(args.first)
299
+ %w[status jobs kb harness execute analyze providers checkpoint mcp].include?(args.first)
291
300
  end
292
301
 
293
302
  def run_subcommand(args)
@@ -301,6 +310,7 @@ module Aidp
301
310
  when "analyze" then run_execute_command(args, mode: :analyze) # symmetry
302
311
  when "providers" then run_providers_command(args)
303
312
  when "checkpoint" then run_checkpoint_command(args)
313
+ when "mcp" then run_mcp_command(args)
304
314
  else
305
315
  display_message("Unknown command: #{cmd}", type: :info)
306
316
  return 1
@@ -585,6 +595,19 @@ module Aidp
585
595
  end
586
596
 
587
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
+
588
611
  # Accept flags directly on `aidp providers` now (health is implicit)
589
612
  no_color = false
590
613
  args.reject! do |a|
@@ -660,6 +683,180 @@ module Aidp
660
683
  display_message("Failed to display provider health: #{e.message}", type: :error)
661
684
  end
662
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
+
663
860
  def extract_mode_option(args)
664
861
  mode = nil
665
862
  args.each do |arg|
@@ -677,17 +874,20 @@ module Aidp
677
874
 
678
875
  def select_mode_interactive(tui)
679
876
  mode_options = [
877
+ "🤖 Guided Workflow (Copilot) - AI helps you choose the right workflow",
680
878
  "🔬 Analyze Mode - Analyze your codebase for insights and recommendations",
681
879
  "🏗️ Execute Mode - Build new features with guided development workflow"
682
880
  ]
683
881
  selected = tui.single_select("Welcome to AI Dev Pipeline! Choose your mode", mode_options, default: 1)
684
882
  # Announce mode explicitly in headless contexts (handled internally otherwise)
685
883
  if (defined?(RSpec) || ENV["RSPEC_RUNNING"]) && tui.respond_to?(:announce_mode)
686
- tui.announce_mode(:analyze) if selected == mode_options[0]
687
- 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]
688
887
  end
689
- return :analyze if selected == mode_options[0]
690
- 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]
691
891
  :analyze
692
892
  end
693
893