aidp 0.9.6 → 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 (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +194 -25
  3. data/lib/aidp/analyze/error_handler.rb +4 -2
  4. data/lib/aidp/{analysis → analyze}/kb_inspector.rb +93 -89
  5. data/lib/aidp/analyze/prioritizer.rb +3 -2
  6. data/lib/aidp/analyze/progress.rb +2 -1
  7. data/lib/aidp/analyze/ruby_maat_integration.rb +7 -3
  8. data/lib/aidp/analyze/runner.rb +73 -11
  9. data/lib/aidp/{analysis → analyze}/seams.rb +1 -1
  10. data/lib/aidp/analyze/steps.rb +10 -8
  11. data/lib/aidp/{analysis → analyze}/tree_sitter_grammar_loader.rb +11 -5
  12. data/lib/aidp/{analysis → analyze}/tree_sitter_scan.rb +21 -15
  13. data/lib/aidp/cli/checkpoint_command.rb +98 -0
  14. data/lib/aidp/cli/first_run_wizard.rb +83 -103
  15. data/lib/aidp/cli/jobs_command.rb +270 -36
  16. data/lib/aidp/cli/terminal_io.rb +3 -3
  17. data/lib/aidp/cli.rb +411 -69
  18. data/lib/aidp/config.rb +5 -8
  19. data/lib/aidp/debug_logger.rb +4 -4
  20. data/lib/aidp/debug_mixin.rb +11 -4
  21. data/lib/aidp/execute/checkpoint.rb +282 -0
  22. data/lib/aidp/execute/checkpoint_display.rb +221 -0
  23. data/lib/aidp/execute/progress.rb +2 -1
  24. data/lib/aidp/execute/prompt_manager.rb +62 -0
  25. data/lib/aidp/execute/runner.rb +67 -20
  26. data/lib/aidp/execute/steps.rb +36 -27
  27. data/lib/aidp/execute/work_loop_runner.rb +308 -0
  28. data/lib/aidp/execute/workflow_selector.rb +50 -26
  29. data/lib/aidp/harness/condition_detector.rb +4 -4
  30. data/lib/aidp/harness/config_schema.rb +40 -0
  31. data/lib/aidp/harness/config_validator.rb +3 -6
  32. data/lib/aidp/harness/configuration.rb +35 -1
  33. data/lib/aidp/harness/enhanced_runner.rb +25 -4
  34. data/lib/aidp/harness/error_handler.rb +103 -28
  35. data/lib/aidp/harness/provider_factory.rb +6 -1
  36. data/lib/aidp/harness/provider_manager.rb +273 -19
  37. data/lib/aidp/harness/runner.rb +14 -6
  38. data/lib/aidp/harness/simple_user_interface.rb +6 -4
  39. data/lib/aidp/harness/status_display.rb +118 -106
  40. data/lib/aidp/harness/test_runner.rb +83 -0
  41. data/lib/aidp/harness/ui/enhanced_tui.rb +7 -5
  42. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +22 -4
  43. data/lib/aidp/harness/ui/error_handler.rb +7 -2
  44. data/lib/aidp/harness/ui/frame_manager.rb +61 -39
  45. data/lib/aidp/harness/ui/job_monitor.rb +2 -0
  46. data/lib/aidp/harness/ui/navigation/main_menu.rb +27 -16
  47. data/lib/aidp/harness/ui/navigation/menu_item.rb +1 -0
  48. data/lib/aidp/harness/ui/navigation/menu_state.rb +1 -0
  49. data/lib/aidp/harness/ui/navigation/submenu.rb +1 -0
  50. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +2 -0
  51. data/lib/aidp/harness/ui/progress_display.rb +26 -7
  52. data/lib/aidp/harness/ui/question_collector.rb +2 -0
  53. data/lib/aidp/harness/ui/spinner_group.rb +2 -0
  54. data/lib/aidp/harness/ui/spinner_helper.rb +1 -1
  55. data/lib/aidp/harness/ui/status_manager.rb +4 -2
  56. data/lib/aidp/harness/ui/status_widget.rb +20 -9
  57. data/lib/aidp/harness/ui/workflow_controller.rb +27 -9
  58. data/lib/aidp/harness/user_interface.rb +338 -330
  59. data/lib/aidp/jobs/background_runner.rb +278 -0
  60. data/lib/aidp/message_display.rb +48 -0
  61. data/lib/aidp/provider_manager.rb +13 -7
  62. data/lib/aidp/providers/anthropic.rb +101 -18
  63. data/lib/aidp/providers/base.rb +51 -1
  64. data/lib/aidp/providers/codex.rb +248 -0
  65. data/lib/aidp/providers/cursor.rb +39 -48
  66. data/lib/aidp/providers/gemini.rb +26 -16
  67. data/lib/aidp/providers/github_copilot.rb +263 -0
  68. data/lib/aidp/providers/opencode.rb +38 -47
  69. data/lib/aidp/version.rb +1 -1
  70. data/lib/aidp/workflows/definitions.rb +357 -0
  71. data/lib/aidp/workflows/selector.rb +171 -0
  72. data/lib/aidp.rb +16 -4
  73. data/templates/planning/generate_llm_style_guide.md +119 -0
  74. metadata +43 -31
  75. data/lib/aidp/analyze/progress_visualizer.rb +0 -314
  76. /data/templates/{ANALYZE/02_ARCHITECTURE_ANALYSIS.md → analysis/analyze_architecture.md} +0 -0
  77. /data/templates/{ANALYZE/05_DOCUMENTATION_ANALYSIS.md → analysis/analyze_documentation.md} +0 -0
  78. /data/templates/{ANALYZE/04_FUNCTIONALITY_ANALYSIS.md → analysis/analyze_functionality.md} +0 -0
  79. /data/templates/{ANALYZE/01_REPOSITORY_ANALYSIS.md → analysis/analyze_repository.md} +0 -0
  80. /data/templates/{ANALYZE/06_STATIC_ANALYSIS.md → analysis/analyze_static_code.md} +0 -0
  81. /data/templates/{ANALYZE/03_TEST_ANALYSIS.md → analysis/analyze_tests.md} +0 -0
  82. /data/templates/{ANALYZE/07_REFACTORING_RECOMMENDATIONS.md → analysis/recommend_refactoring.md} +0 -0
  83. /data/templates/{ANALYZE/06a_tree_sitter_scan.md → analysis/scan_with_tree_sitter.md} +0 -0
  84. /data/templates/{EXECUTE/11_STATIC_ANALYSIS.md → implementation/configure_static_analysis.md} +0 -0
  85. /data/templates/{EXECUTE/14_DOCS_PORTAL.md → implementation/create_documentation_portal.md} +0 -0
  86. /data/templates/{EXECUTE/10_IMPLEMENTATION_AGENT.md → implementation/implement_features.md} +0 -0
  87. /data/templates/{EXECUTE/13_DELIVERY_ROLLOUT.md → implementation/plan_delivery.md} +0 -0
  88. /data/templates/{EXECUTE/15_POST_RELEASE.md → implementation/review_post_release.md} +0 -0
  89. /data/templates/{EXECUTE/09_SCAFFOLDING_DEVEX.md → implementation/setup_scaffolding.md} +0 -0
  90. /data/templates/{EXECUTE/02A_ARCH_GATE_QUESTIONS.md → planning/ask_architecture_questions.md} +0 -0
  91. /data/templates/{EXECUTE/00_PRD.md → planning/create_prd.md} +0 -0
  92. /data/templates/{EXECUTE/08_TASKS.md → planning/create_tasks.md} +0 -0
  93. /data/templates/{EXECUTE/04_DOMAIN_DECOMPOSITION.md → planning/decompose_domain.md} +0 -0
  94. /data/templates/{EXECUTE/01_NFRS.md → planning/define_nfrs.md} +0 -0
  95. /data/templates/{EXECUTE/05_CONTRACTS.md → planning/design_apis.md} +0 -0
  96. /data/templates/{EXECUTE/02_ARCHITECTURE.md → planning/design_architecture.md} +0 -0
  97. /data/templates/{EXECUTE/06_THREAT_MODEL.md → planning/design_data_model.md} +0 -0
  98. /data/templates/{EXECUTE/03_ADR_FACTORY.md → planning/generate_adrs.md} +0 -0
  99. /data/templates/{EXECUTE/12_OBSERVABILITY_SLOS.md → planning/plan_observability.md} +0 -0
  100. /data/templates/{EXECUTE/07_TEST_PLAN.md → planning/plan_testing.md} +0 -0
@@ -1,23 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "tty-prompt"
3
4
  require_relative "steps"
4
5
  require_relative "progress"
6
+ require_relative "work_loop_runner"
5
7
  require_relative "../storage/file_manager"
6
8
 
7
9
  module Aidp
8
10
  module Execute
9
11
  class Runner
10
- def initialize(project_dir, harness_runner = nil)
12
+ include Aidp::MessageDisplay
13
+
14
+ def initialize(project_dir, harness_runner = nil, prompt: TTY::Prompt.new)
11
15
  @project_dir = project_dir
12
16
  @harness_runner = harness_runner
13
17
  @is_harness_mode = !harness_runner.nil?
14
18
  @file_manager = Aidp::Storage::FileManager.new(File.join(project_dir, ".aidp"))
19
+ @prompt = prompt
15
20
  end
16
21
 
17
22
  def progress
18
23
  @progress ||= Aidp::Execute::Progress.new(@project_dir)
19
24
  end
20
25
 
26
+ private
27
+
28
+ public
29
+
21
30
  def run_step(step_name, options = {})
22
31
  # Always validate step exists first
23
32
  step_spec = Aidp::Execute::Steps::SPEC[step_name]
@@ -33,23 +42,22 @@ module Aidp
33
42
 
34
43
  # Harness-aware step execution
35
44
  def run_step_with_harness(step_name, options = {})
36
- # Get current provider from harness
37
- current_provider = @harness_runner.instance_variable_get(:@current_provider)
38
- provider_type = current_provider || "cursor"
39
-
40
- # Compose prompt with harness context
41
- prompt = composed_prompt_with_harness_context(step_name, options)
42
-
43
- # Execute with harness error handling
44
- result = execute_with_harness_provider(provider_type, prompt, step_name, options)
45
+ step_spec = Aidp::Execute::Steps::SPEC[step_name]
45
46
 
46
- # Process result for harness
47
- process_result_for_harness(result, step_name, options)
47
+ # Check if work loops are enabled in configuration
48
+ config = @harness_runner.instance_variable_get(:@configuration)
49
+ if config&.work_loop_enabled?
50
+ # Use WorkLoopRunner for execution
51
+ run_step_with_work_loop(step_name, step_spec, options)
52
+ else
53
+ # Use traditional single-pass execution
54
+ run_step_traditional(step_name, step_spec, options)
55
+ end
48
56
  end
49
57
 
50
58
  # Standalone step execution (simplified - synchronous)
51
59
  def run_step_standalone(step_name, options = {})
52
- puts "🚀 Running execution step synchronously: #{step_name}"
60
+ display_message("🚀 Running execution step synchronously: #{step_name}", type: :info)
53
61
 
54
62
  start_time = Time.now
55
63
  prompt = composed_prompt(step_name, options)
@@ -62,7 +70,7 @@ module Aidp
62
70
  # Store execution metrics
63
71
  @file_manager.record_step_execution(step_name, "cursor", duration, result[:status] == "completed")
64
72
 
65
- puts "✅ Execution step completed in #{duration.round(2)}s"
73
+ display_message("✅ Execution step completed in #{duration.round(2)}s", type: :success)
66
74
  result
67
75
  end
68
76
 
@@ -136,6 +144,42 @@ module Aidp
136
144
 
137
145
  private
138
146
 
147
+ # Execute step with work loop
148
+ def run_step_with_work_loop(step_name, step_spec, options)
149
+ config = @harness_runner.instance_variable_get(:@configuration)
150
+ provider_manager = @harness_runner.provider_manager
151
+
152
+ # Create work loop runner
153
+ work_loop_runner = WorkLoopRunner.new(
154
+ @project_dir,
155
+ provider_manager,
156
+ config,
157
+ options
158
+ )
159
+
160
+ # Execute with work loop
161
+ result = work_loop_runner.execute_step(step_name, step_spec, options)
162
+
163
+ # Process result for harness
164
+ process_result_for_harness(result, step_name, options)
165
+ end
166
+
167
+ # Execute step traditionally (single pass)
168
+ def run_step_traditional(step_name, step_spec, options)
169
+ # Get current provider from harness
170
+ current_provider = @harness_runner.current_provider
171
+ provider_type = current_provider || "cursor"
172
+
173
+ # Compose prompt with harness context
174
+ prompt = composed_prompt_with_harness_context(step_name, options)
175
+
176
+ # Execute with harness error handling
177
+ result = execute_with_harness_provider(provider_type, prompt, step_name, options)
178
+
179
+ # Process result for harness
180
+ process_result_for_harness(result, step_name, options)
181
+ end
182
+
139
183
  # Simple synchronous step execution
140
184
  def execute_step_synchronously(step_name, prompt, options)
141
185
  # Execute step synchronously with provider
@@ -172,11 +216,11 @@ module Aidp
172
216
  # Add current execution context
173
217
  context_parts << "## Execution Context"
174
218
  context_parts << "Project Directory: #{@project_dir}"
175
- context_parts << "Current Step: #{@harness_runner.instance_variable_get(:@current_step)}"
176
- context_parts << "Current Provider: #{@harness_runner.instance_variable_get(:@current_provider)}"
219
+ context_parts << "Current Step: #{@harness_runner.current_step}"
220
+ context_parts << "Current Provider: #{@harness_runner.current_provider}"
177
221
 
178
222
  # Add user input context
179
- user_input = @harness_runner.instance_variable_get(:@user_input)
223
+ user_input = @harness_runner.user_input
180
224
  if user_input && !user_input.empty?
181
225
  context_parts << "\n## Previous User Input"
182
226
  user_input.each do |key, value|
@@ -185,7 +229,7 @@ module Aidp
185
229
  end
186
230
 
187
231
  # Add execution history context
188
- execution_log = @harness_runner.instance_variable_get(:@execution_log)
232
+ execution_log = @harness_runner.execution_log
189
233
  if execution_log && !execution_log.empty?
190
234
  context_parts << "\n## Execution History"
191
235
  recent_logs = execution_log.last(5) # Last 5 entries
@@ -200,7 +244,7 @@ module Aidp
200
244
  # Execute step with harness provider management
201
245
  def execute_with_harness_provider(provider_type, prompt, step_name, _options)
202
246
  # Get provider manager from harness
203
- provider_manager = @harness_runner.instance_variable_get(:@provider_manager)
247
+ provider_manager = @harness_runner.provider_manager
204
248
 
205
249
  # Execute with provider
206
250
  provider_manager.execute_with_provider(provider_type, prompt, {
@@ -262,7 +306,10 @@ module Aidp
262
306
 
263
307
  def template_search_paths
264
308
  [
265
- File.join(@project_dir, "templates", "EXECUTE"),
309
+ File.join(@project_dir, "templates"), # Root templates folder
310
+ File.join(@project_dir, "templates", "planning"),
311
+ File.join(@project_dir, "templates", "analysis"),
312
+ File.join(@project_dir, "templates", "implementation"),
266
313
  File.join(@project_dir, "templates", "COMMON")
267
314
  ]
268
315
  end
@@ -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" => ["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" => ["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" => ["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" => ["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" => ["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" => ["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" => ["api_design.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" => ["data_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" => ["security_review.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" => ["performance_review.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" => ["reliability_review.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" => ["testing_strategy.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" => ["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" => ["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" => ["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" => ["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" => ["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" => ["implementation.md"],
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