aidp 0.27.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.
- checksums.yaml +4 -4
- data/README.md +89 -0
- data/lib/aidp/cli/models_command.rb +5 -6
- data/lib/aidp/cli.rb +10 -8
- data/lib/aidp/config.rb +54 -0
- data/lib/aidp/debug_mixin.rb +23 -1
- data/lib/aidp/execute/agent_signal_parser.rb +22 -0
- data/lib/aidp/execute/repl_macros.rb +2 -2
- data/lib/aidp/execute/steps.rb +94 -1
- data/lib/aidp/execute/work_loop_runner.rb +209 -17
- data/lib/aidp/execute/workflow_selector.rb +2 -25
- data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
- data/lib/aidp/harness/ai_decision_engine.rb +35 -2
- data/lib/aidp/harness/config_manager.rb +0 -5
- data/lib/aidp/harness/config_schema.rb +8 -0
- data/lib/aidp/harness/configuration.rb +27 -19
- data/lib/aidp/harness/enhanced_runner.rb +1 -4
- data/lib/aidp/harness/error_handler.rb +1 -72
- data/lib/aidp/harness/provider_factory.rb +11 -2
- data/lib/aidp/harness/state_manager.rb +0 -7
- data/lib/aidp/harness/thinking_depth_manager.rb +47 -68
- data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
- data/lib/aidp/harness/ui/progress_display.rb +6 -2
- data/lib/aidp/harness/user_interface.rb +0 -58
- data/lib/aidp/init/runner.rb +7 -2
- data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
- data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
- data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
- data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
- data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
- data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
- data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
- data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
- data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
- data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
- data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
- data/lib/aidp/planning/parsers/document_parser.rb +141 -0
- data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
- data/lib/aidp/provider_manager.rb +8 -32
- data/lib/aidp/providers/aider.rb +264 -0
- data/lib/aidp/providers/anthropic.rb +74 -2
- data/lib/aidp/providers/base.rb +25 -1
- data/lib/aidp/providers/codex.rb +26 -3
- data/lib/aidp/providers/cursor.rb +16 -0
- data/lib/aidp/providers/gemini.rb +13 -0
- data/lib/aidp/providers/github_copilot.rb +17 -0
- data/lib/aidp/providers/kilocode.rb +11 -0
- data/lib/aidp/providers/opencode.rb +11 -0
- data/lib/aidp/setup/wizard.rb +249 -39
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +211 -30
- data/lib/aidp/watch/change_request_processor.rb +128 -14
- data/lib/aidp/watch/ci_fix_processor.rb +103 -37
- data/lib/aidp/watch/ci_log_extractor.rb +258 -0
- data/lib/aidp/watch/github_state_extractor.rb +177 -0
- data/lib/aidp/watch/implementation_verifier.rb +284 -0
- data/lib/aidp/watch/plan_generator.rb +7 -43
- data/lib/aidp/watch/plan_processor.rb +7 -6
- data/lib/aidp/watch/repository_client.rb +245 -17
- data/lib/aidp/watch/review_processor.rb +98 -17
- data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
- data/lib/aidp/watch/runner.rb +181 -29
- data/lib/aidp/watch/state_store.rb +22 -1
- data/lib/aidp/workflows/definitions.rb +147 -0
- data/lib/aidp/workstream_cleanup.rb +245 -0
- data/lib/aidp/worktree.rb +19 -0
- data/templates/aidp.yml.example +57 -0
- data/templates/implementation/generate_tdd_specs.md +213 -0
- data/templates/implementation/iterative_implementation.md +122 -0
- data/templates/planning/agile/analyze_feedback.md +183 -0
- data/templates/planning/agile/generate_iteration_plan.md +179 -0
- data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
- data/templates/planning/agile/generate_marketing_report.md +162 -0
- data/templates/planning/agile/generate_mvp_scope.md +127 -0
- data/templates/planning/agile/generate_user_test_plan.md +143 -0
- data/templates/planning/agile/ingest_feedback.md +174 -0
- data/templates/planning/assemble_project_plan.md +113 -0
- data/templates/planning/assign_personas.md +108 -0
- data/templates/planning/create_tasks.md +52 -6
- data/templates/planning/generate_gantt.md +86 -0
- data/templates/planning/generate_wbs.md +85 -0
- data/templates/planning/initialize_planning_mode.md +70 -0
- data/templates/skills/README.md +2 -2
- data/templates/skills/marketing_strategist/SKILL.md +279 -0
- data/templates/skills/product_manager/SKILL.md +177 -0
- data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
- data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
- data/templates/skills/ux_researcher/SKILL.md +222 -0
- metadata +39 -1
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../logger"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
module Planning
|
|
7
|
+
module Generators
|
|
8
|
+
# Generates Mermaid Gantt charts with critical path analysis
|
|
9
|
+
# Visualizes project timeline, dependencies, and milestones
|
|
10
|
+
class GanttGenerator
|
|
11
|
+
def initialize(config: nil)
|
|
12
|
+
@config = config || Aidp::Config.waterfall_config
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Generate Gantt chart from WBS and task list
|
|
16
|
+
# @param wbs [Hash] Work breakdown structure
|
|
17
|
+
# @param task_list [Array<Hash>] Optional detailed task list
|
|
18
|
+
# @return [Hash] Gantt chart data and Mermaid syntax
|
|
19
|
+
def generate(wbs:, task_list: nil)
|
|
20
|
+
Aidp.log_debug("gantt_generator", "generate", phase_count: wbs[:phases].size)
|
|
21
|
+
|
|
22
|
+
tasks = extract_tasks_from_wbs(wbs)
|
|
23
|
+
calculate_durations(tasks)
|
|
24
|
+
critical_path = calculate_critical_path(tasks)
|
|
25
|
+
|
|
26
|
+
Aidp.log_debug("gantt_generator", "calculated", task_count: tasks.size, critical_path_length: critical_path.size)
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
tasks: tasks,
|
|
30
|
+
critical_path: critical_path,
|
|
31
|
+
mermaid: format_mermaid(tasks, critical_path),
|
|
32
|
+
metadata: {
|
|
33
|
+
generated_at: Time.now.iso8601,
|
|
34
|
+
total_tasks: tasks.size,
|
|
35
|
+
critical_path_length: critical_path.size
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Format as Mermaid gantt syntax
|
|
41
|
+
# @param gantt_data [Hash] Gantt chart data
|
|
42
|
+
# @return [String] Mermaid formatted chart
|
|
43
|
+
def format_mermaid(tasks, critical_path)
|
|
44
|
+
Aidp.log_debug("gantt_generator", "format_mermaid")
|
|
45
|
+
|
|
46
|
+
output = ["gantt"]
|
|
47
|
+
output << " title Project Timeline"
|
|
48
|
+
output << " dateFormat YYYY-MM-DD"
|
|
49
|
+
output << " section Planning"
|
|
50
|
+
|
|
51
|
+
current_section = nil
|
|
52
|
+
|
|
53
|
+
tasks.each do |task|
|
|
54
|
+
if task[:phase] != current_section
|
|
55
|
+
current_section = task[:phase]
|
|
56
|
+
output << " section #{current_section}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
status = critical_path.include?(task[:id]) ? "crit" : ""
|
|
60
|
+
duration = task[:duration] || 1
|
|
61
|
+
|
|
62
|
+
# Mermaid gantt task format: TaskName :status, id, start, duration
|
|
63
|
+
if task[:dependencies].empty?
|
|
64
|
+
output << " #{task[:name]} :#{status}, #{task[:id]}, #{duration}d"
|
|
65
|
+
else
|
|
66
|
+
after_task = task[:dependencies].first.tr(" ", "_")
|
|
67
|
+
output << " #{task[:name]} :#{status}, #{task[:id]}, after #{after_task}, #{duration}d"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
output.join("\n")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Extract flat task list from WBS hierarchy
|
|
77
|
+
def extract_tasks_from_wbs(wbs)
|
|
78
|
+
tasks = []
|
|
79
|
+
task_counter = 0
|
|
80
|
+
|
|
81
|
+
wbs[:phases].each do |phase|
|
|
82
|
+
phase[:tasks].each do |task|
|
|
83
|
+
task_counter += 1
|
|
84
|
+
task_id = "task#{task_counter}"
|
|
85
|
+
|
|
86
|
+
tasks << {
|
|
87
|
+
id: task_id,
|
|
88
|
+
name: task[:name],
|
|
89
|
+
phase: phase[:name],
|
|
90
|
+
effort: task[:effort],
|
|
91
|
+
dependencies: task[:dependencies] || [],
|
|
92
|
+
duration: nil # Will be calculated
|
|
93
|
+
}
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
tasks
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Calculate task durations from effort estimates
|
|
101
|
+
# Converts story points to days (simplified: 1 story point = 0.5 days)
|
|
102
|
+
def calculate_durations(tasks)
|
|
103
|
+
Aidp.log_debug("gantt_generator", "calculate_durations", count: tasks.size)
|
|
104
|
+
|
|
105
|
+
tasks.each do |task|
|
|
106
|
+
if task[:effort]
|
|
107
|
+
# Extract numeric value from effort string (e.g., "3 story points" => 3)
|
|
108
|
+
effort_value = task[:effort].to_s.match(/\d+/)&.[](0).to_i
|
|
109
|
+
task[:duration] = [1, (effort_value * 0.5).ceil].max # Minimum 1 day
|
|
110
|
+
else
|
|
111
|
+
task[:duration] = 1 # Default to 1 day
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Calculate critical path through task dependencies
|
|
117
|
+
# Returns array of task IDs that form the longest path
|
|
118
|
+
def calculate_critical_path(tasks)
|
|
119
|
+
Aidp.log_debug("gantt_generator", "calculate_critical_path")
|
|
120
|
+
|
|
121
|
+
# Build dependency graph
|
|
122
|
+
graph = build_dependency_graph(tasks)
|
|
123
|
+
|
|
124
|
+
# Find longest path (critical path)
|
|
125
|
+
longest_path = []
|
|
126
|
+
max_duration = 0
|
|
127
|
+
|
|
128
|
+
# Start from tasks with no dependencies
|
|
129
|
+
start_tasks = tasks.select { |t| t[:dependencies].empty? }
|
|
130
|
+
|
|
131
|
+
start_tasks.each do |start_task|
|
|
132
|
+
path = find_longest_path(start_task, graph, tasks, [])
|
|
133
|
+
path_duration = calculate_path_duration(path, tasks)
|
|
134
|
+
|
|
135
|
+
if path_duration > max_duration
|
|
136
|
+
max_duration = path_duration
|
|
137
|
+
longest_path = path
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
Aidp.log_debug("gantt_generator", "critical_path_found", length: longest_path.size, duration: max_duration)
|
|
142
|
+
longest_path
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def build_dependency_graph(tasks)
|
|
146
|
+
graph = Hash.new { |h, k| h[k] = [] }
|
|
147
|
+
|
|
148
|
+
tasks.each do |task|
|
|
149
|
+
task[:dependencies].each do |dep_name|
|
|
150
|
+
dep_task = tasks.find { |t| t[:name] == dep_name }
|
|
151
|
+
graph[dep_task[:id]] << task[:id] if dep_task
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
graph
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def find_longest_path(current_task, graph, all_tasks, visited)
|
|
159
|
+
return [current_task[:id]] if visited.include?(current_task[:id])
|
|
160
|
+
|
|
161
|
+
visited += [current_task[:id]]
|
|
162
|
+
dependent_tasks = graph[current_task[:id]] || []
|
|
163
|
+
|
|
164
|
+
if dependent_tasks.empty?
|
|
165
|
+
[current_task[:id]]
|
|
166
|
+
else
|
|
167
|
+
longest = []
|
|
168
|
+
|
|
169
|
+
dependent_tasks.each do |dep_id|
|
|
170
|
+
dep_task = all_tasks.find { |t| t[:id] == dep_id }
|
|
171
|
+
next unless dep_task
|
|
172
|
+
|
|
173
|
+
path = find_longest_path(dep_task, graph, all_tasks, visited)
|
|
174
|
+
longest = path if path.size > longest.size
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
[current_task[:id]] + longest
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def calculate_path_duration(path, tasks)
|
|
182
|
+
path.sum do |task_id|
|
|
183
|
+
task = tasks.find { |t| t[:id] == task_id }
|
|
184
|
+
task ? (task[:duration] || 1) : 0
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../../logger"
|
|
4
|
+
|
|
5
|
+
module Aidp
|
|
6
|
+
module Planning
|
|
7
|
+
module Generators
|
|
8
|
+
# Generates next iteration plan based on user feedback
|
|
9
|
+
# Creates actionable tasks to address feedback and improve product
|
|
10
|
+
# Uses Zero Framework Cognition (ZFC) for priority and task generation
|
|
11
|
+
class IterationPlanGenerator
|
|
12
|
+
def initialize(ai_decision_engine:, wbs_generator: nil, config: nil)
|
|
13
|
+
@ai_decision_engine = ai_decision_engine
|
|
14
|
+
@wbs_generator = wbs_generator
|
|
15
|
+
@config = config || Aidp::Config.agile_config
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Generate iteration plan from feedback analysis
|
|
19
|
+
# @param feedback_analysis [Hash] Analyzed feedback data
|
|
20
|
+
# @param current_mvp [Hash] Current MVP scope (optional)
|
|
21
|
+
# @return [Hash] Iteration plan structure
|
|
22
|
+
def generate(feedback_analysis:, current_mvp: nil)
|
|
23
|
+
Aidp.log_debug("iteration_plan_generator", "generate",
|
|
24
|
+
recommendations: feedback_analysis[:recommendations]&.size || 0)
|
|
25
|
+
|
|
26
|
+
# Use AI to generate iteration plan
|
|
27
|
+
iteration_plan = generate_plan_with_ai(feedback_analysis, current_mvp)
|
|
28
|
+
|
|
29
|
+
{
|
|
30
|
+
overview: iteration_plan[:overview],
|
|
31
|
+
goals: iteration_plan[:goals],
|
|
32
|
+
improvements: iteration_plan[:improvements],
|
|
33
|
+
new_features: iteration_plan[:new_features],
|
|
34
|
+
bug_fixes: iteration_plan[:bug_fixes],
|
|
35
|
+
technical_debt: iteration_plan[:technical_debt],
|
|
36
|
+
tasks: iteration_plan[:tasks],
|
|
37
|
+
success_metrics: iteration_plan[:success_metrics],
|
|
38
|
+
risks: iteration_plan[:risks],
|
|
39
|
+
timeline: iteration_plan[:timeline],
|
|
40
|
+
metadata: {
|
|
41
|
+
generated_at: Time.now.iso8601,
|
|
42
|
+
improvement_count: iteration_plan[:improvements]&.size || 0,
|
|
43
|
+
new_feature_count: iteration_plan[:new_features]&.size || 0,
|
|
44
|
+
task_count: iteration_plan[:tasks]&.size || 0,
|
|
45
|
+
based_on_feedback: !feedback_analysis.nil?
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Format iteration plan as markdown
|
|
51
|
+
# @param plan [Hash] Iteration plan structure
|
|
52
|
+
# @return [String] Markdown formatted iteration plan
|
|
53
|
+
def format_as_markdown(plan)
|
|
54
|
+
Aidp.log_debug("iteration_plan_generator", "format_as_markdown")
|
|
55
|
+
|
|
56
|
+
output = ["# Next Iteration Plan", ""]
|
|
57
|
+
output << "**Generated:** #{plan[:metadata][:generated_at]}"
|
|
58
|
+
output << "**Improvements:** #{plan[:metadata][:improvement_count]}"
|
|
59
|
+
output << "**New Features:** #{plan[:metadata][:new_feature_count]}"
|
|
60
|
+
output << "**Total Tasks:** #{plan[:metadata][:task_count]}"
|
|
61
|
+
output << ""
|
|
62
|
+
|
|
63
|
+
output << "## Overview"
|
|
64
|
+
output << ""
|
|
65
|
+
output << plan[:overview]
|
|
66
|
+
output << ""
|
|
67
|
+
|
|
68
|
+
output << "## Iteration Goals"
|
|
69
|
+
output << ""
|
|
70
|
+
plan[:goals].each_with_index do |goal, idx|
|
|
71
|
+
output << "#{idx + 1}. #{goal}"
|
|
72
|
+
end
|
|
73
|
+
output << ""
|
|
74
|
+
|
|
75
|
+
output << "## Feature Improvements"
|
|
76
|
+
output << ""
|
|
77
|
+
output << "Based on user feedback, these existing features will be improved:"
|
|
78
|
+
output << ""
|
|
79
|
+
plan[:improvements].each_with_index do |improvement, idx|
|
|
80
|
+
output << "### #{idx + 1}. #{improvement[:feature_name]}"
|
|
81
|
+
output << ""
|
|
82
|
+
output << "**Current Issue:** #{improvement[:issue]}"
|
|
83
|
+
output << ""
|
|
84
|
+
output << "**Proposed Improvement:** #{improvement[:improvement]}"
|
|
85
|
+
output << ""
|
|
86
|
+
output << "**User Impact:** #{improvement[:impact]}"
|
|
87
|
+
output << ""
|
|
88
|
+
output << "**Effort:** #{improvement[:effort]}"
|
|
89
|
+
output << ""
|
|
90
|
+
output << "**Priority:** #{improvement[:priority]}"
|
|
91
|
+
output << ""
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
output << "## New Features"
|
|
95
|
+
output << ""
|
|
96
|
+
output << "New features to be added based on user requests:"
|
|
97
|
+
output << ""
|
|
98
|
+
plan[:new_features].each_with_index do |feature, idx|
|
|
99
|
+
output << "### #{idx + 1}. #{feature[:name]}"
|
|
100
|
+
output << ""
|
|
101
|
+
output << feature[:description]
|
|
102
|
+
output << ""
|
|
103
|
+
output << "**Rationale:** #{feature[:rationale]}"
|
|
104
|
+
output << ""
|
|
105
|
+
output << "**Acceptance Criteria:**"
|
|
106
|
+
(feature[:acceptance_criteria] || []).each do |criterion|
|
|
107
|
+
output << "- #{criterion}"
|
|
108
|
+
end
|
|
109
|
+
output << ""
|
|
110
|
+
output << "**Effort:** #{feature[:effort]}"
|
|
111
|
+
output << ""
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
output << "## Bug Fixes"
|
|
115
|
+
output << ""
|
|
116
|
+
plan[:bug_fixes].each_with_index do |bug, idx|
|
|
117
|
+
output << "#{idx + 1}. **#{bug[:title]}** (Priority: #{bug[:priority]})"
|
|
118
|
+
output << " - Description: #{bug[:description]}"
|
|
119
|
+
output << " - Affected Users: #{bug[:affected_users]}"
|
|
120
|
+
output << ""
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
output << "## Technical Debt"
|
|
124
|
+
output << ""
|
|
125
|
+
output << "Technical improvements to be addressed:"
|
|
126
|
+
output << ""
|
|
127
|
+
plan[:technical_debt].each do |debt|
|
|
128
|
+
output << "- **#{debt[:title]}:** #{debt[:description]} (Effort: #{debt[:effort]})"
|
|
129
|
+
end
|
|
130
|
+
output << ""
|
|
131
|
+
|
|
132
|
+
output << "## Task Breakdown"
|
|
133
|
+
output << ""
|
|
134
|
+
plan[:tasks].each_with_index do |task, idx|
|
|
135
|
+
output << "### Task #{idx + 1}: #{task[:name]}"
|
|
136
|
+
output << ""
|
|
137
|
+
output << "**Description:** #{task[:description]}"
|
|
138
|
+
output << ""
|
|
139
|
+
output << "**Category:** #{task[:category]}"
|
|
140
|
+
output << ""
|
|
141
|
+
output << "**Priority:** #{task[:priority]}"
|
|
142
|
+
output << ""
|
|
143
|
+
output << "**Estimated Effort:** #{task[:effort]}"
|
|
144
|
+
output << ""
|
|
145
|
+
output << "**Dependencies:** #{task[:dependencies]&.join(", ") || "None"}"
|
|
146
|
+
output << ""
|
|
147
|
+
output << "**Success Criteria:**"
|
|
148
|
+
(task[:success_criteria] || []).each do |criterion|
|
|
149
|
+
output << "- #{criterion}"
|
|
150
|
+
end
|
|
151
|
+
output << ""
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
output << "## Success Metrics"
|
|
155
|
+
output << ""
|
|
156
|
+
output << "How we'll measure success of this iteration:"
|
|
157
|
+
output << ""
|
|
158
|
+
plan[:success_metrics].each do |metric|
|
|
159
|
+
output << "- **#{metric[:name]}:** #{metric[:target]}"
|
|
160
|
+
output << ""
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
output << "## Risks and Mitigation"
|
|
164
|
+
output << ""
|
|
165
|
+
plan[:risks].each_with_index do |risk, idx|
|
|
166
|
+
output << "#{idx + 1}. **#{risk[:title]}**"
|
|
167
|
+
output << " - Probability: #{risk[:probability]}"
|
|
168
|
+
output << " - Impact: #{risk[:impact]}"
|
|
169
|
+
output << " - Mitigation: #{risk[:mitigation]}"
|
|
170
|
+
output << ""
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
output << "## Timeline"
|
|
174
|
+
output << ""
|
|
175
|
+
plan[:timeline].each do |phase|
|
|
176
|
+
output << "- **#{phase[:phase]}:** #{phase[:duration]} (#{phase[:activities]})"
|
|
177
|
+
end
|
|
178
|
+
output << ""
|
|
179
|
+
|
|
180
|
+
output.join("\n")
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
def generate_plan_with_ai(feedback_analysis, current_mvp)
|
|
186
|
+
Aidp.log_debug("iteration_plan_generator", "generate_plan_with_ai")
|
|
187
|
+
|
|
188
|
+
prompt = build_iteration_prompt(feedback_analysis, current_mvp)
|
|
189
|
+
|
|
190
|
+
schema = {
|
|
191
|
+
type: "object",
|
|
192
|
+
properties: {
|
|
193
|
+
overview: {type: "string"},
|
|
194
|
+
goals: {type: "array", items: {type: "string"}},
|
|
195
|
+
improvements: {
|
|
196
|
+
type: "array",
|
|
197
|
+
items: {
|
|
198
|
+
type: "object",
|
|
199
|
+
properties: {
|
|
200
|
+
feature_name: {type: "string"},
|
|
201
|
+
issue: {type: "string"},
|
|
202
|
+
improvement: {type: "string"},
|
|
203
|
+
impact: {type: "string"},
|
|
204
|
+
effort: {type: "string"},
|
|
205
|
+
priority: {type: "string"}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
new_features: {
|
|
210
|
+
type: "array",
|
|
211
|
+
items: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
name: {type: "string"},
|
|
215
|
+
description: {type: "string"},
|
|
216
|
+
rationale: {type: "string"},
|
|
217
|
+
acceptance_criteria: {type: "array", items: {type: "string"}},
|
|
218
|
+
effort: {type: "string"}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
bug_fixes: {
|
|
223
|
+
type: "array",
|
|
224
|
+
items: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
title: {type: "string"},
|
|
228
|
+
description: {type: "string"},
|
|
229
|
+
priority: {type: "string"},
|
|
230
|
+
affected_users: {type: "string"}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
technical_debt: {
|
|
235
|
+
type: "array",
|
|
236
|
+
items: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
title: {type: "string"},
|
|
240
|
+
description: {type: "string"},
|
|
241
|
+
effort: {type: "string"}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
tasks: {
|
|
246
|
+
type: "array",
|
|
247
|
+
items: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
name: {type: "string"},
|
|
251
|
+
description: {type: "string"},
|
|
252
|
+
category: {type: "string"},
|
|
253
|
+
priority: {type: "string"},
|
|
254
|
+
effort: {type: "string"},
|
|
255
|
+
dependencies: {type: "array", items: {type: "string"}},
|
|
256
|
+
success_criteria: {type: "array", items: {type: "string"}}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
success_metrics: {
|
|
261
|
+
type: "array",
|
|
262
|
+
items: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
name: {type: "string"},
|
|
266
|
+
target: {type: "string"}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
risks: {
|
|
271
|
+
type: "array",
|
|
272
|
+
items: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
title: {type: "string"},
|
|
276
|
+
probability: {type: "string"},
|
|
277
|
+
impact: {type: "string"},
|
|
278
|
+
mitigation: {type: "string"}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
timeline: {
|
|
283
|
+
type: "array",
|
|
284
|
+
items: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
phase: {type: "string"},
|
|
288
|
+
duration: {type: "string"},
|
|
289
|
+
activities: {type: "string"}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
required: ["overview", "goals", "improvements", "tasks"]
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
decision = @ai_decision_engine.decide(
|
|
298
|
+
context: "iteration_plan_generation",
|
|
299
|
+
prompt: prompt,
|
|
300
|
+
data: {
|
|
301
|
+
feedback_analysis: feedback_analysis,
|
|
302
|
+
current_mvp: current_mvp
|
|
303
|
+
},
|
|
304
|
+
schema: schema
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
Aidp.log_debug("iteration_plan_generator", "ai_plan_generated",
|
|
308
|
+
tasks: decision[:tasks]&.size || 0,
|
|
309
|
+
improvements: decision[:improvements]&.size || 0)
|
|
310
|
+
|
|
311
|
+
decision
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def build_iteration_prompt(feedback_analysis, current_mvp)
|
|
315
|
+
mvp_context = if current_mvp
|
|
316
|
+
<<~CONTEXT
|
|
317
|
+
CURRENT MVP FEATURES:
|
|
318
|
+
#{current_mvp[:mvp_features]&.map { |f| "- #{f[:name]}: #{f[:description]}" }&.join("\n") || "No features"}
|
|
319
|
+
CONTEXT
|
|
320
|
+
else
|
|
321
|
+
""
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
<<~PROMPT
|
|
325
|
+
Generate a detailed iteration plan based on user feedback analysis.
|
|
326
|
+
|
|
327
|
+
FEEDBACK SUMMARY:
|
|
328
|
+
#{feedback_analysis[:summary]}
|
|
329
|
+
|
|
330
|
+
KEY FINDINGS:
|
|
331
|
+
#{feedback_analysis[:findings]&.map { |f| "- #{f[:title]}: #{f[:description]}" }&.join("\n") || "No findings"}
|
|
332
|
+
|
|
333
|
+
RECOMMENDATIONS:
|
|
334
|
+
#{feedback_analysis[:recommendations]&.map { |r| "- #{r[:title]}: #{r[:description]}" }&.join("\n") || "No recommendations"}
|
|
335
|
+
|
|
336
|
+
PRIORITY ISSUES:
|
|
337
|
+
#{feedback_analysis[:priority_issues]&.map { |i| "- #{i[:title]} (#{i[:priority]})" }&.join("\n") || "No priority issues"}
|
|
338
|
+
|
|
339
|
+
#{mvp_context}
|
|
340
|
+
|
|
341
|
+
TASK:
|
|
342
|
+
Create a comprehensive iteration plan:
|
|
343
|
+
|
|
344
|
+
1. OVERVIEW
|
|
345
|
+
- What is the focus of this iteration?
|
|
346
|
+
- Why these priorities?
|
|
347
|
+
|
|
348
|
+
2. ITERATION GOALS (3-5 goals)
|
|
349
|
+
- Clear, measurable goals for this iteration
|
|
350
|
+
|
|
351
|
+
3. FEATURE IMPROVEMENTS
|
|
352
|
+
- For existing features that need enhancement
|
|
353
|
+
- Current issue, proposed improvement, user impact, effort, priority
|
|
354
|
+
|
|
355
|
+
4. NEW FEATURES
|
|
356
|
+
- Features to add based on user requests
|
|
357
|
+
- Description, rationale, acceptance criteria, effort estimate
|
|
358
|
+
|
|
359
|
+
5. BUG FIXES
|
|
360
|
+
- Critical and high-priority bugs from feedback
|
|
361
|
+
- Priority, description, affected users
|
|
362
|
+
|
|
363
|
+
6. TECHNICAL DEBT
|
|
364
|
+
- Technical improvements needed
|
|
365
|
+
- Performance, scalability, maintainability issues
|
|
366
|
+
|
|
367
|
+
7. TASK BREAKDOWN
|
|
368
|
+
- Specific, actionable tasks
|
|
369
|
+
- For each: description, category, priority, effort, dependencies, success criteria
|
|
370
|
+
- Categories: feature, improvement, bug_fix, tech_debt, testing, documentation
|
|
371
|
+
|
|
372
|
+
8. SUCCESS METRICS
|
|
373
|
+
- How to measure iteration success
|
|
374
|
+
- Specific targets
|
|
375
|
+
|
|
376
|
+
9. RISKS AND MITIGATION
|
|
377
|
+
- What could go wrong?
|
|
378
|
+
- Probability (low/medium/high)
|
|
379
|
+
- Impact (low/medium/high)
|
|
380
|
+
- Mitigation strategy
|
|
381
|
+
|
|
382
|
+
10. TIMELINE
|
|
383
|
+
- Phases of the iteration
|
|
384
|
+
- Duration and key activities
|
|
385
|
+
|
|
386
|
+
Prioritize based on user impact, effort, and dependencies. Be specific and actionable.
|
|
387
|
+
PROMPT
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
end
|