aidp 0.26.0 → 0.28.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -0
  3. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  4. data/lib/aidp/cli/config_command.rb +71 -0
  5. data/lib/aidp/cli/enhanced_input.rb +2 -0
  6. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  7. data/lib/aidp/cli/harness_command.rb +102 -0
  8. data/lib/aidp/cli/jobs_command.rb +3 -3
  9. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  10. data/lib/aidp/cli/models_command.rb +661 -0
  11. data/lib/aidp/cli/providers_command.rb +223 -0
  12. data/lib/aidp/cli.rb +45 -464
  13. data/lib/aidp/config.rb +54 -0
  14. data/lib/aidp/daemon/runner.rb +2 -2
  15. data/lib/aidp/debug_mixin.rb +25 -10
  16. data/lib/aidp/execute/agent_signal_parser.rb +22 -0
  17. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  18. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  19. data/lib/aidp/execute/interactive_repl.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +4 -4
  21. data/lib/aidp/execute/repl_macros.rb +2 -2
  22. data/lib/aidp/execute/steps.rb +94 -1
  23. data/lib/aidp/execute/work_loop_runner.rb +238 -19
  24. data/lib/aidp/execute/workflow_selector.rb +4 -27
  25. data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
  26. data/lib/aidp/harness/ai_decision_engine.rb +35 -2
  27. data/lib/aidp/harness/config_manager.rb +5 -10
  28. data/lib/aidp/harness/config_schema.rb +8 -0
  29. data/lib/aidp/harness/configuration.rb +40 -2
  30. data/lib/aidp/harness/enhanced_runner.rb +25 -19
  31. data/lib/aidp/harness/error_handler.rb +23 -73
  32. data/lib/aidp/harness/model_cache.rb +269 -0
  33. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  34. data/lib/aidp/harness/model_registry.rb +201 -0
  35. data/lib/aidp/harness/provider_factory.rb +11 -2
  36. data/lib/aidp/harness/runner.rb +5 -0
  37. data/lib/aidp/harness/state_manager.rb +0 -7
  38. data/lib/aidp/harness/thinking_depth_manager.rb +202 -7
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
  40. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
  41. data/lib/aidp/harness/ui/progress_display.rb +6 -2
  42. data/lib/aidp/harness/user_interface.rb +0 -58
  43. data/lib/aidp/init/runner.rb +7 -2
  44. data/lib/aidp/message_display.rb +0 -46
  45. data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
  46. data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
  47. data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
  48. data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
  49. data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
  50. data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
  51. data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
  52. data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
  53. data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
  54. data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
  55. data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
  56. data/lib/aidp/planning/parsers/document_parser.rb +141 -0
  57. data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
  58. data/lib/aidp/provider_manager.rb +8 -32
  59. data/lib/aidp/providers/adapter.rb +2 -4
  60. data/lib/aidp/providers/aider.rb +264 -0
  61. data/lib/aidp/providers/anthropic.rb +206 -121
  62. data/lib/aidp/providers/base.rb +123 -3
  63. data/lib/aidp/providers/capability_registry.rb +0 -1
  64. data/lib/aidp/providers/codex.rb +75 -70
  65. data/lib/aidp/providers/cursor.rb +87 -59
  66. data/lib/aidp/providers/gemini.rb +57 -60
  67. data/lib/aidp/providers/github_copilot.rb +19 -66
  68. data/lib/aidp/providers/kilocode.rb +35 -80
  69. data/lib/aidp/providers/opencode.rb +35 -80
  70. data/lib/aidp/setup/wizard.rb +555 -8
  71. data/lib/aidp/version.rb +1 -1
  72. data/lib/aidp/watch/build_processor.rb +211 -30
  73. data/lib/aidp/watch/change_request_processor.rb +128 -14
  74. data/lib/aidp/watch/ci_fix_processor.rb +103 -37
  75. data/lib/aidp/watch/ci_log_extractor.rb +258 -0
  76. data/lib/aidp/watch/github_state_extractor.rb +177 -0
  77. data/lib/aidp/watch/implementation_verifier.rb +284 -0
  78. data/lib/aidp/watch/plan_generator.rb +95 -52
  79. data/lib/aidp/watch/plan_processor.rb +7 -6
  80. data/lib/aidp/watch/repository_client.rb +245 -17
  81. data/lib/aidp/watch/review_processor.rb +100 -19
  82. data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
  83. data/lib/aidp/watch/runner.rb +181 -29
  84. data/lib/aidp/watch/state_store.rb +22 -1
  85. data/lib/aidp/workflows/definitions.rb +147 -0
  86. data/lib/aidp/workflows/guided_agent.rb +3 -3
  87. data/lib/aidp/workstream_cleanup.rb +245 -0
  88. data/lib/aidp/worktree.rb +19 -0
  89. data/templates/aidp-development.yml.example +2 -2
  90. data/templates/aidp-production.yml.example +3 -3
  91. data/templates/aidp.yml.example +57 -0
  92. data/templates/implementation/generate_tdd_specs.md +213 -0
  93. data/templates/implementation/iterative_implementation.md +122 -0
  94. data/templates/planning/agile/analyze_feedback.md +183 -0
  95. data/templates/planning/agile/generate_iteration_plan.md +179 -0
  96. data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
  97. data/templates/planning/agile/generate_marketing_report.md +162 -0
  98. data/templates/planning/agile/generate_mvp_scope.md +127 -0
  99. data/templates/planning/agile/generate_user_test_plan.md +143 -0
  100. data/templates/planning/agile/ingest_feedback.md +174 -0
  101. data/templates/planning/assemble_project_plan.md +113 -0
  102. data/templates/planning/assign_personas.md +108 -0
  103. data/templates/planning/create_tasks.md +52 -6
  104. data/templates/planning/generate_gantt.md +86 -0
  105. data/templates/planning/generate_wbs.md +85 -0
  106. data/templates/planning/initialize_planning_mode.md +70 -0
  107. data/templates/skills/README.md +2 -2
  108. data/templates/skills/marketing_strategist/SKILL.md +279 -0
  109. data/templates/skills/product_manager/SKILL.md +177 -0
  110. data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
  111. data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
  112. data/templates/skills/ux_researcher/SKILL.md +222 -0
  113. metadata +47 -1
data/lib/aidp/config.rb CHANGED
@@ -156,6 +156,27 @@ module Aidp
156
156
  search_paths: [],
157
157
  default_provider_filter: true,
158
158
  enable_custom_skills: true
159
+ },
160
+ waterfall: {
161
+ enabled: true,
162
+ docs_directory: ".aidp/docs",
163
+ generate_decisions_md: true,
164
+ gantt_format: "mermaid",
165
+ wbs_phases: [
166
+ "Requirements",
167
+ "Design",
168
+ "Implementation",
169
+ "Testing",
170
+ "Deployment"
171
+ ],
172
+ effort_estimation: {
173
+ method: "llm_relative",
174
+ units: "story_points"
175
+ },
176
+ persona_assignment: {
177
+ method: "zfc_automatic",
178
+ allow_parallel: true
179
+ }
159
180
  }
160
181
  }.freeze
161
182
 
@@ -246,6 +267,24 @@ module Aidp
246
267
  symbolize_keys(skills_section)
247
268
  end
248
269
 
270
+ # Get waterfall configuration
271
+ def self.waterfall_config(project_dir = Dir.pwd)
272
+ config = load_harness_config(project_dir)
273
+ waterfall_section = config[:waterfall] || config["waterfall"] || {}
274
+
275
+ # Convert string keys to symbols for consistency
276
+ symbolize_keys(waterfall_section)
277
+ end
278
+
279
+ # Get agile configuration
280
+ def self.agile_config(project_dir = Dir.pwd)
281
+ config = load_harness_config(project_dir)
282
+ agile_section = config[:agile] || config["agile"] || {}
283
+
284
+ # Convert string keys to symbols for consistency
285
+ symbolize_keys(agile_section)
286
+ end
287
+
249
288
  # Check if configuration file exists
250
289
  def self.config_exists?(project_dir = Dir.pwd)
251
290
  ConfigPaths.config_exists?(project_dir)
@@ -280,6 +319,15 @@ module Aidp
280
319
  max_tokens: 50_000,
281
320
  default_flags: []
282
321
  }
322
+ },
323
+ agile: {
324
+ mvp_first: true,
325
+ feedback_loops: true,
326
+ auto_iteration: false,
327
+ research_enabled: true,
328
+ marketing_enabled: true,
329
+ legacy_analysis: true,
330
+ personas: ["product_manager", "ux_researcher", "architect", "senior_developer", "qa_engineer", "devops_engineer", "tech_writer", "marketing_strategist"]
283
331
  }
284
332
  }
285
333
 
@@ -337,6 +385,12 @@ module Aidp
337
385
  merged[:thinking] = symbolize_keys(thinking_section)
338
386
  end
339
387
 
388
+ # Deep merge waterfall config
389
+ if config[:waterfall] || config["waterfall"]
390
+ waterfall_section = config[:waterfall] || config["waterfall"]
391
+ merged[:waterfall] = merged[:waterfall].merge(symbolize_keys(waterfall_section))
392
+ end
393
+
340
394
  merged
341
395
  end
342
396
 
@@ -10,11 +10,11 @@ module Aidp
10
10
  # Main daemon runner for background mode execution
11
11
  # Manages work loops, watch mode, and IPC communication
12
12
  class Runner
13
- def initialize(project_dir, config, options = {})
13
+ def initialize(project_dir, config, options = {}, process_manager: nil)
14
14
  @project_dir = project_dir
15
15
  @config = config
16
16
  @options = options
17
- @process_manager = ProcessManager.new(project_dir)
17
+ @process_manager = process_manager || ProcessManager.new(project_dir)
18
18
  @running = false
19
19
  @work_loop_runner = nil
20
20
  @watch_runner = nil
@@ -78,7 +78,9 @@ module Aidp
78
78
  end
79
79
 
80
80
  if error && !error.empty?
81
- debug_logger.error(component_name, "❌ Error output: #{error}")
81
+ # Filter out benign sandbox debug messages from Claude CLI
82
+ filtered_error = filter_benign_errors(error)
83
+ debug_logger.error(component_name, "❌ Error output: #{filtered_error}") unless filtered_error.empty?
82
84
  end
83
85
 
84
86
  if debug_verbose?
@@ -130,7 +132,7 @@ module Aidp
130
132
  end
131
133
 
132
134
  # Execute command with debug logging
133
- def debug_execute_command(cmd, args: [], input: nil, timeout: nil, streaming: false, **options)
135
+ def debug_execute_command(cmd, args: [], input: nil, timeout: nil, **options)
134
136
  require "tty-command"
135
137
 
136
138
  command_str = [cmd, *args].join(" ")
@@ -139,14 +141,7 @@ module Aidp
139
141
  debug_logger.info(component_name, "🚀 Starting command execution: #{command_str}")
140
142
 
141
143
  begin
142
- # Configure printer based on streaming mode
143
- if streaming
144
- # Use progress printer for real-time output
145
- cmd_obj = TTY::Command.new(printer: :progress)
146
- debug_log("📺 Streaming mode enabled - showing real-time output", level: :info)
147
- else
148
- cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
149
- end
144
+ cmd_obj = TTY::Command.new(printer: :null) # Disable TTY::Command's own output
150
145
 
151
146
  # Prepare input
152
147
  input_data = nil
@@ -178,6 +173,26 @@ module Aidp
178
173
 
179
174
  private
180
175
 
176
+ # Filter out benign error messages that don't indicate real failures
177
+ def filter_benign_errors(error)
178
+ return "" if error.nil?
179
+
180
+ # List of string patterns for benign messages to filter out
181
+ # Using simple string matching to avoid ReDoS vulnerabilities
182
+ benign_substrings = [
183
+ "[SandboxDebug]",
184
+ "Seccomp filtering not available",
185
+ "allowAllUnixSockets mode"
186
+ ]
187
+
188
+ lines = error.lines
189
+ filtered_lines = lines.reject do |line|
190
+ benign_substrings.any? { |substring| line.include?(substring) }
191
+ end
192
+
193
+ filtered_lines.join
194
+ end
195
+
181
196
  # Safely derive a component name for logging (memoized).
182
197
  # Handles anonymous classes and modules gracefully.
183
198
  def component_name
@@ -36,6 +36,28 @@ module Aidp
36
36
  tasks
37
37
  end
38
38
 
39
+ # Parse task status update signals from agent output
40
+ # Returns array of status update hashes with task_id, status, and optional reason
41
+ # Pattern: Update task: task_id_here status: done|in_progress|pending|abandoned [reason: "reason"]
42
+ def self.parse_task_status_updates(output)
43
+ return [] unless output
44
+
45
+ updates = []
46
+ # Pattern matches: Update task: task_123_abc status: done
47
+ # Or: Update task: task_123_abc status: abandoned reason: "No longer needed"
48
+ pattern = /Update\s+task:\s*(\S+)\s+status:\s*(done|in_progress|pending|abandoned)(?:\s+reason:\s*"([^"]+)")?/i
49
+
50
+ output.to_s.scan(pattern).each do |task_id, status, reason|
51
+ updates << {
52
+ task_id: task_id.strip,
53
+ status: status.downcase.to_sym,
54
+ reason: reason&.strip
55
+ }
56
+ end
57
+
58
+ updates
59
+ end
60
+
39
61
  def self.normalize_token(raw)
40
62
  return nil if raw.nil? || raw.empty?
41
63
 
@@ -27,6 +27,7 @@ module Aidp
27
27
  @config = config
28
28
  @options = options
29
29
  @cancel_timeout = options[:cancel_timeout] || 5 # seconds to wait for graceful shutdown
30
+ @sync_runner_class = options[:sync_runner_class] || WorkLoopRunner
30
31
  @state = WorkLoopState.new
31
32
  @instruction_queue = InstructionQueue.new
32
33
  @work_thread = nil
@@ -133,7 +134,7 @@ module Aidp
133
134
  # Main async execution loop
134
135
  def run_async_loop
135
136
  # Create synchronous runner (runs in this thread)
136
- @sync_runner = WorkLoopRunner.new(
137
+ @sync_runner = @sync_runner_class.new(
137
138
  @project_dir,
138
139
  @provider_manager,
139
140
  @config,
@@ -8,17 +8,18 @@ module Aidp
8
8
  class CheckpointDisplay
9
9
  include Aidp::MessageDisplay
10
10
 
11
- def initialize
11
+ def initialize(prompt: nil)
12
12
  @pastel = Pastel.new
13
+ @prompt = prompt || TTY::Prompt.new
13
14
  end
14
15
 
15
16
  # Display a checkpoint during work loop iteration
16
17
  def display_checkpoint(checkpoint_data, show_details: false)
17
18
  return unless checkpoint_data
18
19
 
19
- puts
20
- puts @pastel.bold("📊 Checkpoint - Iteration #{checkpoint_data[:iteration]}")
21
- puts @pastel.dim("─" * 60)
20
+ @prompt.say("")
21
+ @prompt.say(@pastel.bold("📊 Checkpoint - Iteration #{checkpoint_data[:iteration]}"))
22
+ @prompt.say(@pastel.dim("─" * 60))
22
23
 
23
24
  display_metrics(checkpoint_data[:metrics])
24
25
  display_status(checkpoint_data[:status])
@@ -27,52 +28,52 @@ module Aidp
27
28
  display_trends(checkpoint_data[:trends]) if checkpoint_data[:trends]
28
29
  end
29
30
 
30
- puts @pastel.dim("─" * 60)
31
- puts
31
+ @prompt.say(@pastel.dim("─" * 60))
32
+ @prompt.say("")
32
33
  end
33
34
 
34
35
  # Display progress summary with trends
35
36
  def display_progress_summary(summary)
36
37
  return unless summary
37
38
 
38
- puts
39
- puts @pastel.bold("📈 Progress Summary")
40
- puts @pastel.dim("=" * 60)
39
+ @prompt.say("")
40
+ @prompt.say(@pastel.bold("📈 Progress Summary"))
41
+ @prompt.say(@pastel.dim("=" * 60))
41
42
 
42
43
  current = summary[:current]
43
- puts "Step: #{@pastel.cyan(current[:step_name])}"
44
- puts "Iteration: #{current[:iteration]}"
45
- puts "Status: #{format_status(current[:status])}"
46
- puts
44
+ @prompt.say("Step: #{@pastel.cyan(current[:step_name])}")
45
+ @prompt.say("Iteration: #{current[:iteration]}")
46
+ @prompt.say("Status: #{format_status(current[:status])}")
47
+ @prompt.say("")
47
48
 
48
- puts @pastel.bold("Current Metrics:")
49
+ @prompt.say(@pastel.bold("Current Metrics:"))
49
50
  display_metrics(current[:metrics])
50
51
 
51
52
  if summary[:trends]
52
- puts
53
- puts @pastel.bold("Trends:")
53
+ @prompt.say("")
54
+ @prompt.say(@pastel.bold("Trends:"))
54
55
  display_trends(summary[:trends])
55
56
  end
56
57
 
57
58
  if summary[:quality_score]
58
- puts
59
+ @prompt.say("")
59
60
  display_quality_score(summary[:quality_score])
60
61
  end
61
62
 
62
- puts @pastel.dim("=" * 60)
63
- puts
63
+ @prompt.say(@pastel.dim("=" * 60))
64
+ @prompt.say("")
64
65
  end
65
66
 
66
67
  # Display checkpoint history as a table
67
68
  def display_checkpoint_history(history, limit: 10)
68
69
  return if history.empty?
69
70
 
70
- puts
71
- puts @pastel.bold("📜 Checkpoint History (Last #{[limit, history.size].min})")
72
- puts @pastel.dim("=" * 80)
71
+ @prompt.say("")
72
+ @prompt.say(@pastel.bold("📜 Checkpoint History (Last #{[limit, history.size].min})"))
73
+ @prompt.say(@pastel.dim("=" * 80))
73
74
 
74
75
  # Table header
75
- puts format_table_row([
76
+ @prompt.say(format_table_row([
76
77
  "Iteration",
77
78
  "Time",
78
79
  "LOC",
@@ -80,15 +81,15 @@ module Aidp
80
81
  "Quality",
81
82
  "PRD Progress",
82
83
  "Status"
83
- ], header: true)
84
- puts @pastel.dim("-" * 80)
84
+ ], header: true))
85
+ @prompt.say(@pastel.dim("-" * 80))
85
86
 
86
87
  # Table rows
87
88
  history.last(limit).each do |checkpoint|
88
89
  metrics = checkpoint[:metrics]
89
90
  timestamp = Time.parse(checkpoint[:timestamp]).strftime("%H:%M:%S")
90
91
 
91
- puts format_table_row([
92
+ @prompt.say(format_table_row([
92
93
  checkpoint[:iteration].to_s,
93
94
  timestamp,
94
95
  metrics[:lines_of_code].to_s,
@@ -96,11 +97,11 @@ module Aidp
96
97
  "#{metrics[:code_quality]}%",
97
98
  "#{metrics[:prd_task_progress]}%",
98
99
  format_status(checkpoint[:status])
99
- ])
100
+ ]))
100
101
  end
101
102
 
102
- puts @pastel.dim("=" * 80)
103
- puts
103
+ @prompt.say(@pastel.dim("=" * 80))
104
+ @prompt.say("")
104
105
  end
105
106
 
106
107
  # Display inline progress indicator (for work loop)
@@ -124,15 +125,15 @@ module Aidp
124
125
  private
125
126
 
126
127
  def display_metrics(metrics)
127
- puts " Lines of Code: #{@pastel.yellow(metrics[:lines_of_code].to_s)}"
128
- puts " Test Coverage: #{format_percentage_with_color(metrics[:test_coverage])}"
129
- puts " Code Quality: #{format_percentage_with_color(metrics[:code_quality])}"
130
- puts " PRD Task Progress: #{format_percentage_with_color(metrics[:prd_task_progress])}"
131
- puts " File Count: #{metrics[:file_count]}"
128
+ @prompt.say(" Lines of Code: #{@pastel.yellow(metrics[:lines_of_code].to_s)}")
129
+ @prompt.say(" Test Coverage: #{format_percentage_with_color(metrics[:test_coverage])}")
130
+ @prompt.say(" Code Quality: #{format_percentage_with_color(metrics[:code_quality])}")
131
+ @prompt.say(" PRD Task Progress: #{format_percentage_with_color(metrics[:prd_task_progress])}")
132
+ @prompt.say(" File Count: #{metrics[:file_count]}")
132
133
  end
133
134
 
134
135
  def display_status(status)
135
- puts " Overall Status: #{format_status(status)}"
136
+ @prompt.say(" Overall Status: #{format_status(status)}")
136
137
  end
137
138
 
138
139
  def format_status(status)
@@ -156,7 +157,7 @@ module Aidp
156
157
  arrow = trend_arrow(trend_data[:direction])
157
158
  change = format_change(trend_data[:change], trend_data[:change_percent])
158
159
 
159
- puts " #{metric_name}: #{arrow} #{change}"
160
+ @prompt.say(" #{metric_name}: #{arrow} #{change}")
160
161
  end
161
162
  end
162
163
 
@@ -185,7 +186,7 @@ module Aidp
185
186
  :red
186
187
  end
187
188
 
188
- puts " Quality Score: #{@pastel.send(color, "#{score.round(2)}%")}"
189
+ @prompt.say(" Quality Score: #{@pastel.send(color, "#{score.round(2)}%")}")
189
190
  end
190
191
 
191
192
  def format_percentage(value)
@@ -29,6 +29,7 @@ module Aidp
29
29
  @config = config
30
30
  @options = options
31
31
  @prompt = options[:prompt] || TTY::Prompt.new
32
+ @async_runner_class = options[:async_runner_class] || AsyncWorkLoopRunner
32
33
  @async_runner = nil
33
34
  @repl_macros = ReplMacros.new
34
35
  @output_display_thread = nil
@@ -38,7 +39,7 @@ module Aidp
38
39
 
39
40
  # Start work loop and enter interactive REPL
40
41
  def start_work_loop(step_name, step_spec, context = {})
41
- @async_runner = AsyncWorkLoopRunner.new(
42
+ @async_runner = @async_runner_class.new(
42
43
  @project_dir,
43
44
  @provider_manager,
44
45
  @config,
@@ -17,20 +17,20 @@ module Aidp
17
17
 
18
18
  attr_reader :optimizer, :last_optimization_stats
19
19
 
20
- def initialize(project_dir, config: nil)
20
+ def initialize(project_dir, config: nil, optimizer: nil)
21
21
  @project_dir = project_dir
22
22
  @aidp_dir = File.join(project_dir, ".aidp")
23
23
  @prompt_path = File.join(@aidp_dir, PROMPT_FILENAME)
24
24
  @archive_dir = File.join(project_dir, ARCHIVE_DIR)
25
25
  @config = config
26
- @optimizer = nil
26
+ @optimizer = optimizer
27
27
  @last_optimization_stats = nil
28
28
 
29
29
  # Ensure .aidp directory exists
30
30
  FileUtils.mkdir_p(@aidp_dir)
31
31
 
32
- # Initialize optimizer if enabled
33
- if config&.respond_to?(:prompt_optimization_enabled?) && config.prompt_optimization_enabled?
32
+ # Initialize optimizer if enabled and not provided
33
+ if @optimizer.nil? && config&.respond_to?(:prompt_optimization_enabled?) && config.prompt_optimization_enabled?
34
34
  @optimizer = Aidp::PromptOptimization::Optimizer.new(
35
35
  project_dir: project_dir,
36
36
  config: config.prompt_optimization_config
@@ -1777,8 +1777,8 @@ module Aidp
1777
1777
  lines << "Legend: → current, ↑ max allowed"
1778
1778
  lines << ""
1779
1779
 
1780
- # Show current model selection
1781
- current_model = manager.select_model_for_tier
1780
+ # Show current model selection (using harness default provider)
1781
+ current_model = manager.select_model_for_tier(provider: config.default_provider)
1782
1782
  if current_model
1783
1783
  provider, model_name, model_data = current_model
1784
1784
  lines << "Current Model: #{provider}/#{model_name}"
@@ -121,12 +121,20 @@ module Aidp
121
121
  },
122
122
  # New implementation step for actual development work
123
123
  "16_IMPLEMENTATION" => {
124
- "templates" => ["implementation/implement_features.md"], # Reuse existing implementation template
124
+ "templates" => ["implementation/iterative_implementation.md"], # Multi-step feature implementation with task decomposition
125
125
  "description" => "Execute Implementation Tasks",
126
126
  "outs" => ["implementation_log.md"],
127
127
  "gate" => false,
128
128
  "implementation" => true # Special step that runs development tasks
129
129
  },
130
+ # Test-Driven Development (TDD) - Optional step for any workflow
131
+ "17_TDD_SPECIFICATION" => {
132
+ "templates" => ["implementation/generate_tdd_specs.md"],
133
+ "description" => "Generate TDD test specifications (write tests first)",
134
+ "outs" => ["docs/tdd_specifications.md", "spec/**/*_spec.rb"],
135
+ "gate" => false,
136
+ "interactive" => false
137
+ },
130
138
  # Simple task execution - for one-off commands and simple fixes
131
139
  "99_SIMPLE_TASK" => {
132
140
  "templates" => ["implementation/simple_task.md"],
@@ -134,6 +142,91 @@ module Aidp
134
142
  "outs" => [],
135
143
  "gate" => false,
136
144
  "simple" => true # Special step for simple, focused tasks
145
+ },
146
+ # Generic planning and project management steps (usable in any workflow)
147
+ "18_WBS" => {
148
+ "templates" => ["planning/generate_wbs.md"],
149
+ "description" => "Generate Work Breakdown Structure with phases and tasks",
150
+ "outs" => [".aidp/docs/WBS.md"],
151
+ "gate" => false
152
+ },
153
+ "19_GANTT_CHART" => {
154
+ "templates" => ["planning/generate_gantt.md"],
155
+ "description" => "Generate Gantt chart with timeline and critical path",
156
+ "outs" => [".aidp/docs/GANTT.md"],
157
+ "gate" => false
158
+ },
159
+ "20_PERSONA_ASSIGNMENT" => {
160
+ "templates" => ["planning/assign_personas.md"],
161
+ "description" => "Assign tasks to personas/roles using AI (ZFC)",
162
+ "outs" => [".aidp/docs/persona_map.yml"],
163
+ "gate" => false
164
+ },
165
+ "21_PROJECT_PLAN_ASSEMBLY" => {
166
+ "templates" => ["planning/assemble_project_plan.md"],
167
+ "description" => "Assemble complete project plan from all artifacts",
168
+ "outs" => [".aidp/docs/PROJECT_PLAN.md"],
169
+ "gate" => false
170
+ },
171
+ # Planning mode initialization (supports ingestion vs generation workflows)
172
+ "22_PLANNING_MODE_INIT" => {
173
+ "templates" => ["planning/initialize_planning_mode.md"],
174
+ "description" => "Initialize planning mode (ingestion of existing docs vs generation from scratch)",
175
+ "outs" => [".aidp/docs/.planning_mode"],
176
+ "gate" => true,
177
+ "interactive" => true
178
+ },
179
+ # Agile planning steps
180
+ "23_MVP_SCOPE" => {
181
+ "skill" => "product_manager",
182
+ "templates" => ["planning/agile/generate_mvp_scope.md"],
183
+ "description" => "Define MVP scope with must-have and nice-to-have features",
184
+ "outs" => [".aidp/docs/MVP_SCOPE.md"],
185
+ "gate" => false,
186
+ "interactive" => true
187
+ },
188
+ "24_USER_TEST_PLAN" => {
189
+ "skill" => "ux_researcher",
190
+ "templates" => ["planning/agile/generate_user_test_plan.md"],
191
+ "description" => "Generate user testing plan with recruitment and survey templates",
192
+ "outs" => [".aidp/docs/USER_TEST_PLAN.md"],
193
+ "gate" => false
194
+ },
195
+ "25_MARKETING_REPORT" => {
196
+ "skill" => "marketing_strategist",
197
+ "templates" => ["planning/agile/generate_marketing_report.md"],
198
+ "description" => "Generate marketing report with key messages and differentiators",
199
+ "outs" => [".aidp/docs/MARKETING_REPORT.md"],
200
+ "gate" => false
201
+ },
202
+ "26_INGEST_FEEDBACK" => {
203
+ "skill" => "ux_researcher",
204
+ "templates" => ["planning/agile/ingest_feedback.md"],
205
+ "description" => "Ingest user feedback data (CSV, JSON, markdown)",
206
+ "outs" => [".aidp/docs/feedback_data.json"],
207
+ "gate" => false,
208
+ "interactive" => true
209
+ },
210
+ "27_ANALYZE_FEEDBACK" => {
211
+ "skill" => "ux_researcher",
212
+ "templates" => ["planning/agile/analyze_feedback.md"],
213
+ "description" => "Analyze user feedback with AI-powered insights",
214
+ "outs" => [".aidp/docs/USER_FEEDBACK_ANALYSIS.md"],
215
+ "gate" => false
216
+ },
217
+ "28_ITERATION_PLAN" => {
218
+ "skill" => "product_manager",
219
+ "templates" => ["planning/agile/generate_iteration_plan.md"],
220
+ "description" => "Generate next iteration plan based on feedback",
221
+ "outs" => [".aidp/docs/NEXT_ITERATION_PLAN.md"],
222
+ "gate" => false
223
+ },
224
+ "29_LEGACY_RESEARCH_PLAN" => {
225
+ "skill" => "ux_researcher",
226
+ "templates" => ["planning/agile/generate_legacy_research_plan.md"],
227
+ "description" => "Generate user research plan for existing codebase",
228
+ "outs" => [".aidp/docs/LEGACY_USER_RESEARCH_PLAN.md"],
229
+ "gate" => false
137
230
  }
138
231
  }.freeze
139
232
  end