aidp 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +210 -0
- data/bin/aidp +5 -0
- data/lib/aidp/analyze/agent_personas.rb +71 -0
- data/lib/aidp/analyze/agent_tool_executor.rb +445 -0
- data/lib/aidp/analyze/data_retention_manager.rb +426 -0
- data/lib/aidp/analyze/database.rb +243 -0
- data/lib/aidp/analyze/dependencies.rb +335 -0
- data/lib/aidp/analyze/error_handler.rb +486 -0
- data/lib/aidp/analyze/export_manager.rb +425 -0
- data/lib/aidp/analyze/feature_analyzer.rb +397 -0
- data/lib/aidp/analyze/focus_guidance.rb +517 -0
- data/lib/aidp/analyze/incremental_analyzer.rb +543 -0
- data/lib/aidp/analyze/language_analysis_strategies.rb +897 -0
- data/lib/aidp/analyze/large_analysis_progress.rb +504 -0
- data/lib/aidp/analyze/memory_manager.rb +365 -0
- data/lib/aidp/analyze/parallel_processor.rb +460 -0
- data/lib/aidp/analyze/performance_optimizer.rb +694 -0
- data/lib/aidp/analyze/prioritizer.rb +402 -0
- data/lib/aidp/analyze/progress.rb +75 -0
- data/lib/aidp/analyze/progress_visualizer.rb +320 -0
- data/lib/aidp/analyze/report_generator.rb +582 -0
- data/lib/aidp/analyze/repository_chunker.rb +702 -0
- data/lib/aidp/analyze/ruby_maat_integration.rb +572 -0
- data/lib/aidp/analyze/runner.rb +245 -0
- data/lib/aidp/analyze/static_analysis_detector.rb +577 -0
- data/lib/aidp/analyze/steps.rb +53 -0
- data/lib/aidp/analyze/storage.rb +600 -0
- data/lib/aidp/analyze/tool_configuration.rb +456 -0
- data/lib/aidp/analyze/tool_modernization.rb +750 -0
- data/lib/aidp/execute/progress.rb +76 -0
- data/lib/aidp/execute/runner.rb +135 -0
- data/lib/aidp/execute/steps.rb +113 -0
- data/lib/aidp/shared/cli.rb +117 -0
- data/lib/aidp/shared/config.rb +35 -0
- data/lib/aidp/shared/project_detector.rb +119 -0
- data/lib/aidp/shared/providers/anthropic.rb +26 -0
- data/lib/aidp/shared/providers/base.rb +17 -0
- data/lib/aidp/shared/providers/cursor.rb +102 -0
- data/lib/aidp/shared/providers/gemini.rb +26 -0
- data/lib/aidp/shared/providers/macos_ui.rb +26 -0
- data/lib/aidp/shared/sync.rb +15 -0
- data/lib/aidp/shared/util.rb +41 -0
- data/lib/aidp/shared/version.rb +7 -0
- data/lib/aidp/shared/workspace.rb +21 -0
- data/lib/aidp.rb +53 -0
- data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +100 -0
- data/templates/ANALYZE/02_ARCHITECTURE_ANALYSIS.md +151 -0
- data/templates/ANALYZE/03_TEST_ANALYSIS.md +182 -0
- data/templates/ANALYZE/04_FUNCTIONALITY_ANALYSIS.md +200 -0
- data/templates/ANALYZE/05_DOCUMENTATION_ANALYSIS.md +202 -0
- data/templates/ANALYZE/06_STATIC_ANALYSIS.md +233 -0
- data/templates/ANALYZE/07_REFACTORING_RECOMMENDATIONS.md +316 -0
- data/templates/COMMON/AGENT_BASE.md +129 -0
- data/templates/COMMON/CONVENTIONS.md +19 -0
- data/templates/COMMON/TEMPLATES/ADR_TEMPLATE.md +21 -0
- data/templates/COMMON/TEMPLATES/DOMAIN_CHARTER.md +27 -0
- data/templates/COMMON/TEMPLATES/EVENT_EXAMPLE.yaml +16 -0
- data/templates/COMMON/TEMPLATES/MERMAID_C4.md +46 -0
- data/templates/COMMON/TEMPLATES/OPENAPI_STUB.yaml +11 -0
- data/templates/EXECUTE/00_PRD.md +36 -0
- data/templates/EXECUTE/01_NFRS.md +27 -0
- data/templates/EXECUTE/02A_ARCH_GATE_QUESTIONS.md +13 -0
- data/templates/EXECUTE/02_ARCHITECTURE.md +42 -0
- data/templates/EXECUTE/03_ADR_FACTORY.md +22 -0
- data/templates/EXECUTE/04_DOMAIN_DECOMPOSITION.md +24 -0
- data/templates/EXECUTE/05_CONTRACTS.md +27 -0
- data/templates/EXECUTE/06_THREAT_MODEL.md +23 -0
- data/templates/EXECUTE/07_TEST_PLAN.md +24 -0
- data/templates/EXECUTE/08_TASKS.md +29 -0
- data/templates/EXECUTE/09_SCAFFOLDING_DEVEX.md +25 -0
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +30 -0
- data/templates/EXECUTE/11_STATIC_ANALYSIS.md +22 -0
- data/templates/EXECUTE/12_OBSERVABILITY_SLOS.md +21 -0
- data/templates/EXECUTE/13_DELIVERY_ROLLOUT.md +21 -0
- data/templates/EXECUTE/14_DOCS_PORTAL.md +23 -0
- data/templates/EXECUTE/15_POST_RELEASE.md +25 -0
- metadata +301 -0
@@ -0,0 +1,402 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "ruby_maat_integration"
|
4
|
+
require_relative "feature_analyzer"
|
5
|
+
|
6
|
+
module Aidp
|
7
|
+
module Analyze
|
8
|
+
class Prioritizer
|
9
|
+
def initialize(project_dir = Dir.pwd)
|
10
|
+
@project_dir = project_dir
|
11
|
+
@code_maat = Aidp::Analyze::RubyMaatIntegration.new(project_dir)
|
12
|
+
@feature_analyzer = Aidp::Analyze::FeatureAnalyzer.new(project_dir)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Generate prioritized analysis recommendations based on Code Maat data
|
16
|
+
def generate_prioritized_recommendations
|
17
|
+
# Get Code Maat analysis data
|
18
|
+
code_maat_data = @code_maat.run_comprehensive_analysis
|
19
|
+
|
20
|
+
# Get feature analysis data
|
21
|
+
features = @feature_analyzer.detect_features
|
22
|
+
|
23
|
+
# Generate prioritized recommendations
|
24
|
+
{
|
25
|
+
high_priority: generate_high_priority_recommendations(code_maat_data, features),
|
26
|
+
medium_priority: generate_medium_priority_recommendations(code_maat_data, features),
|
27
|
+
low_priority: generate_low_priority_recommendations(code_maat_data, features),
|
28
|
+
focus_areas: identify_focus_areas(code_maat_data, features),
|
29
|
+
analysis_strategy: generate_analysis_strategy(code_maat_data, features)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Get high-priority analysis targets
|
34
|
+
def get_high_priority_targets
|
35
|
+
@code_maat.run_comprehensive_analysis
|
36
|
+
|
37
|
+
high_priority = []
|
38
|
+
|
39
|
+
# High churn + single author (knowledge silos)
|
40
|
+
high_churn_files = @code_maat.get_high_churn_files(10)
|
41
|
+
knowledge_silos = @code_maat.get_knowledge_silos
|
42
|
+
|
43
|
+
high_churn_files.each do |churn_file|
|
44
|
+
silo_file = knowledge_silos.find { |s| s[:file] == churn_file[:file] }
|
45
|
+
next unless silo_file
|
46
|
+
|
47
|
+
high_priority << {
|
48
|
+
type: "knowledge_silo",
|
49
|
+
file: churn_file[:file],
|
50
|
+
changes: churn_file[:changes],
|
51
|
+
author: silo_file[:authors].first,
|
52
|
+
priority_score: calculate_priority_score(churn_file, silo_file),
|
53
|
+
recommendation: "High churn file with single author - potential knowledge silo"
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Tightly coupled files
|
58
|
+
tightly_coupled = @code_maat.get_tightly_coupled_files(5)
|
59
|
+
tightly_coupled.each do |coupling|
|
60
|
+
high_priority << {
|
61
|
+
type: "tight_coupling",
|
62
|
+
file1: coupling[:file1],
|
63
|
+
file2: coupling[:file2],
|
64
|
+
shared_changes: coupling[:shared_changes],
|
65
|
+
priority_score: coupling[:shared_changes] * 2,
|
66
|
+
recommendation: "Tightly coupled files - consider refactoring to reduce coupling"
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
high_priority.sort_by { |item| -item[:priority_score] }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Get medium-priority analysis targets
|
74
|
+
def get_medium_priority_targets
|
75
|
+
code_maat_data = @code_maat.run_comprehensive_analysis
|
76
|
+
|
77
|
+
medium_priority = []
|
78
|
+
|
79
|
+
# High churn + multiple authors (coordination issues)
|
80
|
+
high_churn_files = @code_maat.get_high_churn_files(5)
|
81
|
+
multi_author_files = code_maat_data[:authorship][:files].select { |f| f[:author_count] > 1 }
|
82
|
+
|
83
|
+
high_churn_files.each do |churn_file|
|
84
|
+
multi_auth_file = multi_author_files.find { |m| m[:file] == churn_file[:file] }
|
85
|
+
next unless multi_auth_file
|
86
|
+
|
87
|
+
medium_priority << {
|
88
|
+
type: "coordination_issue",
|
89
|
+
file: churn_file[:file],
|
90
|
+
changes: churn_file[:changes],
|
91
|
+
authors: multi_auth_file[:authors],
|
92
|
+
priority_score: calculate_priority_score(churn_file, multi_auth_file),
|
93
|
+
recommendation: "High churn file with multiple authors - potential coordination issues"
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
# Medium churn files
|
98
|
+
medium_churn_files = code_maat_data[:churn][:files].select { |f| f[:changes] > 3 && f[:changes] <= 10 }
|
99
|
+
medium_churn_files.each do |file|
|
100
|
+
medium_priority << {
|
101
|
+
type: "medium_churn",
|
102
|
+
file: file[:file],
|
103
|
+
changes: file[:changes],
|
104
|
+
priority_score: file[:changes],
|
105
|
+
recommendation: "Medium churn file - monitor for increasing complexity"
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
medium_priority.sort_by { |item| -item[:priority_score] }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get low-priority analysis targets
|
113
|
+
def get_low_priority_targets
|
114
|
+
code_maat_data = @code_maat.run_comprehensive_analysis
|
115
|
+
|
116
|
+
low_priority = []
|
117
|
+
|
118
|
+
# Low churn files
|
119
|
+
low_churn_files = code_maat_data[:churn][:files].select { |f| f[:changes] <= 3 }
|
120
|
+
low_churn_files.each do |file|
|
121
|
+
low_priority << {
|
122
|
+
type: "low_churn",
|
123
|
+
file: file[:file],
|
124
|
+
changes: file[:changes],
|
125
|
+
priority_score: file[:changes],
|
126
|
+
recommendation: "Low churn file - stable, may not need immediate attention"
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
low_priority.sort_by { |item| -item[:priority_score] }
|
131
|
+
end
|
132
|
+
|
133
|
+
# Identify focus areas for analysis
|
134
|
+
def identify_focus_areas
|
135
|
+
@code_maat.run_comprehensive_analysis
|
136
|
+
@feature_analyzer.detect_features
|
137
|
+
|
138
|
+
focus_areas = []
|
139
|
+
|
140
|
+
# High churn areas
|
141
|
+
high_churn_files = @code_maat.get_high_churn_files(8)
|
142
|
+
high_churn_areas = group_files_by_directory(high_churn_files.map { |f| f[:file] })
|
143
|
+
|
144
|
+
high_churn_areas.each do |area, files|
|
145
|
+
focus_areas << {
|
146
|
+
type: "high_churn_area",
|
147
|
+
area: area,
|
148
|
+
files: files,
|
149
|
+
priority: "high",
|
150
|
+
recommendation: "High churn area - focus analysis on #{area} directory"
|
151
|
+
}
|
152
|
+
end
|
153
|
+
|
154
|
+
# Knowledge silo areas
|
155
|
+
knowledge_silos = @code_maat.get_knowledge_silos
|
156
|
+
silo_areas = group_files_by_directory(knowledge_silos.map { |f| f[:file] })
|
157
|
+
|
158
|
+
silo_areas.each do |area, files|
|
159
|
+
focus_areas << {
|
160
|
+
type: "knowledge_silo_area",
|
161
|
+
area: area,
|
162
|
+
files: files,
|
163
|
+
priority: "high",
|
164
|
+
recommendation: "Knowledge silo area - #{area} has single-author files"
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
# Coupling hotspots
|
169
|
+
tightly_coupled = @code_maat.get_tightly_coupled_files(3)
|
170
|
+
coupling_areas = identify_coupling_hotspots(tightly_coupled)
|
171
|
+
|
172
|
+
coupling_areas.each do |area, couplings|
|
173
|
+
focus_areas << {
|
174
|
+
type: "coupling_hotspot",
|
175
|
+
area: area,
|
176
|
+
couplings: couplings,
|
177
|
+
priority: "medium",
|
178
|
+
recommendation: "Coupling hotspot - #{area} has tightly coupled files"
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
focus_areas
|
183
|
+
end
|
184
|
+
|
185
|
+
# Generate analysis strategy recommendations
|
186
|
+
def generate_analysis_strategy
|
187
|
+
code_maat_data = @code_maat.run_comprehensive_analysis
|
188
|
+
@feature_analyzer.detect_features
|
189
|
+
|
190
|
+
{
|
191
|
+
overall_approach: determine_overall_approach(code_maat_data),
|
192
|
+
analysis_order: determine_analysis_order(code_maat_data),
|
193
|
+
resource_allocation: determine_resource_allocation(code_maat_data),
|
194
|
+
risk_assessment: assess_analysis_risks(code_maat_data),
|
195
|
+
success_metrics: define_success_metrics(code_maat_data)
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def generate_high_priority_recommendations(code_maat_data, features)
|
202
|
+
recommendations = []
|
203
|
+
|
204
|
+
# Knowledge silos
|
205
|
+
knowledge_silos = @code_maat.get_knowledge_silos
|
206
|
+
knowledge_silos.each do |silo|
|
207
|
+
recommendations << {
|
208
|
+
type: "knowledge_silo",
|
209
|
+
target: silo[:file],
|
210
|
+
priority: "high",
|
211
|
+
rationale: "Single author with #{silo[:changes]} changes",
|
212
|
+
action: "Document knowledge and consider pair programming",
|
213
|
+
effort: "medium",
|
214
|
+
impact: "high"
|
215
|
+
}
|
216
|
+
end
|
217
|
+
|
218
|
+
# Tight coupling
|
219
|
+
tightly_coupled = @code_maat.get_tightly_coupled_files(5)
|
220
|
+
tightly_coupled.each do |coupling|
|
221
|
+
recommendations << {
|
222
|
+
type: "tight_coupling",
|
223
|
+
target: "#{coupling[:file1]} ↔ #{coupling[:file2]}",
|
224
|
+
priority: "high",
|
225
|
+
rationale: "#{coupling[:shared_changes]} shared changes",
|
226
|
+
action: "Refactor to reduce coupling",
|
227
|
+
effort: "high",
|
228
|
+
impact: "high"
|
229
|
+
}
|
230
|
+
end
|
231
|
+
|
232
|
+
recommendations
|
233
|
+
end
|
234
|
+
|
235
|
+
def generate_medium_priority_recommendations(code_maat_data, features)
|
236
|
+
recommendations = []
|
237
|
+
|
238
|
+
# High churn with multiple authors
|
239
|
+
high_churn_multi_author = code_maat_data[:churn][:files].select do |file|
|
240
|
+
file[:changes] > 5 &&
|
241
|
+
(code_maat_data[:authorship][:files].find { |a| a[:file] == file[:file] }&.dig(:author_count)&.> 1)
|
242
|
+
end
|
243
|
+
|
244
|
+
high_churn_multi_author.each do |file|
|
245
|
+
recommendations << {
|
246
|
+
type: "coordination_issue",
|
247
|
+
target: file[:file],
|
248
|
+
priority: "medium",
|
249
|
+
rationale: "High churn (#{file[:changes]} changes) with multiple authors",
|
250
|
+
action: "Improve coordination and communication",
|
251
|
+
effort: "medium",
|
252
|
+
impact: "medium"
|
253
|
+
}
|
254
|
+
end
|
255
|
+
|
256
|
+
recommendations
|
257
|
+
end
|
258
|
+
|
259
|
+
def generate_low_priority_recommendations(code_maat_data, features)
|
260
|
+
recommendations = []
|
261
|
+
|
262
|
+
# Stable files
|
263
|
+
stable_files = code_maat_data[:churn][:files].select { |f| f[:changes] <= 2 }
|
264
|
+
stable_files.each do |file|
|
265
|
+
recommendations << {
|
266
|
+
type: "stable_file",
|
267
|
+
target: file[:file],
|
268
|
+
priority: "low",
|
269
|
+
rationale: "Low churn (#{file[:changes]} changes) - stable",
|
270
|
+
action: "Monitor for changes",
|
271
|
+
effort: "low",
|
272
|
+
impact: "low"
|
273
|
+
}
|
274
|
+
end
|
275
|
+
|
276
|
+
recommendations
|
277
|
+
end
|
278
|
+
|
279
|
+
def calculate_priority_score(churn_file, authorship_file)
|
280
|
+
base_score = churn_file[:changes]
|
281
|
+
|
282
|
+
# Adjust for authorship patterns
|
283
|
+
if authorship_file[:author_count] == 1
|
284
|
+
base_score *= 1.5 # Knowledge silo penalty
|
285
|
+
elsif authorship_file[:author_count] > 3
|
286
|
+
base_score *= 1.2 # Coordination complexity penalty
|
287
|
+
end
|
288
|
+
|
289
|
+
base_score
|
290
|
+
end
|
291
|
+
|
292
|
+
def group_files_by_directory(files)
|
293
|
+
grouped = {}
|
294
|
+
|
295
|
+
files.each do |file|
|
296
|
+
dir = File.dirname(file)
|
297
|
+
grouped[dir] ||= []
|
298
|
+
grouped[dir] << file
|
299
|
+
end
|
300
|
+
|
301
|
+
grouped
|
302
|
+
end
|
303
|
+
|
304
|
+
def identify_coupling_hotspots(couplings)
|
305
|
+
hotspots = {}
|
306
|
+
|
307
|
+
couplings.each do |coupling|
|
308
|
+
dir1 = File.dirname(coupling[:file1])
|
309
|
+
dir2 = File.dirname(coupling[:file2])
|
310
|
+
|
311
|
+
# Group by common directory or create cross-directory coupling
|
312
|
+
if dir1 == dir2
|
313
|
+
hotspots[dir1] ||= []
|
314
|
+
hotspots[dir1] << coupling
|
315
|
+
else
|
316
|
+
cross_dir = "#{dir1} ↔ #{dir2}"
|
317
|
+
hotspots[cross_dir] ||= []
|
318
|
+
hotspots[cross_dir] << coupling
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
hotspots
|
323
|
+
end
|
324
|
+
|
325
|
+
def determine_overall_approach(code_maat_data)
|
326
|
+
total_files = code_maat_data[:churn][:total_files]
|
327
|
+
high_churn_count = code_maat_data[:churn][:files].count { |f| f[:changes] > 10 }
|
328
|
+
knowledge_silos = code_maat_data[:authorship][:files_with_single_author]
|
329
|
+
|
330
|
+
if high_churn_count > total_files * 0.3
|
331
|
+
"aggressive_refactoring"
|
332
|
+
elsif knowledge_silos > total_files * 0.2
|
333
|
+
"knowledge_transfer_focused"
|
334
|
+
elsif code_maat_data[:coupling][:average_coupling] > 5
|
335
|
+
"coupling_reduction_focused"
|
336
|
+
else
|
337
|
+
"incremental_improvement"
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def determine_analysis_order(code_maat_data)
|
342
|
+
order = []
|
343
|
+
|
344
|
+
# Start with highest impact areas
|
345
|
+
order << "knowledge_silos" if code_maat_data[:authorship][:files_with_single_author] > 0
|
346
|
+
|
347
|
+
order << "coupling_analysis" if code_maat_data[:coupling][:average_coupling] > 3
|
348
|
+
|
349
|
+
order << "high_churn_analysis" if code_maat_data[:churn][:files].any? { |f| f[:changes] > 15 }
|
350
|
+
|
351
|
+
order << "general_quality_analysis"
|
352
|
+
order
|
353
|
+
end
|
354
|
+
|
355
|
+
def determine_resource_allocation(code_maat_data)
|
356
|
+
total_files = code_maat_data[:churn][:total_files]
|
357
|
+
|
358
|
+
{
|
359
|
+
high_priority_percentage: 20,
|
360
|
+
medium_priority_percentage: 50,
|
361
|
+
low_priority_percentage: 30,
|
362
|
+
estimated_effort_hours: total_files * 0.5,
|
363
|
+
recommended_team_size: [total_files / 50, 1].max
|
364
|
+
}
|
365
|
+
end
|
366
|
+
|
367
|
+
def assess_analysis_risks(code_maat_data)
|
368
|
+
risks = []
|
369
|
+
|
370
|
+
if code_maat_data[:authorship][:files_with_single_author] > code_maat_data[:churn][:total_files] * 0.3
|
371
|
+
risks << {
|
372
|
+
type: "knowledge_silo_risk",
|
373
|
+
severity: "high",
|
374
|
+
description: "High percentage of single-author files indicates knowledge silos",
|
375
|
+
mitigation: "Implement knowledge sharing and documentation practices"
|
376
|
+
}
|
377
|
+
end
|
378
|
+
|
379
|
+
if code_maat_data[:coupling][:average_coupling] > 8
|
380
|
+
risks << {
|
381
|
+
type: "coupling_risk",
|
382
|
+
severity: "medium",
|
383
|
+
description: "High average coupling indicates tight dependencies",
|
384
|
+
mitigation: "Focus on reducing coupling through refactoring"
|
385
|
+
}
|
386
|
+
end
|
387
|
+
|
388
|
+
risks
|
389
|
+
end
|
390
|
+
|
391
|
+
def define_success_metrics(code_maat_data)
|
392
|
+
{
|
393
|
+
knowledge_silo_reduction: "Reduce single-author files by 50%",
|
394
|
+
coupling_reduction: "Reduce average coupling by 30%",
|
395
|
+
churn_stabilization: "Reduce high-churn files by 25%",
|
396
|
+
documentation_coverage: "Achieve 80% documentation coverage",
|
397
|
+
test_coverage: "Achieve 90% test coverage"
|
398
|
+
}
|
399
|
+
end
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "time"
|
5
|
+
|
6
|
+
module Aidp
|
7
|
+
module Analyze
|
8
|
+
# Manages progress tracking for analyze mode, isolated from execute mode
|
9
|
+
class Progress
|
10
|
+
attr_reader :project_dir, :progress_file
|
11
|
+
|
12
|
+
def initialize(project_dir)
|
13
|
+
@project_dir = project_dir
|
14
|
+
@progress_file = File.join(project_dir, ".aidp-analyze-progress.yml")
|
15
|
+
load_progress
|
16
|
+
end
|
17
|
+
|
18
|
+
def completed_steps
|
19
|
+
@progress["completed_steps"] || []
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_step
|
23
|
+
@progress["current_step"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def started_at
|
27
|
+
@progress["started_at"] ? Time.parse(@progress["started_at"]) : nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def step_completed?(step_name)
|
31
|
+
completed_steps.include?(step_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def mark_step_completed(step_name)
|
35
|
+
@progress["completed_steps"] ||= []
|
36
|
+
@progress["completed_steps"] << step_name unless step_completed?(step_name)
|
37
|
+
@progress["current_step"] = nil
|
38
|
+
save_progress
|
39
|
+
end
|
40
|
+
|
41
|
+
def mark_step_in_progress(step_name)
|
42
|
+
@progress["current_step"] = step_name
|
43
|
+
@progress["started_at"] ||= Time.now.iso8601
|
44
|
+
save_progress
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset
|
48
|
+
@progress = {
|
49
|
+
"completed_steps" => [],
|
50
|
+
"current_step" => nil,
|
51
|
+
"started_at" => nil
|
52
|
+
}
|
53
|
+
save_progress
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_step
|
57
|
+
Aidp::Analyze::Steps::SPEC.keys.find { |step| !step_completed?(step) }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def load_progress
|
63
|
+
@progress = if File.exist?(@progress_file)
|
64
|
+
YAML.load_file(@progress_file) || {}
|
65
|
+
else
|
66
|
+
{}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def save_progress
|
71
|
+
File.write(@progress_file, @progress.to_yaml)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|