aidp 0.9.6 → 0.10.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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aidp/analyze/error_handler.rb +4 -2
  3. data/lib/aidp/{analysis → analyze}/kb_inspector.rb +106 -89
  4. data/lib/aidp/analyze/prioritizer.rb +3 -2
  5. data/lib/aidp/analyze/ruby_maat_integration.rb +20 -3
  6. data/lib/aidp/analyze/runner.rb +27 -9
  7. data/lib/aidp/{analysis → analyze}/seams.rb +1 -1
  8. data/lib/aidp/analyze/steps.rb +7 -7
  9. data/lib/aidp/{analysis → analyze}/tree_sitter_grammar_loader.rb +22 -5
  10. data/lib/aidp/{analysis → analyze}/tree_sitter_scan.rb +32 -15
  11. data/lib/aidp/cli/first_run_wizard.rb +37 -28
  12. data/lib/aidp/cli/jobs_command.rb +37 -18
  13. data/lib/aidp/cli/terminal_io.rb +3 -3
  14. data/lib/aidp/cli.rb +131 -63
  15. data/lib/aidp/execute/runner.rb +27 -9
  16. data/lib/aidp/execute/steps.rb +18 -18
  17. data/lib/aidp/execute/workflow_selector.rb +36 -21
  18. data/lib/aidp/harness/enhanced_runner.rb +3 -3
  19. data/lib/aidp/harness/provider_factory.rb +3 -1
  20. data/lib/aidp/harness/provider_manager.rb +34 -15
  21. data/lib/aidp/harness/runner.rb +24 -5
  22. data/lib/aidp/harness/simple_user_interface.rb +19 -4
  23. data/lib/aidp/harness/status_display.rb +121 -104
  24. data/lib/aidp/harness/ui/enhanced_tui.rb +5 -5
  25. data/lib/aidp/harness/ui/error_handler.rb +3 -2
  26. data/lib/aidp/harness/ui/frame_manager.rb +52 -32
  27. data/lib/aidp/harness/ui/navigation/main_menu.rb +23 -14
  28. data/lib/aidp/harness/ui/progress_display.rb +28 -5
  29. data/lib/aidp/harness/ui/status_widget.rb +17 -8
  30. data/lib/aidp/harness/ui/workflow_controller.rb +25 -9
  31. data/lib/aidp/harness/user_interface.rb +341 -328
  32. data/lib/aidp/provider_manager.rb +10 -6
  33. data/lib/aidp/providers/anthropic.rb +3 -3
  34. data/lib/aidp/providers/base.rb +20 -1
  35. data/lib/aidp/providers/cursor.rb +6 -8
  36. data/lib/aidp/providers/gemini.rb +3 -3
  37. data/lib/aidp/providers/github_copilot.rb +264 -0
  38. data/lib/aidp/providers/opencode.rb +6 -8
  39. data/lib/aidp/version.rb +1 -1
  40. data/lib/aidp.rb +4 -4
  41. metadata +6 -6
  42. data/lib/aidp/analyze/progress_visualizer.rb +0 -314
@@ -15,7 +15,8 @@ module Aidp
15
15
  def initialize(ui_components = {})
16
16
  super()
17
17
  @frame = ui_components[:frame] || TTY::Box
18
- @formatter = ui_components[:formatter] || FrameFormatter.new
18
+ @formatter = ui_components[:formatter] || (defined?(FrameFormatter) ? FrameFormatter.new : nil)
19
+ @output = ui_components[:output]
19
20
  @frame_open = false
20
21
  @frame_stack = []
21
22
  @frame_history = []
@@ -26,6 +27,18 @@ module Aidp
26
27
  }
27
28
  end
28
29
 
30
+ private
31
+
32
+ def display_message(message)
33
+ if @output
34
+ @output.say(message)
35
+ else
36
+ puts message
37
+ end
38
+ end
39
+
40
+ public
41
+
29
42
  def open_frame(frame_type, title, frame_data = nil, &block)
30
43
  validate_frame_type(frame_type)
31
44
  validate_title(title)
@@ -45,9 +58,9 @@ module Aidp
45
58
 
46
59
  if block_given?
47
60
  content = yield
48
- puts TTY::Box.frame(formatted_title, content, width: 80)
61
+ display_message(@frame.frame(formatted_title, content, width: 80))
49
62
  else
50
- puts TTY::Box.frame(formatted_title, width: 80)
63
+ display_message(@frame.frame(formatted_title, width: 80))
51
64
  end
52
65
  rescue InvalidFrameError => e
53
66
  raise e
@@ -60,11 +73,18 @@ module Aidp
60
73
  validate_title(title)
61
74
  raise DisplayError, "No parent frame exists for nesting" if @frame_stack.empty?
62
75
 
63
- formatted_title = @formatter.format_frame_title(frame_type, title, frame_data)
76
+ formatted_title = @formatter ? @formatter.format_frame_title(frame_type, title, frame_data) : title.to_s
64
77
  @frame_stack.push({type: frame_type, title: title, data: frame_data})
65
- puts TTY::Box.frame(formatted_title, width: 80) do
66
- yield if block_given?
78
+
79
+ frame_result = @frame.frame(formatted_title, width: 80) do
80
+ if block_given?
81
+ yield || ""
82
+ else
83
+ ""
84
+ end
67
85
  end
86
+
87
+ display_message(frame_result)
68
88
  rescue InvalidFrameError => e
69
89
  raise e
70
90
  rescue => e
@@ -98,7 +118,7 @@ module Aidp
98
118
  when :failed then "Failed"
99
119
  else status.to_s.capitalize
100
120
  end
101
- puts "Status: #{status_text}"
121
+ display_message("Status: #{status_text}")
102
122
  end
103
123
 
104
124
  def current_frame_status
@@ -125,18 +145,18 @@ module Aidp
125
145
  end
126
146
 
127
147
  def display_frame_summary
128
- puts "\n📊 Frame Summary"
129
- puts "=" * 50
148
+ display_message("\n📊 Frame Summary")
149
+ display_message("=" * 50)
130
150
 
131
151
  if @frame_stats[:total_frames] == 0
132
- puts "No frames used"
152
+ display_message("No frames used")
133
153
  return
134
154
  end
135
155
 
136
- puts "Total Frames: #{@frame_stats[:total_frames]}"
156
+ display_message("Total Frames: #{@frame_stats[:total_frames]}")
137
157
 
138
158
  unless @frame_stats[:frame_types].empty?
139
- puts "\nFrame Types:"
159
+ display_message("\nFrame Types:")
140
160
  @frame_stats[:frame_types].each do |type, count|
141
161
  emoji = case type
142
162
  when :section then "📋"
@@ -145,12 +165,12 @@ module Aidp
145
165
  when :step then "🔧"
146
166
  else "📋"
147
167
  end
148
- puts " #{emoji} #{type.to_s.capitalize}: #{count}"
168
+ display_message(" #{emoji} #{type.to_s.capitalize}: #{count}")
149
169
  end
150
170
  end
151
171
 
152
172
  unless @frame_stats[:status_counts].empty?
153
- puts "\nStatus Counts:"
173
+ display_message("\nStatus Counts:")
154
174
  @frame_stats[:status_counts].each do |status, count|
155
175
  status_emoji = case status
156
176
  when :running then "🔄"
@@ -158,12 +178,12 @@ module Aidp
158
178
  when :failed then "❌"
159
179
  else "❓"
160
180
  end
161
- puts " #{status_emoji} #{status.to_s.capitalize}: #{count}"
181
+ display_message(" #{status_emoji} #{status.to_s.capitalize}: #{count}")
162
182
  end
163
183
  end
164
184
 
165
- puts "\nCurrent Frame Depth: #{@frame_depth}"
166
- puts "Frames in History: #{@frame_history.length}"
185
+ display_message("\nCurrent Frame Depth: #{@frame_depth}")
186
+ display_message("Frames in History: #{@frame_history.length}")
167
187
  end
168
188
 
169
189
  def clear_frame_history
@@ -182,13 +202,13 @@ module Aidp
182
202
  validate_title(title)
183
203
  raise ArgumentError, "Block required for frame_with_block" unless block_given?
184
204
 
185
- formatted_title = @formatter.format_frame_title(frame_type, title, frame_data)
205
+ formatted_title = @formatter ? @formatter.format_frame_title(frame_type, title, frame_data) : title.to_s
186
206
  @frame_open = true
187
207
  @frame_stack.push({type: frame_type, title: title, data: frame_data})
188
208
 
189
209
  begin
190
210
  content = yield
191
- puts TTY::Box.frame(formatted_title, content, width: 80)
211
+ display_message(@frame.frame(formatted_title, content, width: 80))
192
212
 
193
213
  @frame_open = false
194
214
  @frame_stack.pop unless @frame_stack.empty?
@@ -207,7 +227,7 @@ module Aidp
207
227
  def divider(text)
208
228
  validate_text(text)
209
229
 
210
- formatted_text = @formatter.format_divider_text(text)
230
+ formatted_text = @formatter ? @formatter.format_divider_text(text) : text
211
231
  @frame.divider(formatted_text)
212
232
  rescue => e
213
233
  raise DisplayError, "Failed to create divider: #{e.message}"
@@ -216,12 +236,12 @@ module Aidp
216
236
  def section(title, &block)
217
237
  validate_title(title)
218
238
 
219
- formatted_title = @formatter.format_section_title(title)
239
+ formatted_title = @formatter ? @formatter.format_section_title(title) : "📋 #{title}"
220
240
  if block_given?
221
241
  content = yield
222
- puts TTY::Box.frame(formatted_title, content, width: 80)
242
+ display_message(@frame.frame(formatted_title, content, width: 80))
223
243
  else
224
- puts TTY::Box.frame(formatted_title, width: 80)
244
+ display_message(@frame.frame(formatted_title, width: 80))
225
245
  end
226
246
  rescue => e
227
247
  raise DisplayError, "Failed to create section: #{e.message}"
@@ -230,10 +250,10 @@ module Aidp
230
250
  def subsection(title, &block)
231
251
  validate_title(title)
232
252
 
233
- formatted_title = @formatter.format_subsection_title(title)
234
- puts TTY::Box.frame(formatted_title, width: 80) do
253
+ formatted_title = @formatter ? @formatter.format_subsection_title(title) : "📝 #{title}"
254
+ display_message(@frame.frame(formatted_title, width: 80) do
235
255
  yield if block_given?
236
- end
256
+ end)
237
257
  rescue => e
238
258
  raise DisplayError, "Failed to create subsection: #{e.message}"
239
259
  end
@@ -241,10 +261,10 @@ module Aidp
241
261
  def workflow_frame(workflow_name, &block)
242
262
  validate_workflow_name(workflow_name)
243
263
 
244
- formatted_title = @formatter.format_workflow_title(workflow_name)
245
- puts TTY::Box.frame(formatted_title, width: 80) do
264
+ formatted_title = @formatter ? @formatter.format_workflow_title(workflow_name) : "⚙️ #{workflow_name}"
265
+ display_message(@frame.frame(formatted_title, width: 80) do
246
266
  yield if block_given?
247
- end
267
+ end)
248
268
  rescue => e
249
269
  raise DisplayError, "Failed to create workflow frame: #{e.message}"
250
270
  end
@@ -252,10 +272,10 @@ module Aidp
252
272
  def step_frame(step_name, step_number, total_steps, &block)
253
273
  validate_step_inputs(step_name, step_number, total_steps)
254
274
 
255
- formatted_title = @formatter.format_step_title(step_name, step_number, total_steps)
256
- puts TTY::Box.frame(formatted_title, width: 80) do
275
+ formatted_title = @formatter ? @formatter.format_step_title(step_name, step_number, total_steps) : "🔧 #{step_name} (#{step_number}/#{total_steps})"
276
+ display_message(@frame.frame(formatted_title, width: 80) do
257
277
  yield if block_given?
258
- end
278
+ end)
259
279
  rescue => e
260
280
  raise DisplayError, "Failed to create step frame: #{e.message}"
261
281
  end
@@ -23,6 +23,7 @@ module Aidp
23
23
  @pastel = Pastel.new
24
24
  @formatter = ui_components[:formatter] || MenuFormatter.new
25
25
  @state_manager = ui_components[:state_manager] || MenuState.new
26
+ @output = ui_components[:output]
26
27
  @menu_items = []
27
28
  @current_level = 0
28
29
  @breadcrumb = []
@@ -69,9 +70,9 @@ module Aidp
69
70
 
70
71
  # Add navigation instructions if items exist
71
72
  if menu_items.any?
72
- puts "Use arrow keys to navigate, Enter to select"
73
+ display_message "Use arrow keys to navigate, Enter to select"
73
74
  else
74
- puts "No options available"
75
+ display_message "No options available"
75
76
  end
76
77
  rescue => e
77
78
  raise NavigationError, "Failed to display menu: #{e.message}"
@@ -133,7 +134,7 @@ module Aidp
133
134
  if index >= 0 && index < menu_items.length
134
135
  return menu_items[index]
135
136
  else
136
- puts "Invalid selection"
137
+ display_message "Invalid selection"
137
138
  next
138
139
  end
139
140
  else
@@ -142,14 +143,14 @@ module Aidp
142
143
  if selected_item
143
144
  return selected_item
144
145
  else
145
- puts "Invalid selection"
146
+ display_message "Invalid selection"
146
147
  next
147
148
  end
148
149
  end
149
150
  end
150
151
 
151
152
  # If we get here, we've exceeded max attempts
152
- puts "Too many invalid attempts. Returning first option."
153
+ display_message "Too many invalid attempts. Returning first option."
153
154
  menu_items.first
154
155
  end
155
156
 
@@ -194,10 +195,10 @@ module Aidp
194
195
  end
195
196
 
196
197
  def display_navigation_help
197
- puts "Navigation Help"
198
- puts "Use arrow keys to navigate"
199
- puts "Press Enter to select"
200
- puts "Press Escape to go back"
198
+ display_message "Navigation Help"
199
+ display_message "Use arrow keys to navigate"
200
+ display_message "Press Enter to select"
201
+ display_message "Press Escape to go back"
201
202
  end
202
203
 
203
204
  # Additional methods expected by tests
@@ -220,15 +221,23 @@ module Aidp
220
221
  # Make display_breadcrumb public for testing
221
222
  def display_breadcrumb
222
223
  if @breadcrumb.empty?
223
- puts "Home"
224
+ display_message "Home"
224
225
  else
225
226
  breadcrumb_text = @formatter.format_breadcrumb(@breadcrumb)
226
- puts(breadcrumb_text)
227
+ display_message(breadcrumb_text)
227
228
  end
228
229
  end
229
230
 
230
231
  private
231
232
 
233
+ def display_message(message)
234
+ if @output
235
+ @output.say(message)
236
+ else
237
+ puts message
238
+ end
239
+ end
240
+
232
241
  def validate_menu_item(item)
233
242
  raise InvalidMenuError, "Menu item must be a MenuItem" unless item.is_a?(MenuItem)
234
243
  end
@@ -243,14 +252,14 @@ module Aidp
243
252
 
244
253
  def display_menu_header(title)
245
254
  formatted_title = @formatter.format_menu_title(title)
246
- puts(formatted_title)
247
- puts(@formatter.format_separator)
255
+ display_message(formatted_title)
256
+ display_message(@formatter.format_separator)
248
257
  end
249
258
 
250
259
  def display_menu_items
251
260
  @menu_items.each_with_index do |item, index|
252
261
  formatted_item = @formatter.format_menu_item(item, index + 1)
253
- puts(formatted_item)
262
+ display_message(formatted_item)
254
263
  end
255
264
  end
256
265
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "tty-progressbar"
4
+ require "tty-prompt"
4
5
  require "pastel"
5
6
  require_relative "base"
6
7
 
@@ -24,7 +25,8 @@ module Aidp
24
25
  @auto_refresh_enabled = false
25
26
  @refresh_interval = 1.0
26
27
  @refresh_thread = nil
27
- @output = ui_components[:output] || $stdout
28
+ @output = ui_components[:output]
29
+ @prompt = ui_components[:prompt] || TTY::Prompt.new
28
30
  @spinner_class = begin
29
31
  ui_components[:spinner] || TTY::Spinner
30
32
  rescue
@@ -115,7 +117,7 @@ module Aidp
115
117
  raise ArgumentError, "Progress items must be an array" unless progress_items.is_a?(Array)
116
118
 
117
119
  if progress_items.empty?
118
- @output.puts @pastel.dim("No progress items to display.")
120
+ display_message(@pastel.dim("No progress items to display."), type: :muted)
119
121
  return
120
122
  end
121
123
 
@@ -199,7 +201,7 @@ module Aidp
199
201
  " (Step: #{progress_data[:current_step]})"
200
202
  task_id = progress_data[:id] ? "[#{progress_data[:id]}] " : ""
201
203
 
202
- @output.puts "#{task_id}#{progress}% #{message}#{step_info}"
204
+ display_message("#{task_id}#{progress}% #{message}#{step_info}", type: :info)
203
205
  end
204
206
 
205
207
  def display_detailed_progress(progress_data)
@@ -210,13 +212,13 @@ module Aidp
210
212
  started_at = progress_data[:started_at] ? progress_data[:started_at].strftime("%H:%M:%S") : "N/A"
211
213
  eta = progress_data[:eta] || "N/A"
212
214
 
213
- @output.puts "Progress: #{progress}% - #{message} (Step: #{current_step}/#{total_steps}, Started: #{started_at}, ETA: #{eta})"
215
+ display_message("Progress: #{progress}% - #{message} (Step: #{current_step}/#{total_steps}, Started: #{started_at}, ETA: #{eta})", type: :info)
214
216
  end
215
217
 
216
218
  def display_minimal_progress(progress_data)
217
219
  progress = progress_data[:progress] || 0
218
220
  message = progress_data[:message] || "Processing..."
219
- @output.puts "#{@pastel.blue("Progress:")} #{progress}% - #{message}"
221
+ display_message("Progress: #{progress}% - #{message}", type: :info)
220
222
  end
221
223
 
222
224
  def create_progress_bar(progress)
@@ -251,6 +253,27 @@ module Aidp
251
253
  timestamp: Time.now
252
254
  }
253
255
  end
256
+
257
+ private
258
+
259
+ def display_message(message, type: :info)
260
+ if @prompt
261
+ color = case type
262
+ when :error then :red
263
+ when :success then :green
264
+ when :warning then :yellow
265
+ when :info then :blue
266
+ when :highlight then :cyan
267
+ when :muted then :bright_black
268
+ else :white
269
+ end
270
+ @prompt.say(message, color: color)
271
+ elsif @output
272
+ @output.puts(message)
273
+ else
274
+ puts(message)
275
+ end
276
+ end
254
277
  end
255
278
 
256
279
  # Formats progress display text
@@ -18,6 +18,7 @@ module Aidp
18
18
  @spinner = ui_components[:spinner] || TTY::Spinner
19
19
  @pastel = Pastel.new
20
20
  @formatter = ui_components[:formatter] || StatusFormatter.new
21
+ @output = ui_components[:output]
21
22
  @status_history = []
22
23
  @current_spinner = nil
23
24
  @spinner_active = false
@@ -56,7 +57,7 @@ module Aidp
56
57
  validate_message(message)
57
58
 
58
59
  formatted_message = @formatter.format_success_message(message)
59
- puts(formatted_message)
60
+ display_message(formatted_message)
60
61
  rescue => e
61
62
  raise DisplayError, "Failed to show success status: #{e.message}"
62
63
  end
@@ -65,7 +66,7 @@ module Aidp
65
66
  validate_message(message)
66
67
 
67
68
  formatted_message = @formatter.format_error_message(message)
68
- puts(formatted_message)
69
+ display_message(formatted_message)
69
70
  rescue => e
70
71
  raise DisplayError, "Failed to show error status: #{e.message}"
71
72
  end
@@ -74,7 +75,7 @@ module Aidp
74
75
  validate_message(message)
75
76
 
76
77
  formatted_message = @formatter.format_warning_message(message)
77
- puts(formatted_message)
78
+ display_message(formatted_message)
78
79
  rescue => e
79
80
  raise DisplayError, "Failed to show warning status: #{e.message}"
80
81
  end
@@ -181,6 +182,14 @@ module Aidp
181
182
 
182
183
  private
183
184
 
185
+ def display_message(message)
186
+ if @output
187
+ @output.say(message)
188
+ else
189
+ puts message
190
+ end
191
+ end
192
+
184
193
  def validate_message(message)
185
194
  raise InvalidStatusError, "Message cannot be empty" if message.to_s.strip.empty?
186
195
  end
@@ -211,22 +220,22 @@ module Aidp
211
220
  end
212
221
 
213
222
  def display_loading_status(message)
214
- puts("⏳ #{message}")
223
+ display_message("⏳ #{message}")
215
224
  end
216
225
 
217
226
  def display_success_status(message)
218
- puts("✅ #{message}")
227
+ display_message("✅ #{message}")
219
228
  end
220
229
 
221
230
  def display_error_status(message, error_data)
222
- puts("❌ #{message}")
231
+ display_message("❌ #{message}")
223
232
  if error_data && error_data[:message]
224
- puts(" #{error_data[:message]}")
233
+ display_message(" #{error_data[:message]}")
225
234
  end
226
235
  end
227
236
 
228
237
  def display_warning_status(message)
229
- puts("⚠️ #{message}")
238
+ display_message("⚠️ #{message}")
230
239
  end
231
240
  end
232
241
 
@@ -27,6 +27,7 @@ module Aidp
27
27
  @status_manager = ui_components[:status_manager] || StatusManager.new
28
28
  @frame_manager = ui_components[:frame_manager] || FrameManager.new
29
29
  @formatter = ui_components[:formatter] || WorkflowControllerFormatter.new
30
+ @output = ui_components[:output]
30
31
 
31
32
  @current_state = :running
32
33
  @state_history = []
@@ -187,8 +188,23 @@ module Aidp
187
188
  @control_thread = nil
188
189
  end
189
190
 
191
+ # Testing helper method to set state
192
+ def set_state_for_testing(state)
193
+ @control_mutex.synchronize do
194
+ @current_state = state
195
+ end
196
+ end
197
+
190
198
  private
191
199
 
200
+ def display_message(message)
201
+ if @output
202
+ @output.say(message)
203
+ else
204
+ puts message
205
+ end
206
+ end
207
+
192
208
  def validate_state_transition(action)
193
209
  case action
194
210
  when :pause
@@ -229,19 +245,19 @@ module Aidp
229
245
  end
230
246
 
231
247
  def display_status_info(status)
232
- puts("Current State: #{@formatter.format_state(status[:state])}")
233
- puts("State Name: #{status[:state_name]}")
248
+ display_message("Current State: #{@formatter.format_state(status[:state])}")
249
+ display_message("State Name: #{status[:state_name]}")
234
250
 
235
251
  if status[:pause_time]
236
- puts("Paused Since: #{status[:pause_time]}")
252
+ display_message("Paused Since: #{status[:pause_time]}")
237
253
  end
238
254
 
239
- puts("\nAvailable Actions:")
240
- puts(" Pause: #{status[:can_pause] ? "Yes" : "No"}")
241
- puts(" Resume: #{status[:can_resume] ? "Yes" : "No"}")
242
- puts(" Cancel: #{status[:can_cancel] ? "Yes" : "No"}")
243
- puts(" Stop: #{status[:can_stop] ? "Yes" : "No"}")
244
- puts(" Complete: #{status[:can_complete] ? "Yes" : "No"}")
255
+ display_message("\nAvailable Actions:")
256
+ display_message(" Pause: #{status[:can_pause] ? "Yes" : "No"}")
257
+ display_message(" Resume: #{status[:can_resume] ? "Yes" : "No"}")
258
+ display_message(" Cancel: #{status[:can_cancel] ? "Yes" : "No"}")
259
+ display_message(" Stop: #{status[:can_stop] ? "Yes" : "No"}")
260
+ display_message(" Complete: #{status[:can_complete] ? "Yes" : "No"}")
245
261
  end
246
262
 
247
263
  def control_interface_loop