aidp 0.10.0 → 0.11.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.
- checksums.yaml +4 -4
- data/README.md +194 -25
- data/lib/aidp/analyze/kb_inspector.rb +2 -15
- data/lib/aidp/analyze/progress.rb +2 -1
- data/lib/aidp/analyze/ruby_maat_integration.rb +2 -15
- data/lib/aidp/analyze/runner.rb +64 -20
- data/lib/aidp/analyze/steps.rb +10 -8
- data/lib/aidp/analyze/tree_sitter_grammar_loader.rb +2 -13
- data/lib/aidp/analyze/tree_sitter_scan.rb +2 -13
- data/lib/aidp/cli/checkpoint_command.rb +98 -0
- data/lib/aidp/cli/first_run_wizard.rb +65 -94
- data/lib/aidp/cli/jobs_command.rb +249 -34
- data/lib/aidp/cli.rb +312 -38
- data/lib/aidp/config.rb +5 -8
- data/lib/aidp/debug_logger.rb +4 -4
- data/lib/aidp/debug_mixin.rb +11 -4
- data/lib/aidp/execute/checkpoint.rb +282 -0
- data/lib/aidp/execute/checkpoint_display.rb +221 -0
- data/lib/aidp/execute/progress.rb +2 -1
- data/lib/aidp/execute/prompt_manager.rb +62 -0
- data/lib/aidp/execute/runner.rb +53 -24
- data/lib/aidp/execute/steps.rb +36 -27
- data/lib/aidp/execute/work_loop_runner.rb +308 -0
- data/lib/aidp/execute/workflow_selector.rb +26 -17
- data/lib/aidp/harness/condition_detector.rb +4 -4
- data/lib/aidp/harness/config_schema.rb +40 -0
- data/lib/aidp/harness/config_validator.rb +3 -6
- data/lib/aidp/harness/configuration.rb +35 -1
- data/lib/aidp/harness/enhanced_runner.rb +22 -1
- data/lib/aidp/harness/error_handler.rb +103 -28
- data/lib/aidp/harness/provider_factory.rb +4 -1
- data/lib/aidp/harness/provider_manager.rb +250 -15
- data/lib/aidp/harness/runner.rb +3 -14
- data/lib/aidp/harness/simple_user_interface.rb +2 -15
- data/lib/aidp/harness/status_display.rb +12 -17
- data/lib/aidp/harness/test_runner.rb +83 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +2 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -4
- data/lib/aidp/harness/ui/error_handler.rb +4 -0
- data/lib/aidp/harness/ui/frame_manager.rb +10 -8
- data/lib/aidp/harness/ui/job_monitor.rb +2 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +4 -2
- data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
- data/lib/aidp/harness/ui/progress_display.rb +8 -12
- data/lib/aidp/harness/ui/question_collector.rb +2 -0
- data/lib/aidp/harness/ui/spinner_group.rb +2 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
- data/lib/aidp/harness/ui/status_manager.rb +4 -2
- data/lib/aidp/harness/ui/status_widget.rb +3 -1
- data/lib/aidp/harness/ui/workflow_controller.rb +2 -0
- data/lib/aidp/harness/user_interface.rb +12 -17
- data/lib/aidp/jobs/background_runner.rb +278 -0
- data/lib/aidp/message_display.rb +48 -0
- data/lib/aidp/provider_manager.rb +3 -1
- data/lib/aidp/providers/anthropic.rb +100 -17
- data/lib/aidp/providers/base.rb +42 -11
- data/lib/aidp/providers/codex.rb +248 -0
- data/lib/aidp/providers/cursor.rb +35 -42
- data/lib/aidp/providers/gemini.rb +25 -15
- data/lib/aidp/providers/github_copilot.rb +41 -42
- data/lib/aidp/providers/opencode.rb +34 -41
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/workflows/definitions.rb +357 -0
- data/lib/aidp/workflows/selector.rb +171 -0
- data/lib/aidp.rb +12 -0
- data/templates/planning/generate_llm_style_guide.md +119 -0
- metadata +38 -26
- /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
- /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
- /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
- /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
- /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
- /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
- /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
- /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
- /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
- /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
- /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
- /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
- /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
- /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
- /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
- /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
- /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
- /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
- /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
- /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
- /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
- /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
- /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
- /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
- /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
@@ -1,17 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "tty-prompt"
|
4
|
+
require "tty-cursor"
|
4
5
|
|
5
6
|
module Aidp
|
6
7
|
module Harness
|
7
8
|
# Real-time status updates and monitoring interface
|
8
9
|
class StatusDisplay
|
10
|
+
include Aidp::MessageDisplay
|
11
|
+
|
9
12
|
def initialize(provider_manager = nil, metrics_manager = nil, circuit_breaker_manager = nil, error_logger = nil, prompt: TTY::Prompt.new)
|
10
13
|
@provider_manager = provider_manager
|
11
14
|
@metrics_manager = metrics_manager
|
12
15
|
@circuit_breaker_manager = circuit_breaker_manager
|
13
16
|
@error_logger = error_logger
|
14
17
|
@prompt = prompt
|
18
|
+
@cursor = TTY::Cursor
|
15
19
|
|
16
20
|
@start_time = nil
|
17
21
|
@current_step = nil
|
@@ -41,21 +45,6 @@ module Aidp
|
|
41
45
|
@display_animator = DisplayAnimator.new
|
42
46
|
end
|
43
47
|
|
44
|
-
# Helper method for consistent message display using TTY::Prompt
|
45
|
-
def display_message(message, type: :info)
|
46
|
-
color = case type
|
47
|
-
when :error then :red
|
48
|
-
when :success then :green
|
49
|
-
when :warning then :yellow
|
50
|
-
when :info then :blue
|
51
|
-
when :highlight then :cyan
|
52
|
-
when :muted then :bright_black
|
53
|
-
else :white
|
54
|
-
end
|
55
|
-
|
56
|
-
@prompt.say(message, color: color)
|
57
|
-
end
|
58
|
-
|
59
48
|
# Start real-time status updates
|
60
49
|
def start_status_updates(display_mode = :compact)
|
61
50
|
return if @running
|
@@ -755,8 +744,14 @@ module Aidp
|
|
755
744
|
end
|
756
745
|
|
757
746
|
def clear_display
|
758
|
-
# Clear the current line
|
759
|
-
|
747
|
+
# Clear the current line using TTY::Cursor
|
748
|
+
print_to_stderr(@cursor.clear_line, @cursor.column(1))
|
749
|
+
end
|
750
|
+
|
751
|
+
# Helper method to print to stderr with flush
|
752
|
+
def print_to_stderr(*parts)
|
753
|
+
parts.each { |part| $stderr.print part }
|
754
|
+
$stderr.flush
|
760
755
|
end
|
761
756
|
|
762
757
|
def format_duration(seconds)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Aidp
|
6
|
+
module Harness
|
7
|
+
# Executes test and linter commands configured in aidp.yml
|
8
|
+
# Returns results with exit status and output
|
9
|
+
class TestRunner
|
10
|
+
def initialize(project_dir, config)
|
11
|
+
@project_dir = project_dir
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
# Run all configured tests
|
16
|
+
# Returns: { success: boolean, output: string, failures: array }
|
17
|
+
def run_tests
|
18
|
+
test_commands = @config.test_commands || []
|
19
|
+
return {success: true, output: "", failures: []} if test_commands.empty?
|
20
|
+
|
21
|
+
results = test_commands.map { |cmd| execute_command(cmd, "test") }
|
22
|
+
aggregate_results(results, "Tests")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Run all configured linters
|
26
|
+
# Returns: { success: boolean, output: string, failures: array }
|
27
|
+
def run_linters
|
28
|
+
lint_commands = @config.lint_commands || []
|
29
|
+
return {success: true, output: "", failures: []} if lint_commands.empty?
|
30
|
+
|
31
|
+
results = lint_commands.map { |cmd| execute_command(cmd, "linter") }
|
32
|
+
aggregate_results(results, "Linters")
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def execute_command(command, type)
|
38
|
+
stdout, stderr, status = Open3.capture3(command, chdir: @project_dir)
|
39
|
+
|
40
|
+
{
|
41
|
+
command: command,
|
42
|
+
type: type,
|
43
|
+
success: status.success?,
|
44
|
+
exit_code: status.exitstatus,
|
45
|
+
stdout: stdout,
|
46
|
+
stderr: stderr
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def aggregate_results(results, category)
|
51
|
+
failures = results.reject { |r| r[:success] }
|
52
|
+
success = failures.empty?
|
53
|
+
|
54
|
+
output = if success
|
55
|
+
"#{category}: All passed"
|
56
|
+
else
|
57
|
+
format_failures(failures, category)
|
58
|
+
end
|
59
|
+
|
60
|
+
{
|
61
|
+
success: success,
|
62
|
+
output: output,
|
63
|
+
failures: failures
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_failures(failures, category)
|
68
|
+
output = ["#{category} Failures:", ""]
|
69
|
+
|
70
|
+
failures.each do |failure|
|
71
|
+
output << "Command: #{failure[:command]}"
|
72
|
+
output << "Exit Code: #{failure[:exit_code]}"
|
73
|
+
output << "--- Output ---"
|
74
|
+
output << failure[:stdout] unless failure[:stdout].strip.empty?
|
75
|
+
output << failure[:stderr] unless failure[:stderr].strip.empty?
|
76
|
+
output << ""
|
77
|
+
end
|
78
|
+
|
79
|
+
output.join("\n")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -15,7 +15,9 @@ module Aidp
|
|
15
15
|
# Enhanced TUI system using TTY libraries, inspired by Claude Code and modern LLM agents
|
16
16
|
class EnhancedTUI
|
17
17
|
class TUIError < StandardError; end
|
18
|
+
|
18
19
|
class InputError < TUIError; end
|
20
|
+
|
19
21
|
class DisplayError < TUIError; end
|
20
22
|
|
21
23
|
def initialize(prompt: TTY::Prompt.new)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "enhanced_tui"
|
4
|
+
require_relative "../../workflows/selector"
|
4
5
|
|
5
6
|
module Aidp
|
6
7
|
module Harness
|
@@ -12,6 +13,7 @@ module Aidp
|
|
12
13
|
def initialize(tui = nil)
|
13
14
|
@tui = tui || EnhancedTUI.new
|
14
15
|
@user_input = {}
|
16
|
+
@workflow_selector = Aidp::Workflows::Selector.new
|
15
17
|
end
|
16
18
|
|
17
19
|
def select_workflow(harness_mode: false, mode: :analyze)
|
@@ -29,7 +31,7 @@ module Aidp
|
|
29
31
|
when :analyze
|
30
32
|
select_analyze_workflow_interactive
|
31
33
|
when :execute
|
32
|
-
|
34
|
+
select_execute_workflow_interactive_new(mode)
|
33
35
|
else
|
34
36
|
raise ArgumentError, "Unknown mode: #{mode}"
|
35
37
|
end
|
@@ -65,6 +67,22 @@ module Aidp
|
|
65
67
|
}
|
66
68
|
end
|
67
69
|
|
70
|
+
def select_execute_workflow_interactive_new(mode)
|
71
|
+
# Step 1: Collect project information
|
72
|
+
collect_project_info_interactive
|
73
|
+
|
74
|
+
# Step 2: Use new workflow selector
|
75
|
+
result = @workflow_selector.select_workflow(mode)
|
76
|
+
|
77
|
+
{
|
78
|
+
workflow_type: result[:workflow_key],
|
79
|
+
steps: result[:steps],
|
80
|
+
user_input: @user_input,
|
81
|
+
workflow: result[:workflow]
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
# Legacy method - kept for backward compatibility if needed
|
68
86
|
def select_execute_workflow_interactive
|
69
87
|
# Step 1: Collect project information
|
70
88
|
collect_project_info_interactive
|
@@ -160,10 +178,10 @@ module Aidp
|
|
160
178
|
|
161
179
|
def generate_exploration_steps
|
162
180
|
[
|
163
|
-
"00_PRD",
|
181
|
+
"00_PRD", # Generate PRD from user input (no manual gate)
|
164
182
|
"10_TESTING_STRATEGY", # Ensure we have tests
|
165
|
-
"11_STATIC_ANALYSIS",
|
166
|
-
"16_IMPLEMENTATION"
|
183
|
+
"11_STATIC_ANALYSIS", # Code quality
|
184
|
+
"16_IMPLEMENTATION" # Special step for actual development work
|
167
185
|
]
|
168
186
|
end
|
169
187
|
|
@@ -9,9 +9,13 @@ module Aidp
|
|
9
9
|
# Centralized error handling for UI components
|
10
10
|
class ErrorHandler
|
11
11
|
class UIError < StandardError; end
|
12
|
+
|
12
13
|
class ComponentError < UIError; end
|
14
|
+
|
13
15
|
class ValidationError < UIError; end
|
16
|
+
|
14
17
|
class DisplayError < UIError; end
|
18
|
+
|
15
19
|
class InteractionError < UIError; end
|
16
20
|
|
17
21
|
def initialize(ui_components = {})
|
@@ -9,7 +9,9 @@ module Aidp
|
|
9
9
|
# Handles nested framing using CLI UI frames
|
10
10
|
class FrameManager < Base
|
11
11
|
class FrameError < StandardError; end
|
12
|
+
|
12
13
|
class InvalidFrameError < FrameError; end
|
14
|
+
|
13
15
|
class DisplayError < FrameError; end
|
14
16
|
|
15
17
|
def initialize(ui_components = {})
|
@@ -56,7 +58,7 @@ module Aidp
|
|
56
58
|
@frame_stats[:status_counts][frame_data[:status]] += 1
|
57
59
|
end
|
58
60
|
|
59
|
-
if
|
61
|
+
if block
|
60
62
|
content = yield
|
61
63
|
display_message(@frame.frame(formatted_title, content, width: 80))
|
62
64
|
else
|
@@ -77,7 +79,7 @@ module Aidp
|
|
77
79
|
@frame_stack.push({type: frame_type, title: title, data: frame_data})
|
78
80
|
|
79
81
|
frame_result = @frame.frame(formatted_title, width: 80) do
|
80
|
-
if
|
82
|
+
if block
|
81
83
|
yield || ""
|
82
84
|
else
|
83
85
|
""
|
@@ -200,7 +202,7 @@ module Aidp
|
|
200
202
|
def frame_with_block(frame_type, title, frame_data = nil, &block)
|
201
203
|
validate_frame_type(frame_type)
|
202
204
|
validate_title(title)
|
203
|
-
raise ArgumentError, "Block required for frame_with_block" unless
|
205
|
+
raise ArgumentError, "Block required for frame_with_block" unless block
|
204
206
|
|
205
207
|
formatted_title = @formatter ? @formatter.format_frame_title(frame_type, title, frame_data) : title.to_s
|
206
208
|
@frame_open = true
|
@@ -220,7 +222,7 @@ module Aidp
|
|
220
222
|
rescue => e
|
221
223
|
@frame_open = false
|
222
224
|
@frame_stack.pop unless @frame_stack.empty?
|
223
|
-
raise e
|
225
|
+
raise e # Re-raise the original exception
|
224
226
|
end
|
225
227
|
end
|
226
228
|
|
@@ -237,7 +239,7 @@ module Aidp
|
|
237
239
|
validate_title(title)
|
238
240
|
|
239
241
|
formatted_title = @formatter ? @formatter.format_section_title(title) : "📋 #{title}"
|
240
|
-
if
|
242
|
+
if block
|
241
243
|
content = yield
|
242
244
|
display_message(@frame.frame(formatted_title, content, width: 80))
|
243
245
|
else
|
@@ -252,7 +254,7 @@ module Aidp
|
|
252
254
|
|
253
255
|
formatted_title = @formatter ? @formatter.format_subsection_title(title) : "📝 #{title}"
|
254
256
|
display_message(@frame.frame(formatted_title, width: 80) do
|
255
|
-
yield if
|
257
|
+
yield if block
|
256
258
|
end)
|
257
259
|
rescue => e
|
258
260
|
raise DisplayError, "Failed to create subsection: #{e.message}"
|
@@ -263,7 +265,7 @@ module Aidp
|
|
263
265
|
|
264
266
|
formatted_title = @formatter ? @formatter.format_workflow_title(workflow_name) : "⚙️ #{workflow_name}"
|
265
267
|
display_message(@frame.frame(formatted_title, width: 80) do
|
266
|
-
yield if
|
268
|
+
yield if block
|
267
269
|
end)
|
268
270
|
rescue => e
|
269
271
|
raise DisplayError, "Failed to create workflow frame: #{e.message}"
|
@@ -274,7 +276,7 @@ module Aidp
|
|
274
276
|
|
275
277
|
formatted_title = @formatter ? @formatter.format_step_title(step_name, step_number, total_steps) : "🔧 #{step_name} (#{step_number}/#{total_steps})"
|
276
278
|
display_message(@frame.frame(formatted_title, width: 80) do
|
277
|
-
yield if
|
279
|
+
yield if block
|
278
280
|
end)
|
279
281
|
rescue => e
|
280
282
|
raise DisplayError, "Failed to create step frame: #{e.message}"
|
@@ -12,7 +12,9 @@ module Aidp
|
|
12
12
|
# Real-time job monitoring and status tracking
|
13
13
|
class JobMonitor < Base
|
14
14
|
class JobMonitorError < StandardError; end
|
15
|
+
|
15
16
|
class JobNotFoundError < JobMonitorError; end
|
17
|
+
|
16
18
|
class MonitorError < JobMonitorError; end
|
17
19
|
|
18
20
|
JOB_STATUSES = {
|
@@ -14,7 +14,9 @@ module Aidp
|
|
14
14
|
# Main hierarchical navigation menu system
|
15
15
|
class MainMenu < Base
|
16
16
|
class MenuError < StandardError; end
|
17
|
+
|
17
18
|
class InvalidMenuError < MenuError; end
|
19
|
+
|
18
20
|
class NavigationError < MenuError; end
|
19
21
|
|
20
22
|
def initialize(ui_components = {}, prompt: nil)
|
@@ -27,7 +29,7 @@ module Aidp
|
|
27
29
|
@menu_items = []
|
28
30
|
@current_level = 0
|
29
31
|
@breadcrumb = []
|
30
|
-
@navigation_history = []
|
32
|
+
@navigation_history = [] # Track all navigation actions
|
31
33
|
end
|
32
34
|
|
33
35
|
def add_menu_item(item)
|
@@ -113,7 +115,7 @@ module Aidp
|
|
113
115
|
@menu_items = menu_items
|
114
116
|
display_menu_items
|
115
117
|
|
116
|
-
max_attempts = 10
|
118
|
+
max_attempts = 10 # Prevent infinite loops
|
117
119
|
attempts = 0
|
118
120
|
|
119
121
|
loop do
|
@@ -12,7 +12,9 @@ module Aidp
|
|
12
12
|
# Handles workflow mode selection (simple vs advanced)
|
13
13
|
class WorkflowSelector < Base
|
14
14
|
class WorkflowError < StandardError; end
|
15
|
+
|
15
16
|
class InvalidModeError < WorkflowError; end
|
17
|
+
|
16
18
|
class SelectionError < WorkflowError; end
|
17
19
|
|
18
20
|
WORKFLOW_MODES = {
|
@@ -10,8 +10,12 @@ module Aidp
|
|
10
10
|
module UI
|
11
11
|
# Handles progress display using CLI UI progress bars
|
12
12
|
class ProgressDisplay < Base
|
13
|
+
include Aidp::MessageDisplay
|
14
|
+
|
13
15
|
class ProgressError < StandardError; end
|
16
|
+
|
14
17
|
class InvalidProgressError < ProgressError; end
|
18
|
+
|
15
19
|
class DisplayError < ProgressError; end
|
16
20
|
|
17
21
|
attr_reader :refresh_interval
|
@@ -178,7 +182,7 @@ module Aidp
|
|
178
182
|
|
179
183
|
def execute_progress_steps(bar, total_steps, &block)
|
180
184
|
total_steps.times do
|
181
|
-
yield(bar) if
|
185
|
+
yield(bar) if block
|
182
186
|
bar.tick
|
183
187
|
end
|
184
188
|
end
|
@@ -188,7 +192,7 @@ module Aidp
|
|
188
192
|
total_substeps.times do |index|
|
189
193
|
substep_title = @formatter.format_substep_title(title, index + 1, total_substeps)
|
190
194
|
bar.update_title(substep_title)
|
191
|
-
yield(bar, index) if
|
195
|
+
yield(bar, index) if block
|
192
196
|
bar.tick
|
193
197
|
end
|
194
198
|
end
|
@@ -256,18 +260,10 @@ module Aidp
|
|
256
260
|
|
257
261
|
private
|
258
262
|
|
263
|
+
# Use mixin display_message; fallback to stdout if no prompt
|
259
264
|
def display_message(message, type: :info)
|
260
265
|
if @prompt
|
261
|
-
|
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)
|
266
|
+
super
|
271
267
|
elsif @output
|
272
268
|
@output.puts(message)
|
273
269
|
else
|
@@ -9,7 +9,9 @@ module Aidp
|
|
9
9
|
# Handles interactive question collection using CLI UI prompts
|
10
10
|
class QuestionCollector < Base
|
11
11
|
class QuestionError < StandardError; end
|
12
|
+
|
12
13
|
class ValidationError < QuestionError; end
|
14
|
+
|
13
15
|
class CollectionError < QuestionError; end
|
14
16
|
|
15
17
|
def initialize(ui_components = {}, prompt: nil)
|
@@ -9,7 +9,9 @@ module Aidp
|
|
9
9
|
# Handles concurrent operations using CLI UI spinner groups
|
10
10
|
class SpinnerGroup < Base
|
11
11
|
class SpinnerGroupError < StandardError; end
|
12
|
+
|
12
13
|
class InvalidOperationError < SpinnerGroupError; end
|
14
|
+
|
13
15
|
class ExecutionError < SpinnerGroupError; end
|
14
16
|
|
15
17
|
def initialize(ui_components = {})
|
@@ -18,7 +18,7 @@ module Aidp
|
|
18
18
|
|
19
19
|
# Main method: automatically manages spinner around a block
|
20
20
|
def with_spinner(message, format: :dots, success_message: nil, error_message: nil, &block)
|
21
|
-
raise ArgumentError, "Block required for with_spinner" unless
|
21
|
+
raise ArgumentError, "Block required for with_spinner" unless block
|
22
22
|
|
23
23
|
spinner = create_spinner(message, format)
|
24
24
|
start_spinner(spinner)
|
@@ -12,7 +12,9 @@ module Aidp
|
|
12
12
|
# Real-time status updates using CLI UI spinners
|
13
13
|
class StatusManager < Base
|
14
14
|
class StatusError < StandardError; end
|
15
|
+
|
15
16
|
class InvalidStatusError < StatusError; end
|
17
|
+
|
16
18
|
class UpdateError < StatusError; end
|
17
19
|
|
18
20
|
def initialize(ui_components = {})
|
@@ -164,7 +166,7 @@ module Aidp
|
|
164
166
|
end
|
165
167
|
|
166
168
|
def track_workflow_status(workflow_name, spinner, &block) # Will be updated dynamically
|
167
|
-
yield(spinner) if
|
169
|
+
yield(spinner) if block
|
168
170
|
@status_widget.show_success_status("Completed #{workflow_name}")
|
169
171
|
rescue => e
|
170
172
|
@status_widget.show_error_status("Failed #{workflow_name}: #{e.message}")
|
@@ -172,7 +174,7 @@ module Aidp
|
|
172
174
|
end
|
173
175
|
|
174
176
|
def track_step_status(step_name, spinner, &block)
|
175
|
-
yield(spinner) if
|
177
|
+
yield(spinner) if block
|
176
178
|
@status_widget.show_success_status("Completed #{step_name}")
|
177
179
|
rescue => e
|
178
180
|
@status_widget.show_error_status("Failed #{step_name}: #{e.message}")
|
@@ -10,7 +10,9 @@ module Aidp
|
|
10
10
|
# Handles status display using CLI UI spinners
|
11
11
|
class StatusWidget < Base
|
12
12
|
class StatusError < StandardError; end
|
13
|
+
|
13
14
|
class InvalidStatusError < StatusError; end
|
15
|
+
|
14
16
|
class DisplayError < StatusError; end
|
15
17
|
|
16
18
|
def initialize(ui_components = {})
|
@@ -29,7 +31,7 @@ module Aidp
|
|
29
31
|
|
30
32
|
formatted_message = @formatter.format_status_message(message)
|
31
33
|
@spinner.spin(formatted_message) do |spinner|
|
32
|
-
yield(spinner) if
|
34
|
+
yield(spinner) if block
|
33
35
|
end
|
34
36
|
rescue => e
|
35
37
|
raise DisplayError, "Failed to show status: #{e.message}"
|
@@ -11,7 +11,9 @@ module Aidp
|
|
11
11
|
# Workflow control interface for pause/resume/cancel/stop
|
12
12
|
class WorkflowController < Base
|
13
13
|
class WorkflowError < StandardError; end
|
14
|
+
|
14
15
|
class InvalidStateError < WorkflowError; end
|
16
|
+
|
15
17
|
class ControlError < WorkflowError; end
|
16
18
|
|
17
19
|
WORKFLOW_STATES = {
|
@@ -1,15 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "tty-prompt"
|
4
|
+
require "tty-cursor"
|
4
5
|
|
5
6
|
module Aidp
|
6
7
|
module Harness
|
7
8
|
# Handles user interaction and feedback collection
|
8
9
|
class UserInterface
|
10
|
+
include Aidp::MessageDisplay
|
11
|
+
|
9
12
|
def initialize(prompt: TTY::Prompt.new)
|
10
13
|
@input_history = []
|
11
14
|
@file_selection_enabled = false
|
12
15
|
@prompt = prompt
|
16
|
+
@cursor = TTY::Cursor
|
13
17
|
@control_mutex = Mutex.new
|
14
18
|
@pause_requested = false
|
15
19
|
@stop_requested = false
|
@@ -20,21 +24,6 @@ module Aidp
|
|
20
24
|
|
21
25
|
private
|
22
26
|
|
23
|
-
# Helper method for consistent message display using TTY::Prompt
|
24
|
-
def display_message(message, type: :info)
|
25
|
-
color = case type
|
26
|
-
when :error then :red
|
27
|
-
when :success then :green
|
28
|
-
when :warning then :yellow
|
29
|
-
when :info then :blue
|
30
|
-
when :highlight then :cyan
|
31
|
-
when :muted then :bright_black
|
32
|
-
else :white
|
33
|
-
end
|
34
|
-
|
35
|
-
@prompt.say(message, color: color)
|
36
|
-
end
|
37
|
-
|
38
27
|
# Helper method to handle input consistently with TTY::Prompt
|
39
28
|
# Fixed to avoid keystroke loss issues with TTY::Prompt's required validation
|
40
29
|
def get_input_with_prompt(message, required: false, default: nil)
|
@@ -1835,12 +1824,18 @@ module Aidp
|
|
1835
1824
|
|
1836
1825
|
# Display progress message
|
1837
1826
|
def show_progress(message)
|
1838
|
-
|
1827
|
+
print_to_stderr(@cursor.clear_line, @cursor.column(1), message)
|
1839
1828
|
end
|
1840
1829
|
|
1841
1830
|
# Clear progress message
|
1842
1831
|
def clear_progress
|
1843
|
-
|
1832
|
+
print_to_stderr(@cursor.clear_line, @cursor.column(1))
|
1833
|
+
end
|
1834
|
+
|
1835
|
+
# Helper method to print to stderr with flush
|
1836
|
+
def print_to_stderr(*parts)
|
1837
|
+
parts.each { |part| $stderr.print part }
|
1838
|
+
$stderr.flush
|
1844
1839
|
end
|
1845
1840
|
|
1846
1841
|
# Get input history
|