aidp 0.12.0 → 0.13.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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/analyze/json_file_storage.rb +21 -21
  3. data/lib/aidp/cli/enhanced_input.rb +114 -0
  4. data/lib/aidp/cli/first_run_wizard.rb +12 -14
  5. data/lib/aidp/cli/mcp_dashboard.rb +3 -3
  6. data/lib/aidp/cli/terminal_io.rb +26 -0
  7. data/lib/aidp/cli.rb +4 -4
  8. data/lib/aidp/config/paths.rb +131 -0
  9. data/lib/aidp/config.rb +18 -4
  10. data/lib/aidp/harness/condition_detector.rb +6 -6
  11. data/lib/aidp/harness/config_loader.rb +23 -23
  12. data/lib/aidp/harness/config_manager.rb +61 -61
  13. data/lib/aidp/harness/config_validator.rb +13 -12
  14. data/lib/aidp/harness/configuration.rb +30 -29
  15. data/lib/aidp/harness/error_handler.rb +13 -13
  16. data/lib/aidp/harness/provider_config.rb +79 -79
  17. data/lib/aidp/harness/provider_factory.rb +40 -40
  18. data/lib/aidp/harness/provider_info.rb +37 -20
  19. data/lib/aidp/harness/provider_manager.rb +58 -53
  20. data/lib/aidp/harness/provider_type_checker.rb +6 -6
  21. data/lib/aidp/harness/runner.rb +7 -7
  22. data/lib/aidp/harness/status_display.rb +33 -46
  23. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +2 -1
  24. data/lib/aidp/harness/ui/job_monitor.rb +7 -7
  25. data/lib/aidp/harness/user_interface.rb +43 -43
  26. data/lib/aidp/providers/anthropic.rb +100 -26
  27. data/lib/aidp/providers/base.rb +13 -0
  28. data/lib/aidp/providers/codex.rb +28 -27
  29. data/lib/aidp/providers/cursor.rb +141 -34
  30. data/lib/aidp/providers/github_copilot.rb +26 -26
  31. data/lib/aidp/providers/macos_ui.rb +2 -18
  32. data/lib/aidp/providers/opencode.rb +26 -26
  33. data/lib/aidp/version.rb +1 -1
  34. data/lib/aidp/workflows/guided_agent.rb +344 -23
  35. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5c07df32790749c9d60716d1ae3133f0a232cda036e7523b74b8c0725a0b066
4
- data.tar.gz: b15140f57f3d8cc58dc4b4e79200d3b0ef50152c5f6bcd6f45b030aeca8c863e
3
+ metadata.gz: 0f6484f93a1d57d07f223f49abab2f1a919490d4cb25e6623e76ca85b430a0be
4
+ data.tar.gz: 6f765c07f09d77cf3ffab88c36aa9a04acf90c39fc44d326a594b7ff4b67759b
5
5
  SHA512:
6
- metadata.gz: 1e5e5b0f9c209dea98509a29b4004f264cb49706d961cafe42ad2867a40abc6ecd9e3cf279a541e714c914f975ec25f6ed898a7be7549dc6ae1292e369eaa7b9
7
- data.tar.gz: 5f7a286a65cd9e1ecbdbd167182fa1471e9407171a2fc028cfa144d9204606d8c8e3a7138348c8406133713aa1724749e7abcc0fa6904fca64b65b43355bbfd8
6
+ metadata.gz: e3219a59423ae33fc74bfaf38ac3ecebc3a157ac4f789c22252527652f02f63a1d98078de3db8b277d323073bf5cec8b86c97e9e7749f9a4e3097c6d849a4c2e
7
+ data.tar.gz: 3ec1273d7a35839fef07a02fa3a77e9340c008146c87ae790c8829f851f668af5fe1a1df12e28244b2b78c3313dd4ca667cb5e794232a3c4507f09f540d46cd9
@@ -14,7 +14,7 @@ module Aidp
14
14
 
15
15
  # Store data in a JSON file
16
16
  def store_data(filename, data)
17
- file_path = get_file_path(filename)
17
+ file_path = file_path(filename)
18
18
 
19
19
  # Ensure directory exists
20
20
  FileUtils.mkdir_p(File.dirname(file_path))
@@ -31,8 +31,8 @@ module Aidp
31
31
  end
32
32
 
33
33
  # Retrieve data from a JSON file
34
- def get_data(filename)
35
- file_path = get_file_path(filename)
34
+ def data(filename)
35
+ file_path = file_path(filename)
36
36
 
37
37
  return nil unless File.exist?(file_path)
38
38
 
@@ -45,12 +45,12 @@ module Aidp
45
45
 
46
46
  # Check if a JSON file exists
47
47
  def data_exists?(filename)
48
- File.exist?(get_file_path(filename))
48
+ File.exist?(file_path(filename))
49
49
  end
50
50
 
51
51
  # Delete a JSON file
52
52
  def delete_data(filename)
53
- file_path = get_file_path(filename)
53
+ file_path = file_path(filename)
54
54
 
55
55
  if File.exist?(file_path)
56
56
  File.delete(file_path)
@@ -89,8 +89,8 @@ module Aidp
89
89
  end
90
90
 
91
91
  # Get project configuration
92
- def get_project_config
93
- get_data("project_config.json")
92
+ def project_config
93
+ data("project_config.json")
94
94
  end
95
95
 
96
96
  # Store runtime status
@@ -99,8 +99,8 @@ module Aidp
99
99
  end
100
100
 
101
101
  # Get runtime status
102
- def get_runtime_status
103
- get_data("runtime_status.json")
102
+ def runtime_status
103
+ data("runtime_status.json")
104
104
  end
105
105
 
106
106
  # Store simple metrics
@@ -109,8 +109,8 @@ module Aidp
109
109
  end
110
110
 
111
111
  # Get simple metrics
112
- def get_simple_metrics
113
- get_data("simple_metrics.json")
112
+ def simple_metrics
113
+ data("simple_metrics.json")
114
114
  end
115
115
 
116
116
  # Store analysis session data
@@ -119,8 +119,8 @@ module Aidp
119
119
  end
120
120
 
121
121
  # Get analysis session data
122
- def get_analysis_session(session_id)
123
- get_data("sessions/#{session_id}.json")
122
+ def analysis_session(session_id)
123
+ data("sessions/#{session_id}.json")
124
124
  end
125
125
 
126
126
  # List analysis sessions
@@ -145,8 +145,8 @@ module Aidp
145
145
  end
146
146
 
147
147
  # Get user preferences
148
- def get_user_preferences
149
- get_data("user_preferences.json")
148
+ def user_preferences
149
+ data("user_preferences.json")
150
150
  end
151
151
 
152
152
  # Store cache data
@@ -161,8 +161,8 @@ module Aidp
161
161
  end
162
162
 
163
163
  # Get cache data (respects TTL)
164
- def get_cache(cache_key)
165
- cache_file_data = get_data("cache/#{cache_key}.json")
164
+ def cache(cache_key)
165
+ cache_file_data = data("cache/#{cache_key}.json")
166
166
  return nil unless cache_file_data
167
167
 
168
168
  # Check TTL if specified
@@ -203,7 +203,7 @@ module Aidp
203
203
  end
204
204
 
205
205
  # Get storage statistics
206
- def get_storage_statistics
206
+ def storage_statistics
207
207
  files = list_files
208
208
 
209
209
  {
@@ -226,7 +226,7 @@ module Aidp
226
226
 
227
227
  files = list_files
228
228
  files.each do |file_info|
229
- data = get_data(file_info[:filename])
229
+ data = data(file_info[:filename])
230
230
  export_data["files"][file_info[:filename]] = {
231
231
  "data" => data,
232
232
  "metadata" => {
@@ -249,7 +249,7 @@ module Aidp
249
249
 
250
250
  # Import data from an exported JSON file
251
251
  def import_data(import_filename)
252
- import_path = get_file_path(import_filename)
252
+ import_path = file_path(import_filename)
253
253
 
254
254
  unless File.exist?(import_path)
255
255
  raise "Import file does not exist: #{import_filename}"
@@ -280,7 +280,7 @@ module Aidp
280
280
 
281
281
  private
282
282
 
283
- def get_file_path(filename)
283
+ def file_path(filename)
284
284
  File.join(@storage_dir, filename)
285
285
  end
286
286
 
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-prompt"
4
+ require "reline"
5
+
6
+ module Aidp
7
+ class CLI
8
+ # Enhanced input handler with full readline-style key bindings using Reline
9
+ class EnhancedInput
10
+ # Standard key bindings supported by Reline:
11
+ # - Ctrl-A: Move to beginning of line
12
+ # - Ctrl-E: Move to end of line
13
+ # - Ctrl-W: Delete word backward
14
+ # - Ctrl-K: Kill to end of line
15
+ # - Ctrl-U: Kill to beginning of line
16
+ # - Ctrl-D: Delete character forward
17
+ # - Ctrl-H/Backspace: Delete character backward
18
+ # - Left/Right arrows: Move cursor
19
+ # - Alt-F/Alt-B: Move forward/backward by word
20
+ # - Home/End: Jump to beginning/end
21
+ # - Ctrl-T: Transpose characters
22
+ # - And many more Emacs-style bindings
23
+
24
+ def initialize(prompt: nil, input: nil, output: nil, use_reline: true)
25
+ @use_reline = use_reline
26
+ @input = input || $stdin
27
+ @output = output || $stdout
28
+ @prompt = prompt || TTY::Prompt.new(
29
+ input: @input,
30
+ output: @output,
31
+ enable_color: true,
32
+ interrupt: :exit
33
+ )
34
+ @show_hints = false
35
+ end
36
+
37
+ # Ask a question with full readline support
38
+ # Uses Reline for readline-style editing when use_reline is true
39
+ def ask(question, **options)
40
+ # If reline is enabled and we're in a TTY, use reline for better editing
41
+ if @use_reline && @input.tty?
42
+ default = options[:default]
43
+ required = options[:required] || false
44
+
45
+ # Display helpful hint on first use
46
+ if @show_hints
47
+ @output.puts "💡 Hint: Use Ctrl-A (start), Ctrl-E (end), Ctrl-W (delete word), Ctrl-K (kill line)"
48
+ @show_hints = false
49
+ end
50
+
51
+ # Use Reline for input with full key binding support
52
+ loop do
53
+ prompt_text = question.to_s
54
+ prompt_text += " (#{default})" if default
55
+ prompt_text += " "
56
+
57
+ # Reline provides full readline editing capabilities
58
+ Reline.output = @output
59
+ Reline.input = @input
60
+ Reline.completion_append_character = " "
61
+
62
+ answer = Reline.readline(prompt_text, false)
63
+
64
+ # Handle Ctrl-D (nil return)
65
+ if answer.nil?
66
+ @output.puts
67
+ raise Interrupt
68
+ end
69
+
70
+ answer = answer.strip
71
+ answer = default if answer.empty? && default
72
+
73
+ if required && (answer.nil? || answer.empty?)
74
+ @output.puts " Value required."
75
+ next
76
+ end
77
+
78
+ return answer
79
+ end
80
+ else
81
+ # Fall back to TTY::Prompt's ask
82
+ @prompt.ask(question, **options)
83
+ end
84
+ rescue Interrupt
85
+ @output.puts
86
+ raise
87
+ end
88
+
89
+ # Enable hints for key bindings
90
+ def enable_hints!
91
+ @show_hints = true
92
+ end
93
+
94
+ # Disable Reline (fall back to TTY::Prompt)
95
+ def disable_reline!
96
+ @use_reline = false
97
+ end
98
+
99
+ # Enable Reline
100
+ def enable_reline!
101
+ @use_reline = true
102
+ end
103
+
104
+ # Delegate other methods to underlying prompt
105
+ def method_missing(method, *args, **kwargs, &block)
106
+ @prompt.send(method, *args, **kwargs, &block)
107
+ end
108
+
109
+ def respond_to_missing?(method, include_private = false)
110
+ @prompt.respond_to?(method, include_private) || super
111
+ end
112
+ end
113
+ end
114
+ end
@@ -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,26 +120,23 @@ 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].")
136
138
  @prompt.say("")
137
139
 
138
- # Get available providers for validation
139
- available_providers = get_available_providers
140
-
141
140
  # Use TTY::Prompt select for primary provider
142
141
  # Find the formatted string that matches the default
143
142
  default_option = available_providers.find { |option| option.start_with?("cursor -") } || available_providers.first
@@ -169,12 +168,13 @@ module Aidp
169
168
  },
170
169
  "providers" => provider_section
171
170
  }
171
+ Aidp::ConfigPaths.ensure_config_dir(@project_dir)
172
172
  File.write(dest, YAML.dump(data))
173
173
  dest
174
174
  end
175
175
 
176
176
  def run_custom_with_defaults(existing_config)
177
- dest = File.join(@project_dir, "aidp.yml")
177
+ dest = Aidp::ConfigPaths.config_file(@project_dir)
178
178
 
179
179
  # Extract current values from existing config
180
180
  harness_config = existing_config[:harness] || existing_config["harness"] || {}
@@ -188,9 +188,6 @@ module Aidp
188
188
  @prompt.say("Interactive configuration update: press Enter to keep current values shown in [brackets].")
189
189
  @prompt.say("")
190
190
 
191
- # Get available providers for validation
192
- available_providers = get_available_providers
193
-
194
191
  # Use TTY::Prompt select for primary provider
195
192
  # Find the formatted string that matches the current default
196
193
  default_option = available_providers.find { |option| option.start_with?("#{current_default} -") } || available_providers.first
@@ -240,12 +237,13 @@ module Aidp
240
237
  "providers" => provider_section
241
238
  }
242
239
 
240
+ Aidp::ConfigPaths.ensure_config_dir(@project_dir)
243
241
  File.write(dest, YAML.dump(data))
244
242
  dest
245
243
  end
246
244
 
247
245
  def load_existing_config
248
- config_file = File.join(@project_dir, "aidp.yml")
246
+ config_file = Aidp::ConfigPaths.config_file(@project_dir)
249
247
  return nil unless File.exist?(config_file)
250
248
 
251
249
  begin
@@ -274,7 +272,7 @@ module Aidp
274
272
  end
275
273
 
276
274
  # Get available providers for validation
277
- def get_available_providers
275
+ def available_providers
278
276
  # Get all supported providers from the factory (single source of truth)
279
277
  all_providers = Aidp::Harness::ProviderFactory::PROVIDER_CLASSES.keys
280
278
 
@@ -57,7 +57,7 @@ module Aidp
57
57
  server_matrix = build_server_matrix
58
58
  eligible_providers = []
59
59
 
60
- @configuration.configured_providers.each do |provider|
60
+ @configuration.provider_names.each do |provider|
61
61
  provider_servers = server_matrix[:provider_servers][provider] || []
62
62
  enabled_servers = provider_servers.select { |s| s[:enabled] }.map { |s| s[:name] }
63
63
 
@@ -70,7 +70,7 @@ module Aidp
70
70
  {
71
71
  required_servers: required_servers,
72
72
  eligible_providers: eligible_providers,
73
- total_providers: @configuration.configured_providers.size
73
+ total_providers: @configuration.provider_names.size
74
74
  }
75
75
  end
76
76
 
@@ -96,7 +96,7 @@ module Aidp
96
96
  private
97
97
 
98
98
  def build_server_matrix
99
- providers = @configuration.configured_providers
99
+ providers = @configuration.provider_names
100
100
  all_servers = {} # server_name => {providers: {provider_name => server_info}}
101
101
  provider_servers = {} # provider_name => [server_info]
102
102
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
+ require "tty-reader"
4
5
 
5
6
  module Aidp
6
7
  class CLI
@@ -32,6 +33,31 @@ module Aidp
32
33
  @input.gets
33
34
  end
34
35
 
36
+ # Enhanced readline-style input with standard key combinations
37
+ # Supports: Ctrl-A (beginning), Ctrl-E (end), Ctrl-W (delete word), etc.
38
+ def readline(prompt = "", default: nil)
39
+ # Use StringIO for testing, otherwise use TTY::Reader for real input
40
+ if @input.is_a?(StringIO)
41
+ @output.print(prompt)
42
+ @output.flush
43
+ line = @input.gets
44
+ return line&.chomp if line
45
+ return default
46
+ end
47
+
48
+ reader = TTY::Reader.new(
49
+ input: @input,
50
+ output: @output,
51
+ interrupt: :exit
52
+ )
53
+
54
+ # Read line with full readline support (Ctrl-A, Ctrl-E, Ctrl-W, etc.)
55
+ result = reader.read_line(prompt, default: default || "")
56
+ result&.chomp
57
+ rescue TTY::Reader::InputInterrupt
58
+ raise Interrupt
59
+ end
60
+
35
61
  def write(str)
36
62
  @output.write(str)
37
63
  end
data/lib/aidp/cli.rb CHANGED
@@ -618,8 +618,8 @@ module Aidp
618
618
  false
619
619
  end
620
620
  end
621
- configuration = Aidp::Harness::Configuration.new(Dir.pwd)
622
- pm = Aidp::Harness::ProviderManager.new(configuration, prompt: TTY::Prompt.new)
621
+ config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
622
+ pm = Aidp::Harness::ProviderManager.new(config_manager, prompt: TTY::Prompt.new)
623
623
 
624
624
  # Use TTY::Spinner for progress indication
625
625
  require "tty-spinner"
@@ -772,11 +772,11 @@ module Aidp
772
772
  require "tty-spinner"
773
773
 
774
774
  provider_name = args.shift
775
- configuration = Aidp::Harness::Configuration.new(Dir.pwd)
775
+ config_manager = Aidp::Harness::ConfigManager.new(Dir.pwd)
776
776
  providers_to_refresh = if provider_name
777
777
  [provider_name]
778
778
  else
779
- configuration.configured_providers
779
+ config_manager.provider_names
780
780
  end
781
781
 
782
782
  display_message("Refreshing provider information...", type: :info)
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Aidp
6
+ # Centralized path management for all AIDP internal files
7
+ # Ensures consistent file locations and prevents path-related bugs
8
+ module ConfigPaths
9
+ # Get the main AIDP directory for a project
10
+ def self.aidp_dir(project_dir = Dir.pwd)
11
+ File.join(project_dir, ".aidp")
12
+ end
13
+
14
+ # Get the main configuration file path
15
+ def self.config_file(project_dir = Dir.pwd)
16
+ File.join(aidp_dir(project_dir), "aidp.yml")
17
+ end
18
+
19
+ # Get the configuration directory path
20
+ def self.config_dir(project_dir = Dir.pwd)
21
+ aidp_dir(project_dir)
22
+ end
23
+
24
+ # Get the progress directory path
25
+ def self.progress_dir(project_dir = Dir.pwd)
26
+ File.join(aidp_dir(project_dir), "progress")
27
+ end
28
+
29
+ # Get the execute progress file path
30
+ def self.execute_progress_file(project_dir = Dir.pwd)
31
+ File.join(progress_dir(project_dir), "execute.yml")
32
+ end
33
+
34
+ # Get the analyze progress file path
35
+ def self.analyze_progress_file(project_dir = Dir.pwd)
36
+ File.join(progress_dir(project_dir), "analyze.yml")
37
+ end
38
+
39
+ # Get the harness state directory path
40
+ def self.harness_state_dir(project_dir = Dir.pwd)
41
+ File.join(aidp_dir(project_dir), "harness")
42
+ end
43
+
44
+ # Get the harness state file path for a specific mode
45
+ def self.harness_state_file(mode, project_dir = Dir.pwd)
46
+ File.join(harness_state_dir(project_dir), "#{mode}_state.json")
47
+ end
48
+
49
+ # Get the providers directory path
50
+ def self.providers_dir(project_dir = Dir.pwd)
51
+ File.join(aidp_dir(project_dir), "providers")
52
+ end
53
+
54
+ # Get the provider info file path
55
+ def self.provider_info_file(provider_name, project_dir = Dir.pwd)
56
+ File.join(providers_dir(project_dir), "#{provider_name}_info.yml")
57
+ end
58
+
59
+ # Get the jobs directory path
60
+ def self.jobs_dir(project_dir = Dir.pwd)
61
+ File.join(aidp_dir(project_dir), "jobs")
62
+ end
63
+
64
+ # Get the checkpoint file path
65
+ def self.checkpoint_file(project_dir = Dir.pwd)
66
+ File.join(aidp_dir(project_dir), "checkpoint.yml")
67
+ end
68
+
69
+ # Get the checkpoint history file path
70
+ def self.checkpoint_history_file(project_dir = Dir.pwd)
71
+ File.join(aidp_dir(project_dir), "checkpoint_history.jsonl")
72
+ end
73
+
74
+ # Get the JSON storage directory path
75
+ def self.json_storage_dir(project_dir = Dir.pwd)
76
+ File.join(aidp_dir(project_dir), "json")
77
+ end
78
+
79
+ # Check if the main configuration file exists
80
+ def self.config_exists?(project_dir = Dir.pwd)
81
+ File.exist?(config_file(project_dir))
82
+ end
83
+
84
+ # Ensure the main AIDP directory exists
85
+ def self.ensure_aidp_dir(project_dir = Dir.pwd)
86
+ dir = aidp_dir(project_dir)
87
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
88
+ dir
89
+ end
90
+
91
+ # Ensure the configuration directory exists
92
+ def self.ensure_config_dir(project_dir = Dir.pwd)
93
+ ensure_aidp_dir(project_dir)
94
+ end
95
+
96
+ # Ensure the progress directory exists
97
+ def self.ensure_progress_dir(project_dir = Dir.pwd)
98
+ dir = progress_dir(project_dir)
99
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
100
+ dir
101
+ end
102
+
103
+ # Ensure the harness state directory exists
104
+ def self.ensure_harness_state_dir(project_dir = Dir.pwd)
105
+ dir = harness_state_dir(project_dir)
106
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
107
+ dir
108
+ end
109
+
110
+ # Ensure the providers directory exists
111
+ def self.ensure_providers_dir(project_dir = Dir.pwd)
112
+ dir = providers_dir(project_dir)
113
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
114
+ dir
115
+ end
116
+
117
+ # Ensure the jobs directory exists
118
+ def self.ensure_jobs_dir(project_dir = Dir.pwd)
119
+ dir = jobs_dir(project_dir)
120
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
121
+ dir
122
+ end
123
+
124
+ # Ensure the JSON storage directory exists
125
+ def self.ensure_json_storage_dir(project_dir = Dir.pwd)
126
+ dir = json_storage_dir(project_dir)
127
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
128
+ dir
129
+ end
130
+ end
131
+ end
data/lib/aidp/config.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
+ require_relative "config/paths"
4
5
 
5
6
  module Aidp
6
7
  # Configuration management for both execute and analyze modes
@@ -165,7 +166,7 @@ module Aidp
165
166
  }.freeze
166
167
 
167
168
  def self.load(project_dir = Dir.pwd)
168
- config_file = File.join(project_dir, ".aidp", "aidp.yml")
169
+ config_file = ConfigPaths.config_file(project_dir)
169
170
 
170
171
  if File.exist?(config_file)
171
172
  load_yaml_config(config_file)
@@ -244,15 +245,15 @@ module Aidp
244
245
 
245
246
  # Check if configuration file exists
246
247
  def self.config_exists?(project_dir = Dir.pwd)
247
- File.exist?(File.join(project_dir, ".aidp", "aidp.yml"))
248
+ ConfigPaths.config_exists?(project_dir)
248
249
  end
249
250
 
250
251
  # Create example configuration file
251
252
  def self.create_example_config(project_dir = Dir.pwd)
252
- config_path = File.join(project_dir, ".aidp", "aidp.yml")
253
+ config_path = ConfigPaths.config_file(project_dir)
253
254
  return false if File.exist?(config_path)
254
255
 
255
- FileUtils.mkdir_p(File.dirname(config_path))
256
+ ConfigPaths.ensure_config_dir(project_dir)
256
257
 
257
258
  example_config = {
258
259
  harness: {
@@ -283,6 +284,19 @@ module Aidp
283
284
  true
284
285
  end
285
286
 
287
+ # Expose path methods for convenience
288
+ def self.config_file(project_dir = Dir.pwd)
289
+ ConfigPaths.config_file(project_dir)
290
+ end
291
+
292
+ def self.config_dir(project_dir = Dir.pwd)
293
+ ConfigPaths.config_dir(project_dir)
294
+ end
295
+
296
+ def self.aidp_dir(project_dir = Dir.pwd)
297
+ ConfigPaths.aidp_dir(project_dir)
298
+ end
299
+
286
300
  private_class_method def self.load_yaml_config(config_file)
287
301
  YAML.load_file(config_file) || {}
288
302
  rescue => e
@@ -1425,7 +1425,7 @@ module Aidp
1425
1425
  end
1426
1426
 
1427
1427
  # Get completion confidence level
1428
- def get_completion_confidence(completion_info)
1428
+ def completion_confidence(completion_info)
1429
1429
  return 0.0 unless completion_info && completion_info[:confidence]
1430
1430
 
1431
1431
  completion_info[:confidence]
@@ -1433,23 +1433,23 @@ module Aidp
1433
1433
 
1434
1434
  # Check if completion is high confidence
1435
1435
  def high_confidence_completion?(completion_info)
1436
- get_completion_confidence(completion_info) >= 0.8
1436
+ completion_confidence(completion_info) >= 0.8
1437
1437
  end
1438
1438
 
1439
1439
  # Check if completion is medium confidence
1440
1440
  def medium_confidence_completion?(completion_info)
1441
- confidence = get_completion_confidence(completion_info)
1441
+ confidence = completion_confidence(completion_info)
1442
1442
  confidence >= 0.5 && confidence < 0.8
1443
1443
  end
1444
1444
 
1445
1445
  # Check if completion is low confidence
1446
1446
  def low_confidence_completion?(completion_info)
1447
- confidence = get_completion_confidence(completion_info)
1447
+ confidence = completion_confidence(completion_info)
1448
1448
  confidence > 0.0 && confidence < 0.5
1449
1449
  end
1450
1450
 
1451
1451
  # Get next actions from completion info
1452
- def get_next_actions(completion_info)
1452
+ def next_actions(completion_info)
1453
1453
  return [] unless completion_info && completion_info[:next_actions]
1454
1454
 
1455
1455
  completion_info[:next_actions]
@@ -1495,7 +1495,7 @@ module Aidp
1495
1495
  end
1496
1496
 
1497
1497
  # Get progress status description
1498
- def get_progress_status_description(completion_info)
1498
+ def progress_status_description(completion_info)
1499
1499
  return "unknown" unless completion_info
1500
1500
 
1501
1501
  case completion_info[:progress_status]