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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +194 -25
  3. data/lib/aidp/analyze/kb_inspector.rb +2 -15
  4. data/lib/aidp/analyze/progress.rb +2 -1
  5. data/lib/aidp/analyze/ruby_maat_integration.rb +2 -15
  6. data/lib/aidp/analyze/runner.rb +64 -20
  7. data/lib/aidp/analyze/steps.rb +10 -8
  8. data/lib/aidp/analyze/tree_sitter_grammar_loader.rb +2 -13
  9. data/lib/aidp/analyze/tree_sitter_scan.rb +2 -13
  10. data/lib/aidp/cli/checkpoint_command.rb +98 -0
  11. data/lib/aidp/cli/first_run_wizard.rb +65 -94
  12. data/lib/aidp/cli/jobs_command.rb +249 -34
  13. data/lib/aidp/cli.rb +312 -38
  14. data/lib/aidp/config.rb +5 -8
  15. data/lib/aidp/debug_logger.rb +4 -4
  16. data/lib/aidp/debug_mixin.rb +11 -4
  17. data/lib/aidp/execute/checkpoint.rb +282 -0
  18. data/lib/aidp/execute/checkpoint_display.rb +221 -0
  19. data/lib/aidp/execute/progress.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +62 -0
  21. data/lib/aidp/execute/runner.rb +53 -24
  22. data/lib/aidp/execute/steps.rb +36 -27
  23. data/lib/aidp/execute/work_loop_runner.rb +308 -0
  24. data/lib/aidp/execute/workflow_selector.rb +26 -17
  25. data/lib/aidp/harness/condition_detector.rb +4 -4
  26. data/lib/aidp/harness/config_schema.rb +40 -0
  27. data/lib/aidp/harness/config_validator.rb +3 -6
  28. data/lib/aidp/harness/configuration.rb +35 -1
  29. data/lib/aidp/harness/enhanced_runner.rb +22 -1
  30. data/lib/aidp/harness/error_handler.rb +103 -28
  31. data/lib/aidp/harness/provider_factory.rb +4 -1
  32. data/lib/aidp/harness/provider_manager.rb +250 -15
  33. data/lib/aidp/harness/runner.rb +3 -14
  34. data/lib/aidp/harness/simple_user_interface.rb +2 -15
  35. data/lib/aidp/harness/status_display.rb +12 -17
  36. data/lib/aidp/harness/test_runner.rb +83 -0
  37. data/lib/aidp/harness/ui/enhanced_tui.rb +2 -0
  38. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -4
  39. data/lib/aidp/harness/ui/error_handler.rb +4 -0
  40. data/lib/aidp/harness/ui/frame_manager.rb +10 -8
  41. data/lib/aidp/harness/ui/job_monitor.rb +2 -0
  42. data/lib/aidp/harness/ui/navigation/main_menu.rb +4 -2
  43. data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
  44. data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
  45. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  46. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
  47. data/lib/aidp/harness/ui/progress_display.rb +8 -12
  48. data/lib/aidp/harness/ui/question_collector.rb +2 -0
  49. data/lib/aidp/harness/ui/spinner_group.rb +2 -0
  50. data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
  51. data/lib/aidp/harness/ui/status_manager.rb +4 -2
  52. data/lib/aidp/harness/ui/status_widget.rb +3 -1
  53. data/lib/aidp/harness/ui/workflow_controller.rb +2 -0
  54. data/lib/aidp/harness/user_interface.rb +12 -17
  55. data/lib/aidp/jobs/background_runner.rb +278 -0
  56. data/lib/aidp/message_display.rb +48 -0
  57. data/lib/aidp/provider_manager.rb +3 -1
  58. data/lib/aidp/providers/anthropic.rb +100 -17
  59. data/lib/aidp/providers/base.rb +42 -11
  60. data/lib/aidp/providers/codex.rb +248 -0
  61. data/lib/aidp/providers/cursor.rb +35 -42
  62. data/lib/aidp/providers/gemini.rb +25 -15
  63. data/lib/aidp/providers/github_copilot.rb +41 -42
  64. data/lib/aidp/providers/opencode.rb +34 -41
  65. data/lib/aidp/version.rb +1 -1
  66. data/lib/aidp/workflows/definitions.rb +357 -0
  67. data/lib/aidp/workflows/selector.rb +171 -0
  68. data/lib/aidp.rb +12 -0
  69. data/templates/planning/generate_llm_style_guide.md +119 -0
  70. metadata +38 -26
  71. /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
  72. /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
  73. /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
  74. /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
  75. /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
  76. /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
  77. /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
  78. /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
  79. /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
  80. /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
  81. /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
  82. /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
  83. /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
  84. /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
  85. /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
  86. /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
  87. /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
  88. /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
  89. /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
  90. /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
  91. /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
  92. /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
  93. /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
  94. /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
  95. /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "timeout"
4
+ require_relative "base"
5
+ require_relative "../util"
6
+ require_relative "../debug_mixin"
7
+
8
+ module Aidp
9
+ module Providers
10
+ class Codex < Base
11
+ include Aidp::DebugMixin
12
+
13
+ def self.available?
14
+ !!Aidp::Util.which("codex")
15
+ end
16
+
17
+ def name
18
+ "codex"
19
+ end
20
+
21
+ def display_name
22
+ "Codex CLI"
23
+ end
24
+
25
+ def available?
26
+ return false unless self.class.available?
27
+
28
+ # Additional check to ensure the CLI is properly configured
29
+ begin
30
+ result = Aidp::Util.execute_command("codex", ["--version"], timeout: 10)
31
+ result.exit_status == 0
32
+ rescue
33
+ false
34
+ end
35
+ end
36
+
37
+ def send(prompt:, session: nil)
38
+ raise "codex CLI not available" unless self.class.available?
39
+
40
+ # Smart timeout calculation
41
+ timeout_seconds = calculate_timeout
42
+
43
+ debug_provider("codex", "Starting execution", {timeout: timeout_seconds})
44
+ debug_log("📝 Sending prompt to codex (length: #{prompt.length})", level: :info)
45
+
46
+ # Check if streaming mode is enabled
47
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
48
+ if streaming_enabled
49
+ display_message("📺 Streaming mode enabled - output will appear in real-time", type: :info)
50
+ end
51
+
52
+ # Set up activity monitoring
53
+ setup_activity_monitoring("codex", method(:activity_callback))
54
+ record_activity("Starting codex execution")
55
+
56
+ # Create a spinner for activity display
57
+ spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
58
+ spinner.auto_spin
59
+
60
+ # Start activity display thread with timeout
61
+ activity_display_thread = Thread.new do
62
+ start_time = Time.now
63
+ loop do
64
+ sleep 0.5 # Update every 500ms to reduce spam
65
+ elapsed = Time.now - start_time
66
+
67
+ # Break if we've been running too long or state changed
68
+ break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
69
+
70
+ update_spinner_status(spinner, elapsed, "🤖 Codex CLI")
71
+ end
72
+ end
73
+
74
+ begin
75
+ # Use non-interactive mode (exec) for automation
76
+ args = ["exec", prompt]
77
+
78
+ # Add session support if provided (codex may support session/thread continuation)
79
+ if session && !session.empty?
80
+ args += ["--session", session]
81
+ end
82
+
83
+ # Use debug_execute_command for better debugging
84
+ result = debug_execute_command("codex", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
85
+
86
+ # Log the results
87
+ debug_command("codex", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
88
+
89
+ if result.exit_status == 0
90
+ spinner.success("✓")
91
+ mark_completed
92
+ result.out
93
+ else
94
+ spinner.error("✗")
95
+ mark_failed("codex failed with exit code #{result.exit_status}")
96
+ debug_error(StandardError.new("codex failed"), {exit_code: result.exit_status, stderr: result.err})
97
+ raise "codex failed with exit code #{result.exit_status}: #{result.err}"
98
+ end
99
+ rescue => e
100
+ spinner&.error("✗")
101
+ mark_failed("codex execution failed: #{e.message}")
102
+ debug_error(e, {provider: "codex", prompt_length: prompt.length})
103
+ raise
104
+ ensure
105
+ cleanup_activity_display(activity_display_thread, spinner)
106
+ end
107
+ end
108
+
109
+ # Enhanced send method with additional options
110
+ def send_with_options(prompt:, session: nil, model: nil, ask_for_approval: false)
111
+ args = ["exec", prompt]
112
+
113
+ # Add session support
114
+ if session && !session.empty?
115
+ args += ["--session", session]
116
+ end
117
+
118
+ # Add model selection
119
+ if model
120
+ args += ["--model", model]
121
+ end
122
+
123
+ # Add approval flag
124
+ if ask_for_approval
125
+ args += ["--ask-for-approval"]
126
+ end
127
+
128
+ # Use the enhanced version of send
129
+ send_with_custom_args(prompt: prompt, args: args)
130
+ end
131
+
132
+ # Override health check for Codex specific considerations
133
+ def harness_healthy?
134
+ return false unless super
135
+
136
+ # Additional health checks specific to Codex CLI
137
+ # Check if we can access the CLI (basic connectivity test)
138
+ begin
139
+ result = Aidp::Util.execute_command("codex", ["--help"], timeout: 5)
140
+ result.exit_status == 0
141
+ rescue
142
+ false
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ # Internal helper for send_with_options - executes with custom arguments
149
+ def send_with_custom_args(prompt:, args:)
150
+ timeout_seconds = calculate_timeout
151
+
152
+ debug_provider("codex", "Starting execution", {timeout: timeout_seconds, args: args})
153
+ debug_log("📝 Sending prompt to codex with custom args", level: :info)
154
+
155
+ # Check if streaming mode is enabled
156
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
157
+ if streaming_enabled
158
+ display_message("📺 Display streaming enabled - output buffering reduced (codex CLI does not support true streaming)", type: :info)
159
+ end
160
+
161
+ setup_activity_monitoring("codex", method(:activity_callback))
162
+ record_activity("Starting codex execution with custom args")
163
+
164
+ begin
165
+ result = debug_execute_command("codex", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
166
+ debug_command("codex", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
167
+
168
+ if result.exit_status == 0
169
+ mark_completed
170
+ result.out
171
+ else
172
+ mark_failed("codex failed with exit code #{result.exit_status}")
173
+ debug_error(StandardError.new("codex failed"), {exit_code: result.exit_status, stderr: result.err})
174
+ raise "codex failed with exit code #{result.exit_status}: #{result.err}"
175
+ end
176
+ rescue => e
177
+ mark_failed("codex execution failed: #{e.message}")
178
+ debug_error(e, {provider: "codex", prompt_length: prompt.length})
179
+ raise
180
+ end
181
+ end
182
+
183
+ def calculate_timeout
184
+ # Priority order for timeout calculation:
185
+ # 1. Quick mode (for testing)
186
+ # 2. Environment variable override
187
+ # 3. Adaptive timeout based on step type
188
+ # 4. Default timeout
189
+
190
+ if ENV["AIDP_QUICK_MODE"]
191
+ display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
192
+ return TIMEOUT_QUICK_MODE
193
+ end
194
+
195
+ if ENV["AIDP_CODEX_TIMEOUT"]
196
+ return ENV["AIDP_CODEX_TIMEOUT"].to_i
197
+ end
198
+
199
+ # Adaptive timeout based on step type
200
+ step_timeout = get_adaptive_timeout
201
+ if step_timeout
202
+ display_message("🧠 Using adaptive timeout: #{step_timeout} seconds", type: :info)
203
+ return step_timeout
204
+ end
205
+
206
+ # Default timeout
207
+ display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
208
+ TIMEOUT_DEFAULT
209
+ end
210
+
211
+ def get_adaptive_timeout
212
+ # Timeout recommendations based on step type patterns
213
+ step_name = ENV["AIDP_CURRENT_STEP"] || ""
214
+
215
+ case step_name
216
+ when /REPOSITORY_ANALYSIS/
217
+ TIMEOUT_REPOSITORY_ANALYSIS
218
+ when /ARCHITECTURE_ANALYSIS/
219
+ TIMEOUT_ARCHITECTURE_ANALYSIS
220
+ when /TEST_ANALYSIS/
221
+ TIMEOUT_TEST_ANALYSIS
222
+ when /FUNCTIONALITY_ANALYSIS/
223
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
224
+ when /DOCUMENTATION_ANALYSIS/
225
+ TIMEOUT_DOCUMENTATION_ANALYSIS
226
+ when /STATIC_ANALYSIS/
227
+ TIMEOUT_STATIC_ANALYSIS
228
+ when /REFACTORING_RECOMMENDATIONS/
229
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
230
+ else
231
+ nil # Use default
232
+ end
233
+ end
234
+
235
+ def activity_callback(state, message, provider)
236
+ # Handle activity state changes
237
+ case state
238
+ when :stuck
239
+ display_message("\n⚠️ Codex CLI appears stuck: #{message}", type: :warning)
240
+ when :completed
241
+ display_message("\n✅ Codex CLI completed: #{message}", type: :success)
242
+ when :failed
243
+ display_message("\n❌ Codex CLI failed: #{message}", type: :error)
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -18,6 +18,10 @@ module Aidp
18
18
  "cursor"
19
19
  end
20
20
 
21
+ def display_name
22
+ "Cursor AI"
23
+ end
24
+
21
25
  def send(prompt:, session: nil)
22
26
  raise "cursor-agent not available" unless self.class.available?
23
27
 
@@ -27,10 +31,20 @@ module Aidp
27
31
  debug_provider("cursor", "Starting execution", {timeout: timeout_seconds})
28
32
  debug_log("📝 Sending prompt to cursor-agent (length: #{prompt.length})", level: :info)
29
33
 
34
+ # Check if streaming mode is enabled
35
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
36
+ if streaming_enabled
37
+ display_message("📺 Display streaming enabled - output buffering reduced (cursor-agent does not support true streaming)", type: :info)
38
+ end
39
+
30
40
  # Set up activity monitoring
31
41
  setup_activity_monitoring("cursor-agent", method(:activity_callback))
32
42
  record_activity("Starting cursor-agent execution")
33
43
 
44
+ # Create a spinner for activity display
45
+ spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
46
+ spinner.auto_spin
47
+
34
48
  # Start activity display thread with timeout
35
49
  activity_display_thread = Thread.new do
36
50
  start_time = Time.now
@@ -41,7 +55,7 @@ module Aidp
41
55
  # Break if we've been running too long or state changed
42
56
  break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
43
57
 
44
- print_activity_status(elapsed)
58
+ update_spinner_status(spinner, elapsed, "🔄 cursor-agent")
45
59
  end
46
60
  end
47
61
 
@@ -49,59 +63,38 @@ module Aidp
49
63
  # Use debug_execute_command for better debugging
50
64
  # Try agent command first (better for large prompts), fallback to -p mode
51
65
  begin
52
- result = debug_execute_command("cursor-agent", args: ["agent"], input: prompt, timeout: timeout_seconds)
66
+ result = debug_execute_command("cursor-agent", args: ["agent"], input: prompt, timeout: timeout_seconds, streaming: streaming_enabled)
53
67
  rescue => e
54
68
  # Fallback to -p mode if agent command fails
55
69
  debug_log("🔄 Falling back to -p mode: #{e.message}", level: :warn)
56
- result = debug_execute_command("cursor-agent", args: ["-p"], input: prompt, timeout: timeout_seconds)
70
+ result = debug_execute_command("cursor-agent", args: ["-p"], input: prompt, timeout: timeout_seconds, streaming: streaming_enabled)
57
71
  end
58
72
 
59
73
  # Log the results
60
74
  debug_command("cursor-agent", args: ["-p"], input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
61
75
 
62
- # Stop activity display
63
- activity_display_thread.kill if activity_display_thread.alive?
64
- activity_display_thread.join(0.1) # Give it 100ms to finish
65
- clear_activity_status
66
-
67
76
  if result.exit_status == 0
77
+ spinner.success("✓")
68
78
  mark_completed
69
79
  result.out
70
80
  else
81
+ spinner.error("✗")
71
82
  mark_failed("cursor-agent failed with exit code #{result.exit_status}")
72
83
  debug_error(StandardError.new("cursor-agent failed"), {exit_code: result.exit_status, stderr: result.err})
73
84
  raise "cursor-agent failed with exit code #{result.exit_status}: #{result.err}"
74
85
  end
75
86
  rescue => e
76
- # Stop activity display
77
- activity_display_thread.kill if activity_display_thread.alive?
78
- activity_display_thread.join(0.1) # Give it 100ms to finish
79
- clear_activity_status
87
+ spinner&.error("✗")
80
88
  mark_failed("cursor-agent execution failed: #{e.message}")
81
89
  debug_error(e, {provider: "cursor", prompt_length: prompt.length})
82
90
  raise
91
+ ensure
92
+ cleanup_activity_display(activity_display_thread, spinner)
83
93
  end
84
94
  end
85
95
 
86
96
  private
87
97
 
88
- def print_activity_status(elapsed)
89
- # Print activity status during cursor execution with elapsed time
90
- minutes = (elapsed / 60).to_i
91
- seconds = (elapsed % 60).to_i
92
-
93
- if minutes > 0
94
- print "\r🔄 cursor-agent is running... (#{minutes}m #{seconds}s)"
95
- else
96
- print "\r🔄 cursor-agent is running... (#{seconds}s)"
97
- end
98
- end
99
-
100
- def clear_activity_status
101
- # Clear the activity status line
102
- print "\r" + " " * 50 + "\r"
103
- end
104
-
105
98
  def calculate_timeout
106
99
  # Priority order for timeout calculation:
107
100
  # 1. Quick mode (for testing)
@@ -110,8 +103,8 @@ module Aidp
110
103
  # 4. Default timeout
111
104
 
112
105
  if ENV["AIDP_QUICK_MODE"]
113
- display_message("⚡ Quick mode enabled - 2 minute timeout", type: :highlight)
114
- return 120
106
+ display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
107
+ return TIMEOUT_QUICK_MODE
115
108
  end
116
109
 
117
110
  if ENV["AIDP_CURSOR_TIMEOUT"]
@@ -125,9 +118,9 @@ module Aidp
125
118
  return step_timeout
126
119
  end
127
120
 
128
- # Default timeout (5 minutes for interactive use)
129
- display_message("📋 Using default timeout: 5 minutes", type: :info)
130
- 300
121
+ # Default timeout
122
+ display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
123
+ TIMEOUT_DEFAULT
131
124
  end
132
125
 
133
126
  def get_adaptive_timeout
@@ -136,21 +129,21 @@ module Aidp
136
129
 
137
130
  case step_name
138
131
  when /REPOSITORY_ANALYSIS/
139
- 180 # 3 minutes - repository analysis can be quick
132
+ TIMEOUT_REPOSITORY_ANALYSIS
140
133
  when /ARCHITECTURE_ANALYSIS/
141
- 600 # 10 minutes - architecture analysis needs more time
134
+ TIMEOUT_ARCHITECTURE_ANALYSIS
142
135
  when /TEST_ANALYSIS/
143
- 300 # 5 minutes - test analysis is moderate
136
+ TIMEOUT_TEST_ANALYSIS
144
137
  when /FUNCTIONALITY_ANALYSIS/
145
- 600 # 10 minutes - functionality analysis is complex
138
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
146
139
  when /DOCUMENTATION_ANALYSIS/
147
- 300 # 5 minutes - documentation analysis is moderate
140
+ TIMEOUT_DOCUMENTATION_ANALYSIS
148
141
  when /STATIC_ANALYSIS/
149
- 450 # 7.5 minutes - static analysis can be intensive
142
+ TIMEOUT_STATIC_ANALYSIS
150
143
  when /REFACTORING_RECOMMENDATIONS/
151
- 600 # 10 minutes - refactoring recommendations are complex
144
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
152
145
  else
153
- nil # Use default
146
+ nil # Use default
154
147
  end
155
148
  end
156
149
 
@@ -16,6 +16,10 @@ module Aidp
16
16
  "gemini"
17
17
  end
18
18
 
19
+ def display_name
20
+ "Google Gemini"
21
+ end
22
+
19
23
  def send(prompt:, session: nil)
20
24
  raise "gemini CLI not available" unless self.class.available?
21
25
 
@@ -25,9 +29,15 @@ module Aidp
25
29
  debug_provider("gemini", "Starting execution", {timeout: timeout_seconds})
26
30
  debug_log("📝 Sending prompt to gemini...", level: :info)
27
31
 
32
+ # Check if streaming mode is enabled
33
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
34
+ if streaming_enabled
35
+ display_message("📺 Display streaming enabled - output buffering reduced (gemini CLI does not support true streaming)", type: :info)
36
+ end
37
+
28
38
  begin
29
- # Use debug_execute_command for better debugging
30
- result = debug_execute_command("gemini", args: ["--print"], input: prompt, timeout: timeout_seconds)
39
+ # Use debug_execute_command with streaming support
40
+ result = debug_execute_command("gemini", args: ["--print"], input: prompt, timeout: timeout_seconds, streaming: streaming_enabled)
31
41
 
32
42
  # Log the results
33
43
  debug_command("gemini", args: ["--print"], input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
@@ -54,8 +64,8 @@ module Aidp
54
64
  # 4. Default timeout
55
65
 
56
66
  if ENV["AIDP_QUICK_MODE"]
57
- display_message("⚡ Quick mode enabled - 2 minute timeout", type: :highlight)
58
- return 120
67
+ display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
68
+ return TIMEOUT_QUICK_MODE
59
69
  end
60
70
 
61
71
  if ENV["AIDP_GEMINI_TIMEOUT"]
@@ -69,9 +79,9 @@ module Aidp
69
79
  return step_timeout
70
80
  end
71
81
 
72
- # Default timeout (5 minutes for interactive use)
73
- display_message("📋 Using default timeout: 5 minutes", type: :info)
74
- 300
82
+ # Default timeout
83
+ display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
84
+ TIMEOUT_DEFAULT
75
85
  end
76
86
 
77
87
  def get_adaptive_timeout
@@ -80,21 +90,21 @@ module Aidp
80
90
 
81
91
  case step_name
82
92
  when /REPOSITORY_ANALYSIS/
83
- 180 # 3 minutes - repository analysis can be quick
93
+ TIMEOUT_REPOSITORY_ANALYSIS
84
94
  when /ARCHITECTURE_ANALYSIS/
85
- 600 # 10 minutes - architecture analysis needs more time
95
+ TIMEOUT_ARCHITECTURE_ANALYSIS
86
96
  when /TEST_ANALYSIS/
87
- 300 # 5 minutes - test analysis is moderate
97
+ TIMEOUT_TEST_ANALYSIS
88
98
  when /FUNCTIONALITY_ANALYSIS/
89
- 600 # 10 minutes - functionality analysis is complex
99
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
90
100
  when /DOCUMENTATION_ANALYSIS/
91
- 300 # 5 minutes - documentation analysis is moderate
101
+ TIMEOUT_DOCUMENTATION_ANALYSIS
92
102
  when /STATIC_ANALYSIS/
93
- 450 # 7.5 minutes - static analysis can be intensive
103
+ TIMEOUT_STATIC_ANALYSIS
94
104
  when /REFACTORING_RECOMMENDATIONS/
95
- 600 # 10 minutes - refactoring recommendations are complex
105
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
96
106
  else
97
- nil # Use default
107
+ nil # Use default
98
108
  end
99
109
  end
100
110
  end
@@ -18,6 +18,10 @@ module Aidp
18
18
  "github_copilot"
19
19
  end
20
20
 
21
+ def display_name
22
+ "GitHub Copilot CLI"
23
+ end
24
+
21
25
  def available?
22
26
  return false unless self.class.available?
23
27
 
@@ -39,10 +43,20 @@ module Aidp
39
43
  debug_provider("copilot", "Starting execution", {timeout: timeout_seconds})
40
44
  debug_log("📝 Sending prompt to copilot (length: #{prompt.length})", level: :info)
41
45
 
46
+ # Check if streaming mode is enabled
47
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
48
+ if streaming_enabled
49
+ display_message("📺 Display streaming enabled - output buffering reduced (copilot CLI does not support true streaming)", type: :info)
50
+ end
51
+
42
52
  # Set up activity monitoring
43
53
  setup_activity_monitoring("copilot", method(:activity_callback))
44
54
  record_activity("Starting copilot execution")
45
55
 
56
+ # Create a spinner for activity display
57
+ spinner = TTY::Spinner.new("[:spinner] :title", format: :dots, hide_cursor: true)
58
+ spinner.auto_spin
59
+
46
60
  # Start activity display thread with timeout
47
61
  activity_display_thread = Thread.new do
48
62
  start_time = Time.now
@@ -53,7 +67,7 @@ module Aidp
53
67
  # Break if we've been running too long or state changed
54
68
  break if elapsed > timeout_seconds || @activity_state == :completed || @activity_state == :failed
55
69
 
56
- print_activity_status(elapsed)
70
+ update_spinner_status(spinner, elapsed, "🤖 GitHub Copilot CLI")
57
71
  end
58
72
  end
59
73
 
@@ -67,32 +81,28 @@ module Aidp
67
81
  end
68
82
 
69
83
  # Use debug_execute_command for better debugging (no input since prompt is in args)
70
- result = debug_execute_command("copilot", args: args, timeout: timeout_seconds)
84
+ result = debug_execute_command("copilot", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
71
85
 
72
86
  # Log the results
73
87
  debug_command("copilot", args: args, input: prompt, output: result.out, error: result.err, exit_code: result.exit_status)
74
88
 
75
- # Stop activity display
76
- activity_display_thread.kill if activity_display_thread.alive?
77
- activity_display_thread.join(0.1) # Give it 100ms to finish
78
- clear_activity_status
79
-
80
89
  if result.exit_status == 0
90
+ spinner.success("✓")
81
91
  mark_completed
82
92
  result.out
83
93
  else
94
+ spinner.error("✗")
84
95
  mark_failed("copilot failed with exit code #{result.exit_status}")
85
96
  debug_error(StandardError.new("copilot failed"), {exit_code: result.exit_status, stderr: result.err})
86
97
  raise "copilot failed with exit code #{result.exit_status}: #{result.err}"
87
98
  end
88
99
  rescue => e
89
- # Stop activity display
90
- activity_display_thread.kill if activity_display_thread.alive?
91
- activity_display_thread.join(0.1) # Give it 100ms to finish
92
- clear_activity_status
100
+ spinner&.error("✗")
93
101
  mark_failed("copilot execution failed: #{e.message}")
94
102
  debug_error(e, {provider: "github_copilot", prompt_length: prompt.length})
95
103
  raise
104
+ ensure
105
+ cleanup_activity_display(activity_display_thread, spinner)
96
106
  end
97
107
  end
98
108
 
@@ -157,11 +167,17 @@ module Aidp
157
167
  debug_provider("copilot", "Starting execution", {timeout: timeout_seconds, args: args})
158
168
  debug_log("📝 Sending prompt to copilot with custom args", level: :info)
159
169
 
170
+ # Check if streaming mode is enabled
171
+ streaming_enabled = ENV["AIDP_STREAMING"] == "1" || ENV["DEBUG"] == "1"
172
+ if streaming_enabled
173
+ display_message("📺 Display streaming enabled - output buffering reduced (copilot CLI does not support true streaming)", type: :info)
174
+ end
175
+
160
176
  setup_activity_monitoring("copilot", method(:activity_callback))
161
177
  record_activity("Starting copilot execution with custom args")
162
178
 
163
179
  begin
164
- result = debug_execute_command("copilot", args: args, timeout: timeout_seconds)
180
+ result = debug_execute_command("copilot", args: args, timeout: timeout_seconds, streaming: streaming_enabled)
165
181
  debug_command("copilot", args: args, output: result.out, error: result.err, exit_code: result.exit_status)
166
182
 
167
183
  if result.exit_status == 0
@@ -179,23 +195,6 @@ module Aidp
179
195
  end
180
196
  end
181
197
 
182
- def print_activity_status(elapsed)
183
- # Print activity status during execution with elapsed time
184
- minutes = (elapsed / 60).to_i
185
- seconds = (elapsed % 60).to_i
186
-
187
- if minutes > 0
188
- print "\r🤖 GitHub Copilot CLI is running... (#{minutes}m #{seconds}s)"
189
- else
190
- print "\r🤖 GitHub Copilot CLI is running... (#{seconds}s)"
191
- end
192
- end
193
-
194
- def clear_activity_status
195
- # Clear the activity status line
196
- print "\r" + " " * 60 + "\r"
197
- end
198
-
199
198
  def calculate_timeout
200
199
  # Priority order for timeout calculation:
201
200
  # 1. Quick mode (for testing)
@@ -204,8 +203,8 @@ module Aidp
204
203
  # 4. Default timeout
205
204
 
206
205
  if ENV["AIDP_QUICK_MODE"]
207
- display_message("⚡ Quick mode enabled - 2 minute timeout", type: :highlight)
208
- return 120
206
+ display_message("⚡ Quick mode enabled - #{TIMEOUT_QUICK_MODE / 60} minute timeout", type: :highlight)
207
+ return TIMEOUT_QUICK_MODE
209
208
  end
210
209
 
211
210
  if ENV["AIDP_GITHUB_COPILOT_TIMEOUT"]
@@ -219,9 +218,9 @@ module Aidp
219
218
  return step_timeout
220
219
  end
221
220
 
222
- # Default timeout (5 minutes for interactive use)
223
- display_message("📋 Using default timeout: 5 minutes", type: :info)
224
- 300
221
+ # Default timeout
222
+ display_message("📋 Using default timeout: #{TIMEOUT_DEFAULT / 60} minutes", type: :info)
223
+ TIMEOUT_DEFAULT
225
224
  end
226
225
 
227
226
  def get_adaptive_timeout
@@ -230,21 +229,21 @@ module Aidp
230
229
 
231
230
  case step_name
232
231
  when /REPOSITORY_ANALYSIS/
233
- 180 # 3 minutes - repository analysis can be quick
232
+ TIMEOUT_REPOSITORY_ANALYSIS
234
233
  when /ARCHITECTURE_ANALYSIS/
235
- 600 # 10 minutes - architecture analysis needs more time
234
+ TIMEOUT_ARCHITECTURE_ANALYSIS
236
235
  when /TEST_ANALYSIS/
237
- 300 # 5 minutes - test analysis is moderate
236
+ TIMEOUT_TEST_ANALYSIS
238
237
  when /FUNCTIONALITY_ANALYSIS/
239
- 600 # 10 minutes - functionality analysis is complex
238
+ TIMEOUT_FUNCTIONALITY_ANALYSIS
240
239
  when /DOCUMENTATION_ANALYSIS/
241
- 300 # 5 minutes - documentation analysis is moderate
240
+ TIMEOUT_DOCUMENTATION_ANALYSIS
242
241
  when /STATIC_ANALYSIS/
243
- 450 # 7.5 minutes - static analysis can be intensive
242
+ TIMEOUT_STATIC_ANALYSIS
244
243
  when /REFACTORING_RECOMMENDATIONS/
245
- 600 # 10 minutes - refactoring recommendations are complex
244
+ TIMEOUT_REFACTORING_RECOMMENDATIONS
246
245
  else
247
- nil # Use default
246
+ nil # Use default
248
247
  end
249
248
  end
250
249