aidp 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -214
  3. data/bin/aidp +1 -1
  4. data/lib/aidp/analysis/kb_inspector.rb +38 -23
  5. data/lib/aidp/analysis/seams.rb +2 -31
  6. data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +1 -13
  7. data/lib/aidp/analysis/tree_sitter_scan.rb +3 -20
  8. data/lib/aidp/analyze/error_handler.rb +2 -75
  9. data/lib/aidp/analyze/json_file_storage.rb +292 -0
  10. data/lib/aidp/analyze/progress.rb +12 -0
  11. data/lib/aidp/analyze/progress_visualizer.rb +12 -17
  12. data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
  13. data/lib/aidp/analyze/runner.rb +256 -87
  14. data/lib/aidp/cli/jobs_command.rb +100 -432
  15. data/lib/aidp/cli.rb +309 -239
  16. data/lib/aidp/config.rb +298 -10
  17. data/lib/aidp/debug_logger.rb +195 -0
  18. data/lib/aidp/debug_mixin.rb +187 -0
  19. data/lib/aidp/execute/progress.rb +9 -0
  20. data/lib/aidp/execute/runner.rb +221 -40
  21. data/lib/aidp/execute/steps.rb +17 -7
  22. data/lib/aidp/execute/workflow_selector.rb +211 -0
  23. data/lib/aidp/harness/completion_checker.rb +268 -0
  24. data/lib/aidp/harness/condition_detector.rb +1526 -0
  25. data/lib/aidp/harness/config_loader.rb +373 -0
  26. data/lib/aidp/harness/config_manager.rb +382 -0
  27. data/lib/aidp/harness/config_schema.rb +1006 -0
  28. data/lib/aidp/harness/config_validator.rb +355 -0
  29. data/lib/aidp/harness/configuration.rb +477 -0
  30. data/lib/aidp/harness/enhanced_runner.rb +494 -0
  31. data/lib/aidp/harness/error_handler.rb +616 -0
  32. data/lib/aidp/harness/provider_config.rb +423 -0
  33. data/lib/aidp/harness/provider_factory.rb +306 -0
  34. data/lib/aidp/harness/provider_manager.rb +1269 -0
  35. data/lib/aidp/harness/provider_type_checker.rb +88 -0
  36. data/lib/aidp/harness/runner.rb +411 -0
  37. data/lib/aidp/harness/state/errors.rb +28 -0
  38. data/lib/aidp/harness/state/metrics.rb +219 -0
  39. data/lib/aidp/harness/state/persistence.rb +128 -0
  40. data/lib/aidp/harness/state/provider_state.rb +132 -0
  41. data/lib/aidp/harness/state/ui_state.rb +68 -0
  42. data/lib/aidp/harness/state/workflow_state.rb +123 -0
  43. data/lib/aidp/harness/state_manager.rb +586 -0
  44. data/lib/aidp/harness/status_display.rb +888 -0
  45. data/lib/aidp/harness/ui/base.rb +16 -0
  46. data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
  47. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
  48. data/lib/aidp/harness/ui/error_handler.rb +132 -0
  49. data/lib/aidp/harness/ui/frame_manager.rb +361 -0
  50. data/lib/aidp/harness/ui/job_monitor.rb +500 -0
  51. data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
  52. data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
  53. data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
  54. data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
  55. data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
  56. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
  57. data/lib/aidp/harness/ui/progress_display.rb +280 -0
  58. data/lib/aidp/harness/ui/question_collector.rb +141 -0
  59. data/lib/aidp/harness/ui/spinner_group.rb +184 -0
  60. data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
  61. data/lib/aidp/harness/ui/status_manager.rb +312 -0
  62. data/lib/aidp/harness/ui/status_widget.rb +280 -0
  63. data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
  64. data/lib/aidp/harness/user_interface.rb +2381 -0
  65. data/lib/aidp/provider_manager.rb +131 -7
  66. data/lib/aidp/providers/anthropic.rb +28 -103
  67. data/lib/aidp/providers/base.rb +170 -0
  68. data/lib/aidp/providers/cursor.rb +52 -181
  69. data/lib/aidp/providers/gemini.rb +24 -107
  70. data/lib/aidp/providers/macos_ui.rb +99 -5
  71. data/lib/aidp/providers/opencode.rb +194 -0
  72. data/lib/aidp/storage/csv_storage.rb +172 -0
  73. data/lib/aidp/storage/file_manager.rb +214 -0
  74. data/lib/aidp/storage/json_storage.rb +140 -0
  75. data/lib/aidp/version.rb +1 -1
  76. data/lib/aidp.rb +54 -39
  77. data/templates/COMMON/AGENT_BASE.md +11 -0
  78. data/templates/EXECUTE/00_PRD.md +4 -4
  79. data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
  80. data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
  81. data/templates/EXECUTE/08_TASKS.md +4 -4
  82. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
  83. data/templates/README.md +279 -0
  84. data/templates/aidp-development.yml.example +373 -0
  85. data/templates/aidp-minimal.yml.example +48 -0
  86. data/templates/aidp-production.yml.example +475 -0
  87. data/templates/aidp.yml.example +598 -0
  88. metadata +93 -69
  89. data/lib/aidp/analyze/agent_personas.rb +0 -71
  90. data/lib/aidp/analyze/agent_tool_executor.rb +0 -439
  91. data/lib/aidp/analyze/data_retention_manager.rb +0 -421
  92. data/lib/aidp/analyze/database.rb +0 -260
  93. data/lib/aidp/analyze/dependencies.rb +0 -335
  94. data/lib/aidp/analyze/export_manager.rb +0 -418
  95. data/lib/aidp/analyze/focus_guidance.rb +0 -517
  96. data/lib/aidp/analyze/incremental_analyzer.rb +0 -533
  97. data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
  98. data/lib/aidp/analyze/large_analysis_progress.rb +0 -499
  99. data/lib/aidp/analyze/memory_manager.rb +0 -339
  100. data/lib/aidp/analyze/metrics_storage.rb +0 -336
  101. data/lib/aidp/analyze/parallel_processor.rb +0 -454
  102. data/lib/aidp/analyze/performance_optimizer.rb +0 -691
  103. data/lib/aidp/analyze/repository_chunker.rb +0 -697
  104. data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
  105. data/lib/aidp/analyze/storage.rb +0 -655
  106. data/lib/aidp/analyze/tool_configuration.rb +0 -441
  107. data/lib/aidp/analyze/tool_modernization.rb +0 -750
  108. data/lib/aidp/database/pg_adapter.rb +0 -148
  109. data/lib/aidp/database_config.rb +0 -69
  110. data/lib/aidp/database_connection.rb +0 -72
  111. data/lib/aidp/job_manager.rb +0 -41
  112. data/lib/aidp/jobs/base_job.rb +0 -45
  113. data/lib/aidp/jobs/provider_execution_job.rb +0 -83
  114. data/lib/aidp/project_detector.rb +0 -117
  115. data/lib/aidp/providers/agent_supervisor.rb +0 -348
  116. data/lib/aidp/providers/supervised_base.rb +0 -317
  117. data/lib/aidp/providers/supervised_cursor.rb +0 -22
  118. data/lib/aidp/sync.rb +0 -13
  119. data/lib/aidp/workspace.rb +0 -19
@@ -0,0 +1,361 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tty-box"
4
+ require_relative "base"
5
+
6
+ module Aidp
7
+ module Harness
8
+ module UI
9
+ # Handles nested framing using CLI UI frames
10
+ class FrameManager < Base
11
+ class FrameError < StandardError; end
12
+ class InvalidFrameError < FrameError; end
13
+ class DisplayError < FrameError; end
14
+
15
+ def initialize(ui_components = {})
16
+ super()
17
+ @frame = ui_components[:frame] || TTY::Box
18
+ @formatter = ui_components[:formatter] || FrameFormatter.new
19
+ @frame_open = false
20
+ @frame_stack = []
21
+ @frame_history = []
22
+ @frame_stats = {
23
+ total_frames: 0,
24
+ frame_types: Hash.new(0),
25
+ status_counts: Hash.new(0)
26
+ }
27
+ end
28
+
29
+ def open_frame(frame_type, title, frame_data = nil, &block)
30
+ validate_frame_type(frame_type)
31
+ validate_title(title)
32
+
33
+ formatted_title = @formatter.format_frame_title(frame_type, title, frame_data)
34
+ @frame_open = true
35
+ frame_info = {type: frame_type, title: title, data: frame_data}
36
+ @frame_stack.push(frame_info)
37
+ @frame_history.push(frame_info.dup)
38
+
39
+ # Update statistics
40
+ @frame_stats[:total_frames] += 1
41
+ @frame_stats[:frame_types][frame_type] += 1
42
+ if frame_data && frame_data[:status]
43
+ @frame_stats[:status_counts][frame_data[:status]] += 1
44
+ end
45
+
46
+ if block_given?
47
+ content = yield
48
+ puts TTY::Box.frame(formatted_title, content, width: 80)
49
+ else
50
+ puts TTY::Box.frame(formatted_title, width: 80)
51
+ end
52
+ rescue InvalidFrameError => e
53
+ raise e
54
+ rescue => e
55
+ raise DisplayError, "Failed to open frame: #{e.message}"
56
+ end
57
+
58
+ def nested_frame(frame_type, title, frame_data = nil, &block)
59
+ validate_frame_type(frame_type)
60
+ validate_title(title)
61
+ raise DisplayError, "No parent frame exists for nesting" if @frame_stack.empty?
62
+
63
+ formatted_title = @formatter.format_frame_title(frame_type, title, frame_data)
64
+ @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?
67
+ end
68
+ rescue InvalidFrameError => e
69
+ raise e
70
+ rescue => e
71
+ raise DisplayError, "Failed to create nested frame: #{e.message}"
72
+ end
73
+
74
+ def close_frame
75
+ @frame_open = false
76
+ @frame_stack.pop unless @frame_stack.empty?
77
+ end
78
+
79
+ def frame_open?
80
+ @frame_open
81
+ end
82
+
83
+ def frame_depth
84
+ @frame_stack.length
85
+ end
86
+
87
+ def update_frame_status(status)
88
+ raise DisplayError, "No frame is currently open" if @frame_stack.empty?
89
+
90
+ current_frame = @frame_stack.last
91
+ current_frame[:data] ||= {}
92
+ current_frame[:data][:status] = status
93
+
94
+ # Display status update
95
+ status_text = case status
96
+ when :running then "Running"
97
+ when :completed then "Completed"
98
+ when :failed then "Failed"
99
+ else status.to_s.capitalize
100
+ end
101
+ puts "Status: #{status_text}"
102
+ end
103
+
104
+ def current_frame_status
105
+ return nil if @frame_stack.empty?
106
+
107
+ current_frame = @frame_stack.last
108
+ current_frame[:data]&.dig(:status)
109
+ end
110
+
111
+ def current_frame_type
112
+ return nil if @frame_stack.empty?
113
+
114
+ @frame_stack.last[:type]
115
+ end
116
+
117
+ def current_frame_title
118
+ return nil if @frame_stack.empty?
119
+
120
+ @frame_stack.last[:title]
121
+ end
122
+
123
+ def get_frame_stack
124
+ @frame_stack.dup
125
+ end
126
+
127
+ def display_frame_summary
128
+ puts "\n📊 Frame Summary"
129
+ puts "=" * 50
130
+
131
+ if @frame_stats[:total_frames] == 0
132
+ puts "No frames used"
133
+ return
134
+ end
135
+
136
+ puts "Total Frames: #{@frame_stats[:total_frames]}"
137
+
138
+ unless @frame_stats[:frame_types].empty?
139
+ puts "\nFrame Types:"
140
+ @frame_stats[:frame_types].each do |type, count|
141
+ emoji = case type
142
+ when :section then "📋"
143
+ when :subsection then "📝"
144
+ when :workflow then "âš™ī¸"
145
+ when :step then "🔧"
146
+ else "📋"
147
+ end
148
+ puts " #{emoji} #{type.to_s.capitalize}: #{count}"
149
+ end
150
+ end
151
+
152
+ unless @frame_stats[:status_counts].empty?
153
+ puts "\nStatus Counts:"
154
+ @frame_stats[:status_counts].each do |status, count|
155
+ status_emoji = case status
156
+ when :running then "🔄"
157
+ when :completed then "✅"
158
+ when :failed then "❌"
159
+ else "❓"
160
+ end
161
+ puts " #{status_emoji} #{status.to_s.capitalize}: #{count}"
162
+ end
163
+ end
164
+
165
+ puts "\nCurrent Frame Depth: #{@frame_depth}"
166
+ puts "Frames in History: #{@frame_history.length}"
167
+ end
168
+
169
+ def clear_frame_history
170
+ @frame_history.clear
171
+ @frame_stack.clear
172
+ @frame_open = false
173
+ @frame_stats = {
174
+ total_frames: 0,
175
+ frame_types: Hash.new(0),
176
+ status_counts: Hash.new(0)
177
+ }
178
+ end
179
+
180
+ def frame_with_block(frame_type, title, frame_data = nil, &block)
181
+ validate_frame_type(frame_type)
182
+ validate_title(title)
183
+ raise ArgumentError, "Block required for frame_with_block" unless block_given?
184
+
185
+ formatted_title = @formatter.format_frame_title(frame_type, title, frame_data)
186
+ @frame_open = true
187
+ @frame_stack.push({type: frame_type, title: title, data: frame_data})
188
+
189
+ begin
190
+ content = yield
191
+ puts TTY::Box.frame(formatted_title, content, width: 80)
192
+
193
+ @frame_open = false
194
+ @frame_stack.pop unless @frame_stack.empty?
195
+ content
196
+ rescue InvalidFrameError => e
197
+ @frame_open = false
198
+ @frame_stack.pop unless @frame_stack.empty?
199
+ raise e
200
+ rescue => e
201
+ @frame_open = false
202
+ @frame_stack.pop unless @frame_stack.empty?
203
+ raise e # Re-raise the original exception
204
+ end
205
+ end
206
+
207
+ def divider(text)
208
+ validate_text(text)
209
+
210
+ formatted_text = @formatter.format_divider_text(text)
211
+ @frame.divider(formatted_text)
212
+ rescue => e
213
+ raise DisplayError, "Failed to create divider: #{e.message}"
214
+ end
215
+
216
+ def section(title, &block)
217
+ validate_title(title)
218
+
219
+ formatted_title = @formatter.format_section_title(title)
220
+ if block_given?
221
+ content = yield
222
+ puts TTY::Box.frame(formatted_title, content, width: 80)
223
+ else
224
+ puts TTY::Box.frame(formatted_title, width: 80)
225
+ end
226
+ rescue => e
227
+ raise DisplayError, "Failed to create section: #{e.message}"
228
+ end
229
+
230
+ def subsection(title, &block)
231
+ validate_title(title)
232
+
233
+ formatted_title = @formatter.format_subsection_title(title)
234
+ puts TTY::Box.frame(formatted_title, width: 80) do
235
+ yield if block_given?
236
+ end
237
+ rescue => e
238
+ raise DisplayError, "Failed to create subsection: #{e.message}"
239
+ end
240
+
241
+ def workflow_frame(workflow_name, &block)
242
+ validate_workflow_name(workflow_name)
243
+
244
+ formatted_title = @formatter.format_workflow_title(workflow_name)
245
+ puts TTY::Box.frame(formatted_title, width: 80) do
246
+ yield if block_given?
247
+ end
248
+ rescue => e
249
+ raise DisplayError, "Failed to create workflow frame: #{e.message}"
250
+ end
251
+
252
+ def step_frame(step_name, step_number, total_steps, &block)
253
+ validate_step_inputs(step_name, step_number, total_steps)
254
+
255
+ formatted_title = @formatter.format_step_title(step_name, step_number, total_steps)
256
+ puts TTY::Box.frame(formatted_title, width: 80) do
257
+ yield if block_given?
258
+ end
259
+ rescue => e
260
+ raise DisplayError, "Failed to create step frame: #{e.message}"
261
+ end
262
+
263
+ private
264
+
265
+ def validate_frame_type(frame_type)
266
+ valid_types = [:section, :subsection, :workflow, :step]
267
+ unless valid_types.include?(frame_type)
268
+ raise InvalidFrameError, "Invalid frame type: #{frame_type}. Must be one of: #{valid_types.join(", ")}"
269
+ end
270
+ end
271
+
272
+ def validate_title(title)
273
+ raise InvalidFrameError, "Title cannot be empty" if title.to_s.strip.empty?
274
+ end
275
+
276
+ def validate_text(text)
277
+ raise InvalidFrameError, "Text cannot be empty" if text.to_s.strip.empty?
278
+ end
279
+
280
+ def validate_workflow_name(workflow_name)
281
+ raise InvalidFrameError, "Workflow name cannot be empty" if workflow_name.to_s.strip.empty?
282
+ end
283
+
284
+ def validate_step_inputs(step_name, step_number, total_steps)
285
+ validate_title(step_name)
286
+ raise InvalidFrameError, "Step number must be positive" unless step_number > 0
287
+ raise InvalidFrameError, "Total steps must be positive" unless total_steps > 0
288
+ raise InvalidFrameError, "Step number cannot exceed total steps" if step_number > total_steps
289
+ end
290
+ end
291
+
292
+ # Formats frame display text
293
+ class FrameFormatter
294
+ def format_frame_title(frame_type, title, frame_data = nil)
295
+ emoji = case frame_type
296
+ when :section then "📋"
297
+ when :subsection then "📝"
298
+ when :workflow then "âš™ī¸"
299
+ when :step then "🔧"
300
+ else "📋"
301
+ end
302
+
303
+ base_title = "#{emoji} #{title}"
304
+
305
+ if frame_data && frame_data[:status]
306
+ status_text = case frame_data[:status]
307
+ when :running then "Running"
308
+ when :completed then "Completed"
309
+ when :failed then "Failed"
310
+ else frame_data[:status].to_s.capitalize
311
+ end
312
+ base_title += " (#{status_text})"
313
+ end
314
+
315
+ base_title
316
+ end
317
+
318
+ def format_divider_text(text)
319
+ "── #{text} ──"
320
+ end
321
+
322
+ def format_section_title(title)
323
+ "📁 #{title}"
324
+ end
325
+
326
+ def format_subsection_title(title)
327
+ "📄 #{title}"
328
+ end
329
+
330
+ def format_workflow_title(workflow_name)
331
+ "🔄 #{workflow_name} Workflow"
332
+ end
333
+
334
+ def format_step_title(step_name, step_number, total_steps)
335
+ "⚡ Step #{step_number}/#{total_steps}: #{step_name}"
336
+ end
337
+
338
+ def format_progress_title(current, total)
339
+ "📊 Progress: #{current}/#{total}"
340
+ end
341
+
342
+ def format_status_title(status)
343
+ case status
344
+ when :running
345
+ "đŸŸĸ Running"
346
+ when :paused
347
+ "🟡 Paused"
348
+ when :completed
349
+ "✅ Completed"
350
+ when :failed
351
+ "❌ Failed"
352
+ when :cancelled
353
+ "âšī¸ Cancelled"
354
+ else
355
+ "❓ #{status.to_s.capitalize}"
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end
361
+ end