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.
- checksums.yaml +4 -4
- data/README.md +60 -214
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +38 -23
- data/lib/aidp/analysis/seams.rb +2 -31
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +1 -13
- data/lib/aidp/analysis/tree_sitter_scan.rb +3 -20
- data/lib/aidp/analyze/error_handler.rb +2 -75
- data/lib/aidp/analyze/json_file_storage.rb +292 -0
- data/lib/aidp/analyze/progress.rb +12 -0
- data/lib/aidp/analyze/progress_visualizer.rb +12 -17
- data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
- data/lib/aidp/analyze/runner.rb +256 -87
- data/lib/aidp/cli/jobs_command.rb +100 -432
- data/lib/aidp/cli.rb +309 -239
- data/lib/aidp/config.rb +298 -10
- data/lib/aidp/debug_logger.rb +195 -0
- data/lib/aidp/debug_mixin.rb +187 -0
- data/lib/aidp/execute/progress.rb +9 -0
- data/lib/aidp/execute/runner.rb +221 -40
- data/lib/aidp/execute/steps.rb +17 -7
- data/lib/aidp/execute/workflow_selector.rb +211 -0
- data/lib/aidp/harness/completion_checker.rb +268 -0
- data/lib/aidp/harness/condition_detector.rb +1526 -0
- data/lib/aidp/harness/config_loader.rb +373 -0
- data/lib/aidp/harness/config_manager.rb +382 -0
- data/lib/aidp/harness/config_schema.rb +1006 -0
- data/lib/aidp/harness/config_validator.rb +355 -0
- data/lib/aidp/harness/configuration.rb +477 -0
- data/lib/aidp/harness/enhanced_runner.rb +494 -0
- data/lib/aidp/harness/error_handler.rb +616 -0
- data/lib/aidp/harness/provider_config.rb +423 -0
- data/lib/aidp/harness/provider_factory.rb +306 -0
- data/lib/aidp/harness/provider_manager.rb +1269 -0
- data/lib/aidp/harness/provider_type_checker.rb +88 -0
- data/lib/aidp/harness/runner.rb +411 -0
- data/lib/aidp/harness/state/errors.rb +28 -0
- data/lib/aidp/harness/state/metrics.rb +219 -0
- data/lib/aidp/harness/state/persistence.rb +128 -0
- data/lib/aidp/harness/state/provider_state.rb +132 -0
- data/lib/aidp/harness/state/ui_state.rb +68 -0
- data/lib/aidp/harness/state/workflow_state.rb +123 -0
- data/lib/aidp/harness/state_manager.rb +586 -0
- data/lib/aidp/harness/status_display.rb +888 -0
- data/lib/aidp/harness/ui/base.rb +16 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
- data/lib/aidp/harness/ui/error_handler.rb +132 -0
- data/lib/aidp/harness/ui/frame_manager.rb +361 -0
- data/lib/aidp/harness/ui/job_monitor.rb +500 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
- data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
- data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
- data/lib/aidp/harness/ui/progress_display.rb +280 -0
- data/lib/aidp/harness/ui/question_collector.rb +141 -0
- data/lib/aidp/harness/ui/spinner_group.rb +184 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
- data/lib/aidp/harness/ui/status_manager.rb +312 -0
- data/lib/aidp/harness/ui/status_widget.rb +280 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
- data/lib/aidp/harness/user_interface.rb +2381 -0
- data/lib/aidp/provider_manager.rb +131 -7
- data/lib/aidp/providers/anthropic.rb +28 -103
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -181
- data/lib/aidp/providers/gemini.rb +24 -107
- data/lib/aidp/providers/macos_ui.rb +99 -5
- data/lib/aidp/providers/opencode.rb +194 -0
- data/lib/aidp/storage/csv_storage.rb +172 -0
- data/lib/aidp/storage/file_manager.rb +214 -0
- data/lib/aidp/storage/json_storage.rb +140 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +54 -39
- data/templates/COMMON/AGENT_BASE.md +11 -0
- data/templates/EXECUTE/00_PRD.md +4 -4
- data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
- data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
- data/templates/EXECUTE/08_TASKS.md +4 -4
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
- data/templates/README.md +279 -0
- data/templates/aidp-development.yml.example +373 -0
- data/templates/aidp-minimal.yml.example +48 -0
- data/templates/aidp-production.yml.example +475 -0
- data/templates/aidp.yml.example +598 -0
- metadata +93 -69
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -439
- data/lib/aidp/analyze/data_retention_manager.rb +0 -421
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -418
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -533
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -499
- data/lib/aidp/analyze/memory_manager.rb +0 -339
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -454
- data/lib/aidp/analyze/performance_optimizer.rb +0 -691
- data/lib/aidp/analyze/repository_chunker.rb +0 -697
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -655
- data/lib/aidp/analyze/tool_configuration.rb +0 -441
- data/lib/aidp/analyze/tool_modernization.rb +0 -750
- data/lib/aidp/database/pg_adapter.rb +0 -148
- data/lib/aidp/database_config.rb +0 -69
- data/lib/aidp/database_connection.rb +0 -72
- data/lib/aidp/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -45
- data/lib/aidp/jobs/provider_execution_job.rb +0 -83
- data/lib/aidp/project_detector.rb +0 -117
- data/lib/aidp/providers/agent_supervisor.rb +0 -348
- data/lib/aidp/providers/supervised_base.rb +0 -317
- data/lib/aidp/providers/supervised_cursor.rb +0 -22
- data/lib/aidp/sync.rb +0 -13
- 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
|