aidp 0.9.5 → 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 +33 -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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12e70b298f80b189c33c7db0a67027086a8441a4fdd5c0ae45045b491c5ca825
4
- data.tar.gz: 1f6445fd7ed137c78e4c6000aac0418d15d00856beea9db9afd4d843aad11b4a
3
+ metadata.gz: 2358e7598866519630169447a6ba6fd60704571ca81afc8b6e9ec856916cacc1
4
+ data.tar.gz: a33aba24daef5e8492d36954b4a36c44fff738dc9b9a54762c81ac28708f2cfa
5
5
  SHA512:
6
- metadata.gz: 5fab5f97f7874314f0991cd75260175b7aab803d199bb25ab1223fae0d280a7b6a97a11ff160d90b5bc263fc312bc64bb3c95c32e34e31d960c8ab7a403a8778
7
- data.tar.gz: 9bf30d28286d09e7d25bb23bd3109e422e91518f7e46113949ed27a87cc722046dc8cfd881f00279c6f849173a6a9f4c503d07b519d7cd7ca53d7cd53a71cf67
6
+ metadata.gz: c37341e9a7a26a5bb23141133f08a67f26f5a88dcf1d5e12b36f638359f6e738cb4ffd86e15b0d8cc1c8282ecea31803faa6b0ff82c55bb1d9b63aaf987cc82b
7
+ data.tar.gz: 8b78de9305399181898a47542880840487ab667ccb7e151c4ff52d497437a5fd780440844c3b2585d6356c91c25378c7e73b15eebb1fa8f5e321bb46d4a79773
@@ -8,7 +8,8 @@ module Aidp
8
8
  class ErrorHandler
9
9
  attr_reader :logger, :error_counts, :recovery_strategies
10
10
 
11
- def initialize(log_file: nil, verbose: false)
11
+ def initialize(log_file: nil, verbose: false, output: nil)
12
+ @output = output
12
13
  @logger = setup_logger(log_file, verbose)
13
14
  @error_counts = Hash.new(0)
14
15
  @recovery_strategies = setup_recovery_strategies
@@ -87,7 +88,8 @@ module Aidp
87
88
  private
88
89
 
89
90
  def setup_logger(log_file, verbose)
90
- logger = Logger.new(log_file || $stdout)
91
+ output_stream = log_file || @output || $stdout
92
+ logger = Logger.new(output_stream)
91
93
  logger.level = verbose ? Logger::DEBUG : Logger::INFO
92
94
  logger.formatter = proc do |severity, datetime, progname, msg|
93
95
  "#{datetime.strftime("%Y-%m-%d %H:%M:%S")} [#{severity}] #{msg}\n"
@@ -2,15 +2,32 @@
2
2
 
3
3
  require "json"
4
4
  require "tty-box"
5
+ require "tty-prompt"
5
6
 
6
7
  module Aidp
7
- module Analysis
8
+ module Analyze
8
9
  class KBInspector
9
- def initialize(kb_dir = ".aidp/kb")
10
+ def initialize(kb_dir = ".aidp/kb", prompt: TTY::Prompt.new)
10
11
  @kb_dir = File.expand_path(kb_dir)
12
+ @prompt = prompt
11
13
  @data = load_kb_data
12
14
  end
13
15
 
16
+ # Helper method for consistent message display using TTY::Prompt
17
+ def display_message(message, type: :info)
18
+ color = case type
19
+ when :error then :red
20
+ when :success then :green
21
+ when :warning then :yellow
22
+ when :info then :blue
23
+ when :highlight then :cyan
24
+ when :muted then :bright_black
25
+ else :white
26
+ end
27
+
28
+ @prompt.say(message, color: color)
29
+ end
30
+
14
31
  def show(type, format: "summary")
15
32
  case type
16
33
  when "seams"
@@ -28,8 +45,8 @@ module Aidp
28
45
  when "summary"
29
46
  show_summary(format)
30
47
  else
31
- puts "Unknown KB type: #{type}"
32
- puts "Available types: seams, hotspots, cycles, apis, symbols, imports, summary"
48
+ display_message("Unknown KB type: #{type}", type: :error)
49
+ display_message("Available types: seams, hotspots, cycles, apis, symbols, imports, summary", type: :info)
33
50
  end
34
51
  end
35
52
 
@@ -42,8 +59,8 @@ module Aidp
42
59
  when "cycles"
43
60
  generate_cycle_graph(format, output)
44
61
  else
45
- puts "Unknown graph type: #{type}"
46
- puts "Available types: imports, calls, cycles"
62
+ display_message("Unknown graph type: #{type}", type: :error)
63
+ display_message("Available types: imports, calls, cycles", type: :info)
47
64
  end
48
65
  end
49
66
 
@@ -73,7 +90,7 @@ module Aidp
73
90
  border: :thick,
74
91
  padding: [1, 2]
75
92
  )
76
- puts box
93
+ display_message(box)
77
94
  end
78
95
 
79
96
  def load_kb_data
@@ -87,7 +104,7 @@ module Aidp
87
104
  rescue JSON::ParserError => e
88
105
  # Suppress warnings in test mode to avoid CI failures
89
106
  unless ENV["RACK_ENV"] == "test" || defined?(RSpec)
90
- puts "Warning: Could not parse #{file_path}: #{e.message}"
107
+ display_message("Warning: Could not parse #{file_path}: #{e.message}", type: :warn)
91
108
  end
92
109
  data[type.to_sym] = []
93
110
  end
@@ -100,42 +117,42 @@ module Aidp
100
117
  end
101
118
 
102
119
  def show_summary(_format)
103
- puts "\n📊 Knowledge Base Summary"
104
- puts "=" * 50
105
-
106
- puts "📁 KB Directory: #{@kb_dir}"
107
- puts "📄 Files analyzed: #{count_files}"
108
- puts "🏗️ Symbols: #{@data[:symbols]&.length || 0}"
109
- puts "📦 Imports: #{@data[:imports]&.length || 0}"
110
- puts "🔗 Calls: #{@data[:calls]&.length || 0}"
111
- puts "📏 Metrics: #{@data[:metrics]&.length || 0}"
112
- puts "🔧 Seams: #{@data[:seams]&.length || 0}"
113
- puts "🔥 Hotspots: #{@data[:hotspots]&.length || 0}"
114
- puts "🧪 Tests: #{@data[:tests]&.length || 0}"
115
- puts "🔄 Cycles: #{@data[:cycles]&.length || 0}"
120
+ display_message("\n📊 Knowledge Base Summary", type: :highlight)
121
+ display_message("=" * 50, type: :info)
122
+
123
+ display_message("📁 KB Directory: #{@kb_dir}", type: :info)
124
+ display_message("📄 Files analyzed: #{count_files}", type: :info)
125
+ display_message("🏗️ Symbols: #{@data[:symbols]&.length || 0}", type: :info)
126
+ display_message("📦 Imports: #{@data[:imports]&.length || 0}", type: :info)
127
+ display_message("🔗 Calls: #{@data[:calls]&.length || 0}", type: :info)
128
+ display_message("📏 Metrics: #{@data[:metrics]&.length || 0}", type: :info)
129
+ display_message("🔧 Seams: #{@data[:seams]&.length || 0}", type: :info)
130
+ display_message("🔥 Hotspots: #{@data[:hotspots]&.length || 0}", type: :info)
131
+ display_message("🧪 Tests: #{@data[:tests]&.length || 0}", type: :info)
132
+ display_message("🔄 Cycles: #{@data[:cycles]&.length || 0}", type: :info)
116
133
 
117
134
  if @data[:seams]&.any?
118
- puts "\n🔧 Seam Types:"
135
+ display_message("\n🔧 Seam Types:", type: :info)
119
136
  seam_types = @data[:seams].group_by { |s| s[:kind] }
120
137
  seam_types.each do |type, seams|
121
- puts " #{type}: #{seams.length}"
138
+ display_message(" #{type}: #{seams.length}", type: :info)
122
139
  end
123
140
  end
124
141
 
125
142
  if @data[:hotspots]&.any?
126
- puts "\n🔥 Top 5 Hotspots:"
143
+ display_message("\n🔥 Top 5 Hotspots:", type: :info)
127
144
  @data[:hotspots].first(5).each_with_index do |hotspot, i|
128
- puts " #{i + 1}. #{hotspot[:file]}:#{hotspot[:method]} (score: #{hotspot[:score]})"
145
+ display_message(" #{i + 1}. #{hotspot[:file]}:#{hotspot[:method]} (score: #{hotspot[:score]})", type: :info)
129
146
  end
130
147
  end
131
148
  end
132
149
 
133
150
  def show_seams(format)
134
- return puts "No seams data available" unless @data[:seams]&.any?
151
+ return display_message("No seams data available") unless @data[:seams]&.any?
135
152
 
136
153
  case format
137
154
  when "json"
138
- puts JSON.pretty_generate(@data[:seams])
155
+ display_message(JSON.pretty_generate(@data[:seams]))
139
156
  when "table"
140
157
  show_seams_table
141
158
  else
@@ -144,8 +161,8 @@ module Aidp
144
161
  end
145
162
 
146
163
  def show_seams_table
147
- puts "\n🔧 Seams Analysis"
148
- puts "=" * 80
164
+ display_message("\n🔧 Seams Analysis")
165
+ display_message("=" * 80)
149
166
 
150
167
  create_table(
151
168
  ["Type", "File", "Line", "Symbol", "Suggestion"],
@@ -162,34 +179,34 @@ module Aidp
162
179
  end
163
180
 
164
181
  def show_seams_summary
165
- puts "\n🔧 Seams Analysis"
166
- puts "=" * 50
182
+ display_message("\n🔧 Seams Analysis")
183
+ display_message("=" * 50)
167
184
 
168
185
  seam_types = @data[:seams].group_by { |s| s[:kind] }
169
186
 
170
187
  seam_types.each do |type, seams|
171
- puts "\n📌 #{type.upcase} (#{seams.length} found)"
172
- puts "-" * 30
188
+ display_message("\n📌 #{type.upcase} (#{seams.length} found)")
189
+ display_message("-" * 30)
173
190
 
174
191
  seams.first(10).each do |seam|
175
- puts " #{seam[:file]}:#{seam[:line]}"
176
- puts " Symbol: #{seam[:symbol_id]&.split(":")&.last}"
177
- puts " Suggestion: #{seam[:suggestion]}"
178
- puts
192
+ display_message(" #{seam[:file]}:#{seam[:line]}")
193
+ display_message(" Symbol: #{seam[:symbol_id]&.split(":")&.last}")
194
+ display_message(" Suggestion: #{seam[:suggestion]}")
195
+ display_message("")
179
196
  end
180
197
 
181
198
  if seams.length > 10
182
- puts " ... and #{seams.length - 10} more"
199
+ display_message(" ... and #{seams.length - 10} more")
183
200
  end
184
201
  end
185
202
  end
186
203
 
187
204
  def show_hotspots(format)
188
- return puts "No hotspots data available" unless @data[:hotspots]&.any?
205
+ return display_message("No hotspots data available") unless @data[:hotspots]&.any?
189
206
 
190
207
  case format
191
208
  when "json"
192
- puts JSON.pretty_generate(@data[:hotspots])
209
+ display_message(JSON.pretty_generate(@data[:hotspots]))
193
210
  when "table"
194
211
  show_hotspots_table
195
212
  else
@@ -198,8 +215,8 @@ module Aidp
198
215
  end
199
216
 
200
217
  def show_hotspots_table
201
- puts "\n🔥 Code Hotspots"
202
- puts "=" * 80
218
+ display_message("\n🔥 Code Hotspots")
219
+ display_message("=" * 80)
203
220
 
204
221
  create_table(
205
222
  ["Rank", "File", "Method", "Score", "Complexity", "Touches"],
@@ -217,81 +234,81 @@ module Aidp
217
234
  end
218
235
 
219
236
  def show_hotspots_summary
220
- puts "\n🔥 Code Hotspots (Top 20)"
221
- puts "=" * 50
237
+ display_message("\n🔥 Code Hotspots (Top 20)")
238
+ display_message("=" * 50)
222
239
 
223
240
  @data[:hotspots].each_with_index do |hotspot, i|
224
- puts "#{i + 1}. #{hotspot[:file]}:#{hotspot[:method]}"
225
- puts " Score: #{hotspot[:score]} (Complexity: #{hotspot[:complexity]}, Touches: #{hotspot[:touches]})"
226
- puts
241
+ display_message("#{i + 1}. #{hotspot[:file]}:#{hotspot[:method]}")
242
+ display_message(" Score: #{hotspot[:score]} (Complexity: #{hotspot[:complexity]}, Touches: #{hotspot[:touches]})")
243
+ display_message("")
227
244
  end
228
245
  end
229
246
 
230
247
  def show_cycles(format)
231
- return puts "No cycles data available" unless @data[:cycles]&.any?
248
+ return display_message("No cycles data available") unless @data[:cycles]&.any?
232
249
 
233
250
  case format
234
251
  when "json"
235
- puts JSON.pretty_generate(@data[:cycles])
252
+ display_message(JSON.pretty_generate(@data[:cycles]))
236
253
  else
237
254
  show_cycles_summary
238
255
  end
239
256
  end
240
257
 
241
258
  def show_cycles_summary
242
- puts "\n🔄 Import Cycles"
243
- puts "=" * 50
259
+ display_message("\n🔄 Import Cycles")
260
+ display_message("=" * 50)
244
261
 
245
262
  @data[:cycles].each_with_index do |cycle, i|
246
- puts "Cycle #{i + 1}:"
263
+ display_message("Cycle #{i + 1}:")
247
264
  cycle[:members].each do |member|
248
- puts " - #{member}"
265
+ display_message(" - #{member}")
249
266
  end
250
- puts " Weight: #{cycle[:weight]}" if cycle[:weight]
251
- puts
267
+ display_message(" Weight: #{cycle[:weight]}") if cycle[:weight]
268
+ display_message("")
252
269
  end
253
270
  end
254
271
 
255
272
  def show_apis(format)
256
- return puts "No APIs data available" unless @data[:tests]&.any?
273
+ return display_message("No APIs data available") unless @data[:tests]&.any?
257
274
 
258
275
  untested_apis = @data[:tests].select { |t| t[:tests].empty? }
259
276
 
260
277
  case format
261
278
  when "json"
262
- puts JSON.pretty_generate(untested_apis)
279
+ display_message(JSON.pretty_generate(untested_apis))
263
280
  else
264
281
  show_apis_summary(untested_apis)
265
282
  end
266
283
  end
267
284
 
268
285
  def show_apis_summary(untested_apis)
269
- puts "\n🧪 Untested Public APIs"
270
- puts "=" * 50
286
+ display_message("\n🧪 Untested Public APIs")
287
+ display_message("=" * 50)
271
288
 
272
289
  if untested_apis.empty?
273
- puts "✅ All public APIs have associated tests!"
290
+ display_message("✅ All public APIs have associated tests!")
274
291
  else
275
- puts "Found #{untested_apis.length} untested public APIs:"
276
- puts
292
+ display_message("Found #{untested_apis.length} untested public APIs:")
293
+ display_message("")
277
294
 
278
295
  untested_apis.each do |api|
279
296
  symbol = @data[:symbols]&.find { |s| s[:id] == api[:symbol_id] }
280
297
  if symbol
281
- puts " #{symbol[:file]}:#{symbol[:line]} - #{symbol[:name]}"
282
- puts " Suggestion: Create characterization tests"
283
- puts
298
+ display_message(" #{symbol[:file]}:#{symbol[:line]} - #{symbol[:name]}")
299
+ display_message(" Suggestion: Create characterization tests")
300
+ display_message("")
284
301
  end
285
302
  end
286
303
  end
287
304
  end
288
305
 
289
306
  def show_symbols(format)
290
- return puts "No symbols data available" unless @data[:symbols]&.any?
307
+ return display_message("No symbols data available") unless @data[:symbols]&.any?
291
308
 
292
309
  case format
293
310
  when "json"
294
- puts JSON.pretty_generate(@data[:symbols])
311
+ display_message(JSON.pretty_generate(@data[:symbols]))
295
312
  when "table"
296
313
  show_symbols_table
297
314
  else
@@ -300,8 +317,8 @@ module Aidp
300
317
  end
301
318
 
302
319
  def show_symbols_table
303
- puts "\n🏗️ Symbols"
304
- puts "=" * 80
320
+ display_message("\n🏗️ Symbols")
321
+ display_message("=" * 80)
305
322
 
306
323
  create_table(
307
324
  ["Type", "Name", "File", "Line", "Visibility"],
@@ -318,22 +335,22 @@ module Aidp
318
335
  end
319
336
 
320
337
  def show_symbols_summary
321
- puts "\n🏗️ Symbols Summary"
322
- puts "=" * 50
338
+ display_message("\n🏗️ Symbols Summary")
339
+ display_message("=" * 50)
323
340
 
324
341
  symbol_types = @data[:symbols].group_by { |s| s[:kind] }
325
342
 
326
343
  symbol_types.each do |type, symbols|
327
- puts "#{type.capitalize}: #{symbols.length}"
344
+ display_message("#{type.capitalize}: #{symbols.length}")
328
345
  end
329
346
  end
330
347
 
331
348
  def show_imports(format)
332
- return puts "No imports data available" unless @data[:imports]&.any?
349
+ return display_message("No imports data available") unless @data[:imports]&.any?
333
350
 
334
351
  case format
335
352
  when "json"
336
- puts JSON.pretty_generate(@data[:imports])
353
+ display_message(JSON.pretty_generate(@data[:imports]))
337
354
  when "table"
338
355
  show_imports_table
339
356
  else
@@ -342,8 +359,8 @@ module Aidp
342
359
  end
343
360
 
344
361
  def show_imports_table
345
- puts "\n📦 Imports"
346
- puts "=" * 80
362
+ display_message("\n📦 Imports")
363
+ display_message("=" * 80)
347
364
 
348
365
  create_table(
349
366
  ["Type", "Target", "File", "Line"],
@@ -359,18 +376,18 @@ module Aidp
359
376
  end
360
377
 
361
378
  def show_imports_summary
362
- puts "\n📦 Imports Summary"
363
- puts "=" * 50
379
+ display_message("\n📦 Imports Summary")
380
+ display_message("=" * 50)
364
381
 
365
382
  import_types = @data[:imports].group_by { |i| i[:kind] }
366
383
 
367
384
  import_types.each do |type, imports|
368
- puts "#{type.capitalize}: #{imports.length}"
385
+ display_message("#{type.capitalize}: #{imports.length}")
369
386
  end
370
387
  end
371
388
 
372
389
  def generate_import_graph(format, output)
373
- puts "Generating import graph in #{format} format..."
390
+ display_message("Generating import graph in #{format} format...")
374
391
 
375
392
  case format
376
393
  when "dot"
@@ -380,7 +397,7 @@ module Aidp
380
397
  when "json"
381
398
  generate_json_graph(output)
382
399
  else
383
- puts "Unsupported graph format: #{format}"
400
+ display_message("Unsupported graph format: #{format}")
384
401
  end
385
402
  end
386
403
 
@@ -399,9 +416,9 @@ module Aidp
399
416
 
400
417
  if output
401
418
  File.write(output, content.join("\n"))
402
- puts "Graph written to #{output}"
419
+ display_message("Graph written to #{output}")
403
420
  else
404
- puts content.join("\n")
421
+ display_message(content.join("\n"))
405
422
  end
406
423
  end
407
424
 
@@ -416,9 +433,9 @@ module Aidp
416
433
 
417
434
  if output
418
435
  File.write(output, content.join("\n"))
419
- puts "Graph written to #{output}"
436
+ display_message("Graph written to #{output}")
420
437
  else
421
- puts content.join("\n")
438
+ display_message(content.join("\n"))
422
439
  end
423
440
  end
424
441
 
@@ -447,20 +464,20 @@ module Aidp
447
464
 
448
465
  if output
449
466
  File.write(output, JSON.pretty_generate(graph_data))
450
- puts "Graph written to #{output}"
467
+ display_message("Graph written to #{output}")
451
468
  else
452
- puts JSON.pretty_generate(graph_data)
469
+ display_message(JSON.pretty_generate(graph_data))
453
470
  end
454
471
  end
455
472
 
456
473
  def generate_call_graph(_format, _output)
457
474
  # Similar to import graph but for method calls
458
- puts "Call graph generation not yet implemented"
475
+ display_message("Call graph generation not yet implemented")
459
476
  end
460
477
 
461
478
  def generate_cycle_graph(_format, _output)
462
479
  # Generate graph showing only the cycles
463
- puts "Cycle graph generation not yet implemented"
480
+ display_message("Cycle graph generation not yet implemented")
464
481
  end
465
482
 
466
483
  def count_files
@@ -1,14 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tty-prompt"
3
4
  require_relative "ruby_maat_integration"
4
5
  require_relative "feature_analyzer"
5
6
 
6
7
  module Aidp
7
8
  module Analyze
8
9
  class Prioritizer
9
- def initialize(project_dir = Dir.pwd)
10
+ def initialize(project_dir = Dir.pwd, prompt: TTY::Prompt.new)
10
11
  @project_dir = project_dir
11
- @code_maat = Aidp::Analyze::RubyMaatIntegration.new(project_dir)
12
+ @code_maat = Aidp::Analyze::RubyMaatIntegration.new(project_dir, prompt: prompt)
12
13
  @feature_analyzer = Aidp::Analyze::FeatureAnalyzer.new(project_dir)
13
14
  end
14
15
 
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "tty-command"
4
+ require "tty-prompt"
4
5
  require "json"
5
6
  require "fileutils"
6
7
 
7
8
  module Aidp
8
9
  module Analyze
9
10
  class RubyMaatIntegration
10
- def initialize(project_dir = Dir.pwd)
11
+ def initialize(project_dir = Dir.pwd, prompt: TTY::Prompt.new)
11
12
  @project_dir = project_dir
13
+ @prompt = prompt
12
14
  end
13
15
 
14
16
  # Check if RubyMaat gem is available and accessible
@@ -80,7 +82,7 @@ module Aidp
80
82
 
81
83
  # Run analysis on large repositories using chunking
82
84
  def run_chunked_analysis(git_log_file)
83
- puts "Large repository detected. Running chunked analysis..."
85
+ display_message("Large repository detected. Running chunked analysis...", type: :info)
84
86
 
85
87
  # Split analysis into chunks
86
88
  chunks = create_analysis_chunks(git_log_file)
@@ -93,7 +95,7 @@ module Aidp
93
95
  }
94
96
 
95
97
  chunks.each_with_index do |chunk, index|
96
- puts "Processing chunk #{index + 1}/#{chunks.length}..."
98
+ display_message("Processing chunk #{index + 1}/#{chunks.length}...", type: :info)
97
99
 
98
100
  chunk_results = analyze_chunk(chunk)
99
101
 
@@ -453,6 +455,21 @@ module Aidp
453
455
 
454
456
  result.success? && !result.out.strip.empty?
455
457
  end
458
+
459
+ private
460
+
461
+ # Helper method for consistent message display using TTY::Prompt
462
+ def display_message(message, type: :info)
463
+ color = case type
464
+ when :error then :red
465
+ when :warn then :yellow
466
+ when :success then :green
467
+ when :highlight then :cyan
468
+ else :white
469
+ end
470
+
471
+ @prompt.say(message, color: color)
472
+ end
456
473
  end
457
474
  end
458
475
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tty-prompt"
3
4
  require_relative "steps"
4
5
  require_relative "progress"
5
6
  require_relative "../storage/file_manager"
@@ -10,17 +11,34 @@ module Aidp
10
11
  class Runner
11
12
  include Aidp::DebugMixin
12
13
 
13
- def initialize(project_dir, harness_runner = nil)
14
+ def initialize(project_dir, harness_runner = nil, prompt: TTY::Prompt.new)
14
15
  @project_dir = project_dir
15
16
  @harness_runner = harness_runner
16
17
  @is_harness_mode = !harness_runner.nil?
17
18
  @file_manager = Aidp::Storage::FileManager.new(File.join(project_dir, ".aidp"))
19
+ @prompt = prompt
18
20
  end
19
21
 
20
22
  def progress
21
23
  @progress ||= Aidp::Analyze::Progress.new(@project_dir)
22
24
  end
23
25
 
26
+ private
27
+
28
+ def display_message(message, type: :info)
29
+ color = case type
30
+ when :error then :red
31
+ when :success then :green
32
+ when :warning then :yellow
33
+ when :info then :blue
34
+ when :highlight then :cyan
35
+ else :white
36
+ end
37
+ @prompt.say(message, color: color)
38
+ end
39
+
40
+ public
41
+
24
42
  def run_step(step_name, options = {})
25
43
  # Always validate step exists first
26
44
  step_spec = Aidp::Analyze::Steps::SPEC[step_name]
@@ -42,7 +60,7 @@ module Aidp
42
60
  # Harness-aware step execution
43
61
  def run_step_with_harness(step_name, options = {})
44
62
  # Get current provider from harness
45
- current_provider = @harness_runner.instance_variable_get(:@current_provider)
63
+ current_provider = @harness_runner.current_provider
46
64
  provider_type = current_provider || "cursor"
47
65
 
48
66
  debug_step(step_name, "Harness execution", {
@@ -72,7 +90,7 @@ module Aidp
72
90
 
73
91
  # Standalone step execution (simplified - synchronous)
74
92
  def run_step_standalone(step_name, options = {})
75
- puts "🚀 Running step synchronously: #{step_name}"
93
+ display_message("🚀 Running step synchronously: #{step_name}", type: :info)
76
94
 
77
95
  start_time = Time.now
78
96
  prompt = composed_prompt(step_name, options)
@@ -85,7 +103,7 @@ module Aidp
85
103
  # Store execution metrics
86
104
  @file_manager.record_step_execution(step_name, "cursor", duration, result[:status] == "completed")
87
105
 
88
- puts "✅ Step completed in #{duration.round(2)}s"
106
+ display_message("✅ Step completed in #{duration.round(2)}s", type: :success)
89
107
  result
90
108
  end
91
109
 
@@ -223,11 +241,11 @@ module Aidp
223
241
  # Add current execution context
224
242
  context_parts << "## Analysis Context"
225
243
  context_parts << "Project Directory: #{@project_dir}"
226
- context_parts << "Current Step: #{@harness_runner.instance_variable_get(:@current_step)}"
227
- context_parts << "Current Provider: #{@harness_runner.instance_variable_get(:@current_provider)}"
244
+ context_parts << "Current Step: #{@harness_runner.current_step}"
245
+ context_parts << "Current Provider: #{@harness_runner.current_provider}"
228
246
 
229
247
  # Add user input context
230
- user_input = @harness_runner.instance_variable_get(:@user_input)
248
+ user_input = @harness_runner.user_input
231
249
  if user_input && !user_input.empty?
232
250
  context_parts << "\n## Previous User Input"
233
251
  user_input.each do |key, value|
@@ -236,7 +254,7 @@ module Aidp
236
254
  end
237
255
 
238
256
  # Add execution history context
239
- execution_log = @harness_runner.instance_variable_get(:@execution_log)
257
+ execution_log = @harness_runner.execution_log
240
258
  if execution_log && !execution_log.empty?
241
259
  context_parts << "\n## Analysis History"
242
260
  recent_logs = execution_log.last(5) # Last 5 entries
@@ -251,7 +269,7 @@ module Aidp
251
269
  # Execute step with harness provider management
252
270
  def execute_with_harness_provider(provider_type, prompt, step_name, _options)
253
271
  # Get provider manager from harness
254
- provider_manager = @harness_runner.instance_variable_get(:@provider_manager)
272
+ provider_manager = @harness_runner.provider_manager
255
273
 
256
274
  # Execute with provider
257
275
  provider_manager.execute_with_provider(provider_type, prompt, {
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aidp
4
- module Analysis
4
+ module Analyze
5
5
  module Seams
6
6
  # I/O and OS integration patterns
7
7
  IO_PATTERNS = [