aidp 0.26.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  3. data/lib/aidp/cli/config_command.rb +71 -0
  4. data/lib/aidp/cli/enhanced_input.rb +2 -0
  5. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  6. data/lib/aidp/cli/harness_command.rb +102 -0
  7. data/lib/aidp/cli/jobs_command.rb +3 -3
  8. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  9. data/lib/aidp/cli/models_command.rb +662 -0
  10. data/lib/aidp/cli/providers_command.rb +223 -0
  11. data/lib/aidp/cli.rb +35 -456
  12. data/lib/aidp/daemon/runner.rb +2 -2
  13. data/lib/aidp/debug_mixin.rb +2 -9
  14. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  15. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  16. data/lib/aidp/execute/interactive_repl.rb +2 -1
  17. data/lib/aidp/execute/prompt_manager.rb +4 -4
  18. data/lib/aidp/execute/work_loop_runner.rb +29 -2
  19. data/lib/aidp/execute/workflow_selector.rb +2 -2
  20. data/lib/aidp/harness/config_manager.rb +5 -5
  21. data/lib/aidp/harness/configuration.rb +32 -2
  22. data/lib/aidp/harness/enhanced_runner.rb +24 -15
  23. data/lib/aidp/harness/error_handler.rb +26 -5
  24. data/lib/aidp/harness/model_cache.rb +269 -0
  25. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  26. data/lib/aidp/harness/model_registry.rb +201 -0
  27. data/lib/aidp/harness/runner.rb +5 -0
  28. data/lib/aidp/harness/thinking_depth_manager.rb +223 -7
  29. data/lib/aidp/message_display.rb +0 -46
  30. data/lib/aidp/providers/adapter.rb +2 -4
  31. data/lib/aidp/providers/anthropic.rb +141 -128
  32. data/lib/aidp/providers/base.rb +98 -2
  33. data/lib/aidp/providers/capability_registry.rb +0 -1
  34. data/lib/aidp/providers/codex.rb +49 -67
  35. data/lib/aidp/providers/cursor.rb +71 -59
  36. data/lib/aidp/providers/gemini.rb +44 -60
  37. data/lib/aidp/providers/github_copilot.rb +2 -66
  38. data/lib/aidp/providers/kilocode.rb +24 -80
  39. data/lib/aidp/providers/opencode.rb +24 -80
  40. data/lib/aidp/setup/wizard.rb +345 -8
  41. data/lib/aidp/version.rb +1 -1
  42. data/lib/aidp/watch/plan_generator.rb +93 -14
  43. data/lib/aidp/watch/review_processor.rb +3 -3
  44. data/lib/aidp/workflows/guided_agent.rb +3 -3
  45. data/templates/aidp-development.yml.example +2 -2
  46. data/templates/aidp-production.yml.example +3 -3
  47. metadata +9 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b4770edc850e936b3201ec532cdd369f7fd881ff79dd10e24407c6453b8cb22
4
- data.tar.gz: d7d2640aa63d2c25afbf795bbf77d48741bdd99d6a074765fa5595f3982b96e6
3
+ metadata.gz: 174c58fcccbc5d5c927cb30dd3bf8c735de926cf52de5eb2a4bb56f2bc984cc3
4
+ data.tar.gz: 73efb11b31d3ff346a482fde9616047fd64525e44ffe768a81a6e4808e748cd1
5
5
  SHA512:
6
- metadata.gz: 1841e0b35478d65d382d13dd138955c30655002b91c2a83d96ec2c83925eec4f954892aa710ded867b50554dbc9653bddfad5c5aa915890e674bb49afed347bc
7
- data.tar.gz: 1fc1443566c4e01e0c6ff25f1ae62c890ddcd5373257265ee7119ea8db359c20282cf646d61b955204e4dc538ceb9c6313725cb4fed21cda231b97321cc2c030
6
+ metadata.gz: 407aecc4942fbda7fbf09e15ebfdfa6ba8595be9f282a0b425ce65dc22e45d3a524fe5dd211ce96ec59029801df01528a835412ba38f90cb273c5b1594df2016
7
+ data.tar.gz: 174a42200bb01198f3c88de3ec35198c9475f018f06a673e7d1d3c0e0dda9c294dd832519327261ac767a42323e965f1fa7ea1fa1d3b394425604ceaaa7bf371
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require_relative "../execute/checkpoint"
5
+ require_relative "../execute/checkpoint_display"
6
+
7
+ module Aidp
8
+ class CLI
9
+ # Command handler for `aidp checkpoint` subcommand
10
+ #
11
+ # Provides commands for managing workflow checkpoints:
12
+ # - show: Display latest checkpoint data
13
+ # - summary: Show progress summary with trends
14
+ # - history: Show last N checkpoints
15
+ # - metrics: Show detailed metrics
16
+ # - clear: Clear all checkpoint data
17
+ #
18
+ # Usage:
19
+ # aidp checkpoint show
20
+ # aidp checkpoint summary --watch
21
+ # aidp checkpoint history 10
22
+ # aidp checkpoint metrics
23
+ # aidp checkpoint clear --force
24
+ class CheckpointCommand
25
+ include Aidp::MessageDisplay
26
+
27
+ def initialize(prompt: TTY::Prompt.new, checkpoint_class: nil, display_class: nil, project_dir: nil)
28
+ @prompt = prompt
29
+ @checkpoint_class = checkpoint_class || Aidp::Execute::Checkpoint
30
+ @display_class = display_class || Aidp::Execute::CheckpointDisplay
31
+ @project_dir = project_dir || Dir.pwd
32
+ end
33
+
34
+ # Main entry point for checkpoint subcommands
35
+ def run(args)
36
+ sub = args.shift || "summary"
37
+ checkpoint = @checkpoint_class.new(@project_dir)
38
+ display = @display_class.new
39
+
40
+ case sub
41
+ when "show"
42
+ run_show_command(checkpoint, display)
43
+ when "summary"
44
+ run_summary_command(checkpoint, display, args)
45
+ when "history"
46
+ run_history_command(checkpoint, display, args)
47
+ when "metrics"
48
+ run_metrics_command(checkpoint)
49
+ when "clear"
50
+ run_clear_command(checkpoint, args)
51
+ else
52
+ display_usage
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def run_show_command(checkpoint, display)
59
+ latest = checkpoint.latest_checkpoint
60
+ if latest
61
+ display.display_checkpoint(latest, show_details: true)
62
+ else
63
+ display_message("No checkpoint data found.", type: :info)
64
+ end
65
+ end
66
+
67
+ def run_summary_command(checkpoint, display, args)
68
+ watch = args.include?("--watch")
69
+ interval = extract_interval_option(args) || 5
70
+
71
+ if watch
72
+ watch_checkpoint_summary(checkpoint, display, interval)
73
+ else
74
+ summary = checkpoint.progress_summary
75
+ if summary
76
+ display.display_progress_summary(summary)
77
+ else
78
+ display_message("No checkpoint data found.", type: :info)
79
+ end
80
+ end
81
+ end
82
+
83
+ def run_history_command(checkpoint, display, args)
84
+ limit = args.shift || "10"
85
+ history = checkpoint.checkpoint_history(limit: limit.to_i)
86
+ if history.any?
87
+ display.display_checkpoint_history(history, limit: limit.to_i)
88
+ else
89
+ display_message("No checkpoint history found.", type: :info)
90
+ end
91
+ end
92
+
93
+ def run_metrics_command(checkpoint)
94
+ latest = checkpoint.latest_checkpoint
95
+ unless latest
96
+ display_message("No checkpoint data found.", type: :info)
97
+ return
98
+ end
99
+
100
+ display_message("", type: :info)
101
+ display_message("šŸ“Š Detailed Metrics", type: :info)
102
+ display_message("=" * 60, type: :muted)
103
+
104
+ metrics = latest[:metrics]
105
+ display_message("Lines of Code: #{metrics[:lines_of_code]}", type: :info)
106
+ display_message("File Count: #{metrics[:file_count]}", type: :info)
107
+ display_message("Test Coverage: #{metrics[:test_coverage]}%", type: :info)
108
+ display_message("Code Quality: #{metrics[:code_quality]}%", type: :info)
109
+ display_message("PRD Task Progress: #{metrics[:prd_task_progress]}%", type: :info)
110
+
111
+ if metrics.key?(:tests_passing)
112
+ status = metrics[:tests_passing] ? "āœ“ Passing" : "āœ— Failing"
113
+ display_message("Tests: #{status}", type: :info)
114
+ end
115
+
116
+ if metrics.key?(:linters_passing)
117
+ status = metrics[:linters_passing] ? "āœ“ Passing" : "āœ— Failing"
118
+ display_message("Linters: #{status}", type: :info)
119
+ end
120
+
121
+ display_message("=" * 60, type: :muted)
122
+ display_message("", type: :info)
123
+ end
124
+
125
+ def run_clear_command(checkpoint, args)
126
+ force = args.include?("--force")
127
+ unless force
128
+ confirm = @prompt.yes?("Are you sure you want to clear all checkpoint data?")
129
+ return unless confirm
130
+ end
131
+
132
+ checkpoint.clear
133
+ display_message("āœ“ Checkpoint data cleared.", type: :success)
134
+ end
135
+
136
+ def watch_checkpoint_summary(checkpoint, display, interval)
137
+ display_message("Watching checkpoint summary (refresh: #{interval}s, Ctrl+C to exit)...", type: :info)
138
+ display_message("", type: :info)
139
+
140
+ begin
141
+ loop do
142
+ # Clear screen
143
+ print "\e[2J\e[H"
144
+
145
+ summary = checkpoint.progress_summary
146
+ if summary
147
+ display.display_progress_summary(summary)
148
+
149
+ # Show last update time
150
+ if summary[:current] && summary[:current][:timestamp]
151
+ last_update = Time.parse(summary[:current][:timestamp])
152
+ age = Time.now - last_update
153
+ display_message("", type: :info)
154
+ display_message("Last update: #{format_time_ago_simple(age)} | Refreshing in #{interval}s...", type: :muted)
155
+ end
156
+ else
157
+ display_message("No checkpoint data found. Waiting for data...", type: :info)
158
+ end
159
+
160
+ sleep interval
161
+ end
162
+ rescue Interrupt
163
+ display_message("\nStopped watching checkpoint summary", type: :info)
164
+ end
165
+ end
166
+
167
+ def format_time_ago_simple(seconds)
168
+ if seconds < 60
169
+ "#{seconds.to_i}s ago"
170
+ elsif seconds < 3600
171
+ "#{(seconds / 60).to_i}m ago"
172
+ else
173
+ "#{(seconds / 3600).to_i}h ago"
174
+ end
175
+ end
176
+
177
+ def extract_interval_option(args)
178
+ args.each_with_index do |arg, i|
179
+ if arg == "--interval" && args[i + 1]
180
+ return args[i + 1].to_i
181
+ elsif arg.start_with?("--interval=")
182
+ return arg.split("=")[1].to_i
183
+ end
184
+ end
185
+ nil
186
+ end
187
+
188
+ def display_usage
189
+ display_message("Usage: aidp checkpoint <show|summary|history|metrics|clear>", type: :info)
190
+ display_message(" show - Show the latest checkpoint data", type: :info)
191
+ display_message(" summary [--watch] - Show progress summary with trends", type: :info)
192
+ display_message(" history [N] - Show last N checkpoints", type: :info)
193
+ display_message(" metrics - Show detailed metrics", type: :info)
194
+ display_message(" clear [--force] - Clear all checkpoint data", type: :info)
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require_relative "../setup/wizard"
5
+
6
+ module Aidp
7
+ class CLI
8
+ # Command handler for `aidp config` subcommand
9
+ #
10
+ # Provides commands for managing AIDP configuration:
11
+ # - Interactive configuration wizard
12
+ # - Dry-run mode for testing configuration changes
13
+ #
14
+ # Usage:
15
+ # aidp config --interactive
16
+ # aidp config --interactive --dry-run
17
+ class ConfigCommand
18
+ include Aidp::MessageDisplay
19
+
20
+ def initialize(prompt: TTY::Prompt.new, wizard_class: nil, project_dir: nil)
21
+ @prompt = prompt
22
+ @wizard_class = wizard_class || Aidp::Setup::Wizard
23
+ @project_dir = project_dir || Dir.pwd
24
+ end
25
+
26
+ # Main entry point for config command
27
+ def run(args)
28
+ interactive = false
29
+ dry_run = false
30
+
31
+ until args.empty?
32
+ token = args.shift
33
+ case token
34
+ when "--interactive"
35
+ interactive = true
36
+ when "--dry-run"
37
+ dry_run = true
38
+ when "-h", "--help"
39
+ display_usage
40
+ return
41
+ else
42
+ display_message("Unknown option: #{token}", type: :error)
43
+ display_usage
44
+ return
45
+ end
46
+ end
47
+
48
+ unless interactive
49
+ display_usage
50
+ return
51
+ end
52
+
53
+ wizard = @wizard_class.new(@project_dir, prompt: @prompt, dry_run: dry_run)
54
+ wizard.run
55
+ end
56
+
57
+ private
58
+
59
+ def display_usage
60
+ display_message("\nUsage: aidp config --interactive [--dry-run]", type: :info)
61
+ display_message("\nOptions:", type: :info)
62
+ display_message(" --interactive Run interactive configuration wizard", type: :info)
63
+ display_message(" --dry-run Perform a dry run without making changes", type: :info)
64
+ display_message(" -h, --help Show this help message", type: :info)
65
+ display_message("\nExamples:", type: :info)
66
+ display_message(" aidp config --interactive", type: :info)
67
+ display_message(" aidp config --interactive --dry-run", type: :info)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -21,6 +21,8 @@ module Aidp
21
21
  # - Ctrl-T: Transpose characters
22
22
  # - And many more Emacs-style bindings
23
23
 
24
+ attr_reader :use_reline, :show_hints
25
+
24
26
  def initialize(prompt: nil, input: nil, output: nil, use_reline: true)
25
27
  @use_reline = use_reline
26
28
  @input = input || $stdin
@@ -10,10 +10,10 @@ module Aidp
10
10
  class FirstRunWizard
11
11
  include Aidp::MessageDisplay
12
12
 
13
- def self.ensure_config(project_dir, non_interactive: false, prompt: TTY::Prompt.new)
13
+ def self.ensure_config(project_dir, non_interactive: false, prompt: TTY::Prompt.new, wizard_class: Aidp::Setup::Wizard)
14
14
  return true if Aidp::Config.config_exists?(project_dir)
15
15
 
16
- wizard = new(project_dir, prompt: prompt)
16
+ wizard = new(project_dir, prompt: prompt, wizard_class: wizard_class)
17
17
 
18
18
  if non_interactive
19
19
  wizard.create_minimal_config
@@ -24,22 +24,23 @@ module Aidp
24
24
  end
25
25
  end
26
26
 
27
- def self.setup_config(project_dir, non_interactive: false, prompt: TTY::Prompt.new)
27
+ def self.setup_config(project_dir, non_interactive: false, prompt: TTY::Prompt.new, wizard_class: Aidp::Setup::Wizard)
28
28
  if non_interactive
29
- new(project_dir, prompt: prompt).send(:display_message, "Configuration setup skipped in non-interactive environment", type: :info)
29
+ new(project_dir, prompt: prompt, wizard_class: wizard_class).send(:display_message, "Configuration setup skipped in non-interactive environment", type: :info)
30
30
  return true
31
31
  end
32
32
 
33
- new(project_dir, prompt: prompt).run
33
+ new(project_dir, prompt: prompt, wizard_class: wizard_class).run
34
34
  end
35
35
 
36
- def initialize(project_dir, prompt: TTY::Prompt.new)
36
+ def initialize(project_dir, prompt: TTY::Prompt.new, wizard_class: Aidp::Setup::Wizard)
37
37
  @project_dir = project_dir
38
38
  @prompt = prompt
39
+ @wizard_class = wizard_class
39
40
  end
40
41
 
41
42
  def run
42
- wizard = Aidp::Setup::Wizard.new(@project_dir, prompt: @prompt)
43
+ wizard = @wizard_class.new(@project_dir, prompt: @prompt)
43
44
  wizard.run
44
45
  end
45
46
 
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require_relative "../harness/runner"
5
+
6
+ module Aidp
7
+ class CLI
8
+ # Command handler for `aidp harness status` and `aidp harness reset` subcommands
9
+ #
10
+ # Provides commands for viewing and managing harness state:
11
+ # - status: Show detailed harness status for all modes
12
+ # - reset: Reset harness state for a specific mode
13
+ #
14
+ # Usage:
15
+ # aidp harness status
16
+ # aidp harness reset --mode analyze
17
+ class HarnessCommand
18
+ include Aidp::MessageDisplay
19
+ include Aidp::RescueLogging
20
+
21
+ def initialize(prompt: TTY::Prompt.new, runner_class: nil, project_dir: nil)
22
+ @prompt = prompt
23
+ @runner_class = runner_class || Aidp::Harness::Runner
24
+ @project_dir = project_dir || Dir.pwd
25
+ end
26
+
27
+ # Main entry point for harness status/reset subcommands
28
+ def run(args, subcommand:, options: {})
29
+ case subcommand
30
+ when "status"
31
+ run_status_command
32
+ when "reset"
33
+ run_reset_command(options)
34
+ else
35
+ display_message("Unknown harness subcommand: #{subcommand}", type: :error)
36
+ display_help
37
+ 1
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def run_status_command
44
+ modes = %i[analyze execute]
45
+ display_message("šŸ”§ Harness Status", type: :highlight)
46
+ modes.each do |mode|
47
+ status = fetch_harness_status(mode)
48
+ print_harness_mode_status(mode, status)
49
+ end
50
+ end
51
+
52
+ def run_reset_command(options)
53
+ mode = (options[:mode] || "analyze").to_s
54
+ unless %w[analyze execute].include?(mode)
55
+ display_message("āŒ Invalid mode. Use 'analyze' or 'execute'", type: :error)
56
+ return
57
+ end
58
+
59
+ # Build a runner to access state manager
60
+ runner = @runner_class.new(@project_dir, mode.to_sym, {})
61
+ state_manager = runner.instance_variable_get(:@state_manager)
62
+ state_manager.reset_all if state_manager.respond_to?(:reset_all)
63
+ display_message("āœ… Reset harness state for #{mode} mode", type: :success)
64
+ end
65
+
66
+ def fetch_harness_status(mode)
67
+ runner = @runner_class.new(@project_dir, mode, {})
68
+ if runner.respond_to?(:detailed_status)
69
+ runner.detailed_status
70
+ else
71
+ {harness: {state: "unknown"}}
72
+ end
73
+ rescue => e
74
+ log_rescue(e, component: "harness_command", action: "fetch_harness_status", fallback: {harness: {state: "error"}}, mode: mode)
75
+ {harness: {state: "error", error: e.message}}
76
+ end
77
+
78
+ def print_harness_mode_status(mode, status)
79
+ harness = status[:harness] || {}
80
+ display_message("\nšŸ“‹ #{mode.to_s.capitalize} Mode:", type: :info)
81
+ display_message(" State: #{harness[:state]}", type: :info)
82
+ if harness[:progress]
83
+ prog = harness[:progress]
84
+ display_message(" Progress: #{prog[:completed_steps]}/#{prog[:total_steps]}", type: :success)
85
+ display_message(" Current Step: #{harness[:current_step]}", type: :info) if harness[:current_step]
86
+ end
87
+ end
88
+
89
+ def display_help
90
+ display_message("\nUsage: aidp harness <subcommand> [options]", type: :info)
91
+ display_message("\nSubcommands:", type: :info)
92
+ display_message(" status Show harness status for all modes", type: :info)
93
+ display_message(" reset Reset harness state for a mode", type: :info)
94
+ display_message("\nOptions:", type: :info)
95
+ display_message(" --mode MODE Specify mode for reset (analyze|execute)", type: :info)
96
+ display_message("\nExamples:", type: :info)
97
+ display_message(" aidp harness status", type: :info)
98
+ display_message(" aidp harness reset --mode analyze", type: :info)
99
+ end
100
+ end
101
+ end
102
+ end
@@ -14,7 +14,7 @@ module Aidp
14
14
  class JobsCommand
15
15
  include Aidp::MessageDisplay
16
16
 
17
- def initialize(input: nil, output: nil, prompt: TTY::Prompt.new)
17
+ def initialize(input: nil, output: nil, prompt: TTY::Prompt.new, file_manager: nil, background_runner: nil)
18
18
  @io = TerminalIO.new(input: input, output: output)
19
19
  @prompt = prompt
20
20
  @pastel = Pastel.new
@@ -22,8 +22,8 @@ module Aidp
22
22
  @view_mode = :list
23
23
  @selected_job_id = nil
24
24
  @jobs_displayed = false # Track if we've displayed jobs in interactive mode
25
- @file_manager = Aidp::Storage::FileManager.new(File.join(Dir.pwd, ".aidp"))
26
- @background_runner = Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
25
+ @file_manager = file_manager || Aidp::Storage::FileManager.new(File.join(Dir.pwd, ".aidp"))
26
+ @background_runner = background_runner || Aidp::Jobs::BackgroundRunner.new(Dir.pwd)
27
27
  @screen_width = 80 # Default screen width
28
28
  end
29
29
 
@@ -10,9 +10,10 @@ module Aidp
10
10
  class McpDashboard
11
11
  include Aidp::MessageDisplay
12
12
 
13
- def initialize(root_dir = nil)
13
+ def initialize(root_dir = nil, configuration: nil, provider_info_class: Aidp::Harness::ProviderInfo)
14
14
  @root_dir = root_dir || Dir.pwd
15
- @configuration = Aidp::Harness::Configuration.new(@root_dir)
15
+ @configuration = configuration || Aidp::Harness::Configuration.new(@root_dir)
16
+ @provider_info_class = provider_info_class
16
17
  end
17
18
 
18
19
  # Display MCP dashboard showing all servers across all providers
@@ -101,7 +102,7 @@ module Aidp
101
102
  provider_servers = {} # provider_name => [server_info]
102
103
 
103
104
  providers.each do |provider|
104
- provider_info = Aidp::Harness::ProviderInfo.new(provider, @root_dir)
105
+ provider_info = @provider_info_class.new(provider, @root_dir)
105
106
  info = provider_info.info
106
107
 
107
108
  next unless info[:mcp_support]