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
@@ -4,117 +4,126 @@ module Aidp
4
4
  module Execute
5
5
  module Steps
6
6
  # Simplified step specifications with fewer gates
7
+ # Templates are now organized by purpose (planning/, analysis/, implementation/)
8
+ # and named with action verbs for clarity
7
9
  SPEC = {
10
+ "00_LLM_STYLE_GUIDE" => {
11
+ "templates" => ["planning/generate_llm_style_guide.md"],
12
+ "description" => "Generate project-specific LLM Style Guide",
13
+ "outs" => ["docs/LLM_STYLE_GUIDE.md"],
14
+ "gate" => false,
15
+ "interactive" => false
16
+ },
8
17
  "00_PRD" => {
9
- "templates" => ["00_PRD.md"],
18
+ "templates" => ["planning/create_prd.md"],
10
19
  "description" => "Generate Product Requirements Document",
11
20
  "outs" => ["docs/prd.md"],
12
- "gate" => false, # Now auto-generated from user input
13
- "interactive" => true # Uses collected user input
21
+ "gate" => false, # Now auto-generated from user input
22
+ "interactive" => true # Uses collected user input
14
23
  },
15
24
  "01_NFRS" => {
16
- "templates" => ["01_NFRS.md"],
25
+ "templates" => ["planning/define_nfrs.md"],
17
26
  "description" => "Define Non-Functional Requirements",
18
27
  "outs" => ["docs/nfrs.md"],
19
- "gate" => false # Auto-generated
28
+ "gate" => false # Auto-generated
20
29
  },
21
30
  "02_ARCHITECTURE" => {
22
- "templates" => ["02_ARCHITECTURE.md"],
31
+ "templates" => ["planning/design_architecture.md"],
23
32
  "description" => "Design System Architecture",
24
33
  "outs" => ["docs/architecture.md"],
25
- "gate" => false # Auto-generated
34
+ "gate" => false # Auto-generated
26
35
  },
27
36
  "02A_ARCH_GATE_QUESTIONS" => {
28
- "templates" => ["02A_ARCH_GATE_QUESTIONS.md"],
37
+ "templates" => ["planning/ask_architecture_questions.md"],
29
38
  "description" => "Architecture Gate Questions",
30
39
  "outs" => ["docs/arch_gate_questions.md"],
31
40
  "gate" => true
32
41
  },
33
42
  "03_ADR_FACTORY" => {
34
- "templates" => ["03_ADR_FACTORY.md"],
43
+ "templates" => ["planning/generate_adrs.md"],
35
44
  "description" => "Generate Architecture Decision Records",
36
45
  "outs" => ["docs/adr/*.md"],
37
46
  "gate" => false
38
47
  },
39
48
  "04_DOMAIN_DECOMPOSITION" => {
40
- "templates" => ["04_DOMAIN_DECOMPOSITION.md"],
49
+ "templates" => ["planning/decompose_domain.md"],
41
50
  "description" => "Decompose Domain into Components",
42
51
  "outs" => ["docs/domain_decomposition.md"],
43
- "gate" => false # Auto-generated
52
+ "gate" => false # Auto-generated
44
53
  },
45
54
  "05_API_DESIGN" => {
46
- "templates" => ["05_CONTRACTS.md"],
55
+ "templates" => ["planning/design_apis.md"],
47
56
  "description" => "Design APIs and Interfaces",
48
57
  "outs" => ["docs/api_design.md"],
49
- "gate" => false # Auto-generated
58
+ "gate" => false # Auto-generated
50
59
  },
51
60
  "06_DATA_MODEL" => {
52
- "templates" => ["06_THREAT_MODEL.md"],
61
+ "templates" => ["planning/design_data_model.md"],
53
62
  "description" => "Design Data Model",
54
63
  "outs" => ["docs/data_model.md"],
55
64
  "gate" => true
56
65
  },
57
66
  "07_SECURITY_REVIEW" => {
58
- "templates" => ["07_TEST_PLAN.md"],
67
+ "templates" => ["planning/plan_testing.md"],
59
68
  "description" => "Security Review and Threat Model",
60
69
  "outs" => ["docs/security_review.md"],
61
70
  "gate" => true
62
71
  },
63
72
  "08_PERFORMANCE_REVIEW" => {
64
- "templates" => ["08_TASKS.md"],
73
+ "templates" => ["planning/create_tasks.md"],
65
74
  "description" => "Performance Review and Optimization",
66
75
  "outs" => ["docs/performance_review.md"],
67
76
  "gate" => true
68
77
  },
69
78
  "09_RELIABILITY_REVIEW" => {
70
- "templates" => ["09_SCAFFOLDING_DEVEX.md"],
79
+ "templates" => ["implementation/setup_scaffolding.md"],
71
80
  "description" => "Reliability Review and SLOs",
72
81
  "outs" => ["docs/reliability_review.md"],
73
82
  "gate" => true
74
83
  },
75
84
  "10_TESTING_STRATEGY" => {
76
- "templates" => ["10_IMPLEMENTATION_AGENT.md"],
85
+ "templates" => ["implementation/implement_features.md"],
77
86
  "description" => "Define Testing Strategy",
78
87
  "outs" => ["docs/testing_strategy.md"],
79
- "gate" => false # Auto-generated
88
+ "gate" => false # Auto-generated
80
89
  },
81
90
  "11_STATIC_ANALYSIS" => {
82
- "templates" => ["11_STATIC_ANALYSIS.md"],
91
+ "templates" => ["implementation/configure_static_analysis.md"],
83
92
  "description" => "Static Code Analysis",
84
93
  "outs" => ["docs/static_analysis.md"],
85
94
  "gate" => false
86
95
  },
87
96
  "12_OBSERVABILITY_SLOS" => {
88
- "templates" => ["12_OBSERVABILITY_SLOS.md"],
97
+ "templates" => ["planning/plan_observability.md"],
89
98
  "description" => "Define Observability and SLOs",
90
99
  "outs" => ["docs/observability_slos.md"],
91
100
  "gate" => true
92
101
  },
93
102
  "13_DELIVERY_ROLLOUT" => {
94
- "templates" => ["13_DELIVERY_ROLLOUT.md"],
103
+ "templates" => ["implementation/plan_delivery.md"],
95
104
  "description" => "Plan Delivery and Rollout",
96
105
  "outs" => ["docs/delivery_rollout.md"],
97
106
  "gate" => true
98
107
  },
99
108
  "14_DOCS_PORTAL" => {
100
- "templates" => ["14_DOCS_PORTAL.md"],
109
+ "templates" => ["implementation/create_documentation_portal.md"],
101
110
  "description" => "Documentation Portal",
102
111
  "outs" => ["docs/docs_portal.md"],
103
112
  "gate" => false
104
113
  },
105
114
  "15_POST_RELEASE" => {
106
- "templates" => ["15_POST_RELEASE.md"],
115
+ "templates" => ["implementation/review_post_release.md"],
107
116
  "description" => "Post-Release Review",
108
117
  "outs" => ["docs/post_release.md"],
109
- "gate" => false # Auto-generated
118
+ "gate" => false # Auto-generated
110
119
  },
111
120
  # New implementation step for actual development work
112
121
  "16_IMPLEMENTATION" => {
113
- "templates" => ["10_IMPLEMENTATION_AGENT.md"], # Reuse existing implementation template
122
+ "templates" => ["implementation/implement_features.md"], # Reuse existing implementation template
114
123
  "description" => "Execute Implementation Tasks",
115
124
  "outs" => ["implementation_log.md"],
116
125
  "gate" => false,
117
- "implementation" => true # Special step that runs development tasks
126
+ "implementation" => true # Special step that runs development tasks
118
127
  }
119
128
  }.freeze
120
129
  end
@@ -0,0 +1,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "prompt_manager"
4
+ require_relative "checkpoint"
5
+ require_relative "checkpoint_display"
6
+ require_relative "../harness/test_runner"
7
+
8
+ module Aidp
9
+ module Execute
10
+ # Executes work loops for a single step
11
+ # Responsibilities:
12
+ # - Create initial PROMPT.md from templates and context
13
+ # - Loop: send PROMPT.md to agent, run tests/linters, check completion
14
+ # - Only send test/lint failures back to agent
15
+ # - Track iteration count
16
+ # - Record periodic checkpoints with metrics
17
+ class WorkLoopRunner
18
+ include Aidp::MessageDisplay
19
+
20
+ attr_reader :iteration_count, :project_dir
21
+
22
+ MAX_ITERATIONS = 50 # Safety limit
23
+ CHECKPOINT_INTERVAL = 5 # Record checkpoint every N iterations
24
+
25
+ def initialize(project_dir, provider_manager, config, options = {})
26
+ @project_dir = project_dir
27
+ @provider_manager = provider_manager
28
+ @config = config
29
+ @prompt_manager = PromptManager.new(project_dir)
30
+ @test_runner = Aidp::Harness::TestRunner.new(project_dir, config)
31
+ @checkpoint = Checkpoint.new(project_dir)
32
+ @checkpoint_display = CheckpointDisplay.new
33
+ @iteration_count = 0
34
+ @step_name = nil
35
+ @options = options
36
+ end
37
+
38
+ # Execute a step using work loop pattern
39
+ # Returns final result when step is complete
40
+ def execute_step(step_name, step_spec, context = {})
41
+ @step_name = step_name
42
+ @iteration_count = 0
43
+
44
+ display_message("🔄 Starting work loop for step: #{step_name}", type: :info)
45
+
46
+ # Create initial PROMPT.md
47
+ create_initial_prompt(step_spec, context)
48
+
49
+ # Main work loop
50
+ loop do
51
+ @iteration_count += 1
52
+ display_message(" Iteration #{@iteration_count}", type: :info)
53
+
54
+ break if @iteration_count > MAX_ITERATIONS
55
+
56
+ # Send PROMPT.md to agent
57
+ result = send_to_agent
58
+
59
+ # Run tests and linters
60
+ test_results = @test_runner.run_tests
61
+ lint_results = @test_runner.run_linters
62
+
63
+ # Record checkpoint at intervals
64
+ record_periodic_checkpoint(test_results, lint_results)
65
+
66
+ # Check if step is complete
67
+ if step_complete?(result, test_results, lint_results)
68
+ # Record final checkpoint
69
+ record_final_checkpoint(test_results, lint_results)
70
+ display_message("✅ Step #{step_name} completed after #{@iteration_count} iterations", type: :success)
71
+ archive_and_cleanup
72
+ return build_success_result(result)
73
+ end
74
+
75
+ # If not complete, prepare next iteration with failures (if any)
76
+ prepare_next_iteration(test_results, lint_results)
77
+ end
78
+
79
+ # Safety: max iterations reached
80
+ display_message("⚠️ Max iterations (#{MAX_ITERATIONS}) reached for #{step_name}", type: :warning)
81
+ archive_and_cleanup
82
+ build_max_iterations_result
83
+ end
84
+
85
+ private
86
+
87
+ # Create initial PROMPT.md with all context
88
+ def create_initial_prompt(step_spec, context)
89
+ template_content = load_template(step_spec["templates"]&.first)
90
+ prd_content = load_prd
91
+ style_guide = load_style_guide
92
+ user_input = format_user_input(context[:user_input])
93
+
94
+ initial_prompt = build_initial_prompt_content(
95
+ template: template_content,
96
+ prd: prd_content,
97
+ style_guide: style_guide,
98
+ user_input: user_input,
99
+ step_name: @step_name
100
+ )
101
+
102
+ @prompt_manager.write(initial_prompt)
103
+ display_message(" Created PROMPT.md (#{initial_prompt.length} chars)", type: :info)
104
+ end
105
+
106
+ def build_initial_prompt_content(template:, prd:, style_guide:, user_input:, step_name:)
107
+ parts = []
108
+
109
+ parts << "# Work Loop: #{step_name}"
110
+ parts << ""
111
+ parts << "## Instructions"
112
+ parts << "You are working in a work loop. Your responsibilities:"
113
+ parts << "1. Read this PROMPT.md file to understand what needs to be done"
114
+ parts << "2. Complete the work described below"
115
+ parts << "3. **IMPORTANT**: Edit this PROMPT.md file yourself to:"
116
+ parts << " - Remove completed items"
117
+ parts << " - Update with current status"
118
+ parts << " - Keep it concise (remove unnecessary context)"
119
+ parts << " - Mark the step COMPLETE when 100% done"
120
+ parts << "4. After you finish, tests and linters will run automatically"
121
+ parts << "5. If tests/linters fail, you'll see the errors in the next iteration"
122
+ parts << ""
123
+ parts << "## Completion Criteria"
124
+ parts << "Mark this step COMPLETE by adding this line to PROMPT.md:"
125
+ parts << "```"
126
+ parts << "STATUS: COMPLETE"
127
+ parts << "```"
128
+ parts << ""
129
+
130
+ if user_input && !user_input.empty?
131
+ parts << "## User Input"
132
+ parts << user_input
133
+ parts << ""
134
+ end
135
+
136
+ if style_guide
137
+ parts << "## LLM Style Guide"
138
+ parts << style_guide
139
+ parts << ""
140
+ end
141
+
142
+ if prd
143
+ parts << "## Product Requirements (PRD)"
144
+ parts << prd
145
+ parts << ""
146
+ end
147
+
148
+ parts << "## Task Template"
149
+ parts << template
150
+ parts << ""
151
+
152
+ parts.join("\n")
153
+ end
154
+
155
+ def send_to_agent
156
+ prompt_content = @prompt_manager.read
157
+ return {status: "error", message: "PROMPT.md not found"} unless prompt_content
158
+
159
+ # Send to provider via provider_manager
160
+ @provider_manager.execute_with_provider(
161
+ @provider_manager.current_provider,
162
+ prompt_content,
163
+ {
164
+ step_name: @step_name,
165
+ iteration: @iteration_count,
166
+ project_dir: @project_dir
167
+ }
168
+ )
169
+ end
170
+
171
+ def step_complete?(agent_result, test_results, lint_results)
172
+ # Check if agent marked step complete
173
+ agent_complete = agent_result[:status] == "completed" || prompt_marked_complete?
174
+
175
+ # Check if tests and linters pass
176
+ tests_pass = test_results[:success]
177
+ linters_pass = lint_results[:success]
178
+
179
+ agent_complete && tests_pass && linters_pass
180
+ end
181
+
182
+ def prompt_marked_complete?
183
+ prompt_content = @prompt_manager.read
184
+ return false unless prompt_content
185
+
186
+ # Check for STATUS: COMPLETE marker
187
+ prompt_content.match?(/^STATUS:\s*COMPLETE/i)
188
+ end
189
+
190
+ def prepare_next_iteration(test_results, lint_results)
191
+ # Only append failures to PROMPT.md for agent to see
192
+ failures = []
193
+
194
+ unless test_results[:success]
195
+ failures << "## Test Failures"
196
+ failures << test_results[:output]
197
+ failures << ""
198
+ end
199
+
200
+ unless lint_results[:success]
201
+ failures << "## Linter Failures"
202
+ failures << lint_results[:output]
203
+ failures << ""
204
+ end
205
+
206
+ return if failures.empty?
207
+
208
+ # Append failures to PROMPT.md
209
+ current_prompt = @prompt_manager.read
210
+ updated_prompt = current_prompt + "\n\n---\n\n" + failures.join("\n")
211
+ @prompt_manager.write(updated_prompt)
212
+
213
+ display_message(" Added failure reports to PROMPT.md", type: :warning)
214
+ end
215
+
216
+ def archive_and_cleanup
217
+ @prompt_manager.archive(@step_name)
218
+ @prompt_manager.delete
219
+ end
220
+
221
+ def load_template(template_name)
222
+ return "" unless template_name
223
+
224
+ # Template name now includes subdirectory (e.g., "planning/create_prd.md")
225
+ template_path = File.join(@project_dir, "templates", template_name)
226
+ return File.read(template_path) if File.exist?(template_path)
227
+
228
+ # Fallback: try COMMON directory
229
+ common_path = File.join(@project_dir, "templates", "COMMON", template_name)
230
+ return File.read(common_path) if File.exist?(common_path)
231
+
232
+ ""
233
+ end
234
+
235
+ def load_prd
236
+ prd_path = File.join(@project_dir, "docs", "prd.md")
237
+ File.exist?(prd_path) ? File.read(prd_path) : nil
238
+ end
239
+
240
+ def load_style_guide
241
+ style_guide_path = File.join(@project_dir, "docs", "LLM_STYLE_GUIDE.md")
242
+ File.exist?(style_guide_path) ? File.read(style_guide_path) : nil
243
+ end
244
+
245
+ def format_user_input(user_input)
246
+ return nil if user_input.nil? || user_input.empty?
247
+
248
+ lines = user_input.map { |key, value| "- **#{key}**: #{value}" }
249
+ lines.join("\n")
250
+ end
251
+
252
+ def build_success_result(agent_result)
253
+ {
254
+ status: "completed",
255
+ message: "Step #{@step_name} completed successfully",
256
+ iterations: @iteration_count,
257
+ final_result: agent_result
258
+ }
259
+ end
260
+
261
+ def build_max_iterations_result
262
+ {
263
+ status: "error",
264
+ message: "Maximum iterations reached",
265
+ iterations: @iteration_count,
266
+ error: "Step did not complete within #{MAX_ITERATIONS} iterations"
267
+ }
268
+ end
269
+
270
+ # Record checkpoint at regular intervals
271
+ def record_periodic_checkpoint(test_results, lint_results)
272
+ # Record every CHECKPOINT_INTERVAL iterations or on iteration 1
273
+ return unless @iteration_count == 1 || (@iteration_count % CHECKPOINT_INTERVAL == 0)
274
+
275
+ metrics = {
276
+ tests_passing: test_results[:success],
277
+ linters_passing: lint_results[:success]
278
+ }
279
+
280
+ checkpoint_data = @checkpoint.record_checkpoint(@step_name, @iteration_count, metrics)
281
+
282
+ # Display inline progress
283
+ @checkpoint_display.display_inline_progress(@iteration_count, checkpoint_data[:metrics])
284
+
285
+ # Show detailed checkpoint every 10 iterations
286
+ if @iteration_count % 10 == 0
287
+ @checkpoint_display.display_checkpoint(checkpoint_data)
288
+ end
289
+ end
290
+
291
+ # Record final checkpoint when step completes
292
+ def record_final_checkpoint(test_results, lint_results)
293
+ metrics = {
294
+ tests_passing: test_results[:success],
295
+ linters_passing: lint_results[:success],
296
+ completed: true
297
+ }
298
+
299
+ checkpoint_data = @checkpoint.record_checkpoint(@step_name, @iteration_count, metrics)
300
+ @checkpoint_display.display_checkpoint(checkpoint_data, show_details: true)
301
+
302
+ # Display progress summary
303
+ summary = @checkpoint.progress_summary
304
+ @checkpoint_display.display_progress_summary(summary) if summary
305
+ end
306
+ end
307
+ end
308
+ end
@@ -1,42 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "tty-prompt"
4
+ require_relative "../workflows/definitions"
5
+ require_relative "../workflows/selector"
4
6
 
5
7
  module Aidp
6
8
  module Execute
7
9
  # Handles interactive workflow selection and project setup
8
10
  class WorkflowSelector
11
+ include Aidp::MessageDisplay
12
+
9
13
  def initialize(prompt: TTY::Prompt.new)
10
14
  @user_input = {}
11
15
  @prompt = prompt
16
+ @workflow_selector = Aidp::Workflows::Selector.new(prompt: @prompt)
12
17
  end
13
18
 
14
19
  # Main entry point for interactive workflow selection
15
- def select_workflow(harness_mode: false)
20
+ def select_workflow(harness_mode: false, use_new_selector: true, mode: nil)
16
21
  if harness_mode
17
22
  # In harness mode, use default values to avoid blocking
18
23
  select_workflow_with_defaults
24
+ elsif use_new_selector
25
+ # Use new unified workflow selector (default as of Issue #79)
26
+ select_workflow_with_new_selector(mode)
19
27
  else
20
- # Interactive mode for standalone usage
28
+ # Legacy interactive mode for backward compatibility
21
29
  select_workflow_interactive
22
30
  end
23
31
  end
24
32
 
25
33
  private
26
34
 
27
- # Helper method for consistent message display using TTY::Prompt
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
- when :muted then :bright_black
36
- else :white
37
- end
35
+ def select_workflow_with_new_selector(mode = nil)
36
+ # Step 1: Collect project information (still useful for all workflows)
37
+ collect_project_info
38
38
 
39
- @prompt.say(message, color: color)
39
+ # Step 2: Use new workflow selector, defaulting to execute mode if not specified
40
+ workflow_mode = mode || :execute
41
+ result = @workflow_selector.select_workflow(workflow_mode)
42
+
43
+ {
44
+ workflow_type: result[:workflow_key],
45
+ steps: result[:steps],
46
+ user_input: @user_input,
47
+ workflow: result[:workflow]
48
+ }
40
49
  end
41
50
 
42
51
  def select_workflow_interactive
@@ -144,10 +153,10 @@ module Aidp
144
153
 
145
154
  def exploration_workflow_steps
146
155
  [
147
- "00_PRD", # Generate PRD from user input (no manual gate)
156
+ "00_PRD", # Generate PRD from user input (no manual gate)
148
157
  "10_TESTING_STRATEGY", # Ensure we have tests
149
- "11_STATIC_ANALYSIS", # Code quality
150
- "16_IMPLEMENTATION" # Special step for actual development work
158
+ "11_STATIC_ANALYSIS", # Code quality
159
+ "16_IMPLEMENTATION" # Special step for actual development work
151
160
  ]
152
161
  end
153
162
 
@@ -1310,13 +1310,13 @@ module Aidp
1310
1310
  # Get timeout duration for operation type
1311
1311
  def get_timeout_duration(operation_type, configuration = nil)
1312
1312
  default_timeouts = {
1313
- analyze: 300, # 5 minutes
1314
- execute: 600, # 10 minutes
1313
+ analyze: 300, # 5 minutes
1314
+ execute: 600, # 10 minutes
1315
1315
  provider_call: 120, # 2 minutes
1316
1316
  file_operation: 30, # 30 seconds
1317
1317
  network_request: 60, # 1 minute
1318
- user_input: 300, # 5 minutes
1319
- default: 120 # 2 minutes
1318
+ user_input: 300, # 5 minutes
1319
+ default: 120 # 2 minutes
1320
1320
  }
1321
1321
 
1322
1322
  # Get timeout from configuration if available
@@ -366,6 +366,46 @@ module Aidp
366
366
  enum: ["provider_model", "provider", "model", "none"]
367
367
  }
368
368
  }
369
+ },
370
+ work_loop: {
371
+ type: :hash,
372
+ required: false,
373
+ default: {
374
+ enabled: true,
375
+ max_iterations: 50,
376
+ test_commands: [],
377
+ lint_commands: []
378
+ },
379
+ properties: {
380
+ enabled: {
381
+ type: :boolean,
382
+ required: false,
383
+ default: true
384
+ },
385
+ max_iterations: {
386
+ type: :integer,
387
+ required: false,
388
+ default: 50,
389
+ min: 1,
390
+ max: 200
391
+ },
392
+ test_commands: {
393
+ type: :array,
394
+ required: false,
395
+ default: [],
396
+ items: {
397
+ type: :string
398
+ }
399
+ },
400
+ lint_commands: {
401
+ type: :array,
402
+ required: false,
403
+ default: [],
404
+ items: {
405
+ type: :string
406
+ }
407
+ }
408
+ }
369
409
  }
370
410
  }
371
411
  },
@@ -76,8 +76,9 @@ module Aidp
76
76
  return false if config_exists?
77
77
 
78
78
  example_config = ConfigSchema.generate_example
79
- config_path = File.join(@project_dir, "aidp.yml")
79
+ config_path = File.join(@project_dir, ".aidp", "aidp.yml")
80
80
 
81
+ FileUtils.mkdir_p(File.dirname(config_path))
81
82
  File.write(config_path, YAML.dump(example_config))
82
83
  true
83
84
  end
@@ -243,14 +244,10 @@ module Aidp
243
244
  private
244
245
 
245
246
  def find_config_file
246
- # Try new aidp.yml format first, then fall back to .aidp.yml
247
- config_file = File.join(@project_dir, "aidp.yml")
248
- legacy_config_file = File.join(@project_dir, ".aidp.yml")
247
+ config_file = File.join(@project_dir, ".aidp", "aidp.yml")
249
248
 
250
249
  if File.exist?(config_file)
251
250
  config_file
252
- elsif File.exist?(legacy_config_file)
253
- legacy_config_file
254
251
  end
255
252
  end
256
253