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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +210 -0
  4. data/bin/aidp +5 -0
  5. data/lib/aidp/analyze/agent_personas.rb +71 -0
  6. data/lib/aidp/analyze/agent_tool_executor.rb +445 -0
  7. data/lib/aidp/analyze/data_retention_manager.rb +426 -0
  8. data/lib/aidp/analyze/database.rb +243 -0
  9. data/lib/aidp/analyze/dependencies.rb +335 -0
  10. data/lib/aidp/analyze/error_handler.rb +486 -0
  11. data/lib/aidp/analyze/export_manager.rb +425 -0
  12. data/lib/aidp/analyze/feature_analyzer.rb +397 -0
  13. data/lib/aidp/analyze/focus_guidance.rb +517 -0
  14. data/lib/aidp/analyze/incremental_analyzer.rb +543 -0
  15. data/lib/aidp/analyze/language_analysis_strategies.rb +897 -0
  16. data/lib/aidp/analyze/large_analysis_progress.rb +504 -0
  17. data/lib/aidp/analyze/memory_manager.rb +365 -0
  18. data/lib/aidp/analyze/parallel_processor.rb +460 -0
  19. data/lib/aidp/analyze/performance_optimizer.rb +694 -0
  20. data/lib/aidp/analyze/prioritizer.rb +402 -0
  21. data/lib/aidp/analyze/progress.rb +75 -0
  22. data/lib/aidp/analyze/progress_visualizer.rb +320 -0
  23. data/lib/aidp/analyze/report_generator.rb +582 -0
  24. data/lib/aidp/analyze/repository_chunker.rb +702 -0
  25. data/lib/aidp/analyze/ruby_maat_integration.rb +572 -0
  26. data/lib/aidp/analyze/runner.rb +245 -0
  27. data/lib/aidp/analyze/static_analysis_detector.rb +577 -0
  28. data/lib/aidp/analyze/steps.rb +53 -0
  29. data/lib/aidp/analyze/storage.rb +600 -0
  30. data/lib/aidp/analyze/tool_configuration.rb +456 -0
  31. data/lib/aidp/analyze/tool_modernization.rb +750 -0
  32. data/lib/aidp/execute/progress.rb +76 -0
  33. data/lib/aidp/execute/runner.rb +135 -0
  34. data/lib/aidp/execute/steps.rb +113 -0
  35. data/lib/aidp/shared/cli.rb +117 -0
  36. data/lib/aidp/shared/config.rb +35 -0
  37. data/lib/aidp/shared/project_detector.rb +119 -0
  38. data/lib/aidp/shared/providers/anthropic.rb +26 -0
  39. data/lib/aidp/shared/providers/base.rb +17 -0
  40. data/lib/aidp/shared/providers/cursor.rb +102 -0
  41. data/lib/aidp/shared/providers/gemini.rb +26 -0
  42. data/lib/aidp/shared/providers/macos_ui.rb +26 -0
  43. data/lib/aidp/shared/sync.rb +15 -0
  44. data/lib/aidp/shared/util.rb +41 -0
  45. data/lib/aidp/shared/version.rb +7 -0
  46. data/lib/aidp/shared/workspace.rb +21 -0
  47. data/lib/aidp.rb +53 -0
  48. data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +100 -0
  49. data/templates/ANALYZE/02_ARCHITECTURE_ANALYSIS.md +151 -0
  50. data/templates/ANALYZE/03_TEST_ANALYSIS.md +182 -0
  51. data/templates/ANALYZE/04_FUNCTIONALITY_ANALYSIS.md +200 -0
  52. data/templates/ANALYZE/05_DOCUMENTATION_ANALYSIS.md +202 -0
  53. data/templates/ANALYZE/06_STATIC_ANALYSIS.md +233 -0
  54. data/templates/ANALYZE/07_REFACTORING_RECOMMENDATIONS.md +316 -0
  55. data/templates/COMMON/AGENT_BASE.md +129 -0
  56. data/templates/COMMON/CONVENTIONS.md +19 -0
  57. data/templates/COMMON/TEMPLATES/ADR_TEMPLATE.md +21 -0
  58. data/templates/COMMON/TEMPLATES/DOMAIN_CHARTER.md +27 -0
  59. data/templates/COMMON/TEMPLATES/EVENT_EXAMPLE.yaml +16 -0
  60. data/templates/COMMON/TEMPLATES/MERMAID_C4.md +46 -0
  61. data/templates/COMMON/TEMPLATES/OPENAPI_STUB.yaml +11 -0
  62. data/templates/EXECUTE/00_PRD.md +36 -0
  63. data/templates/EXECUTE/01_NFRS.md +27 -0
  64. data/templates/EXECUTE/02A_ARCH_GATE_QUESTIONS.md +13 -0
  65. data/templates/EXECUTE/02_ARCHITECTURE.md +42 -0
  66. data/templates/EXECUTE/03_ADR_FACTORY.md +22 -0
  67. data/templates/EXECUTE/04_DOMAIN_DECOMPOSITION.md +24 -0
  68. data/templates/EXECUTE/05_CONTRACTS.md +27 -0
  69. data/templates/EXECUTE/06_THREAT_MODEL.md +23 -0
  70. data/templates/EXECUTE/07_TEST_PLAN.md +24 -0
  71. data/templates/EXECUTE/08_TASKS.md +29 -0
  72. data/templates/EXECUTE/09_SCAFFOLDING_DEVEX.md +25 -0
  73. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +30 -0
  74. data/templates/EXECUTE/11_STATIC_ANALYSIS.md +22 -0
  75. data/templates/EXECUTE/12_OBSERVABILITY_SLOS.md +21 -0
  76. data/templates/EXECUTE/13_DELIVERY_ROLLOUT.md +21 -0
  77. data/templates/EXECUTE/14_DOCS_PORTAL.md +23 -0
  78. data/templates/EXECUTE/15_POST_RELEASE.md +25 -0
  79. metadata +301 -0
@@ -0,0 +1,397 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Aidp
6
+ class FeatureAnalyzer
7
+ def initialize(project_dir = Dir.pwd)
8
+ @project_dir = project_dir
9
+ end
10
+
11
+ # Detect and categorize features in the codebase
12
+ def detect_features
13
+ features = []
14
+
15
+ # Scan directories for potential features
16
+ scan_directories_for_features(features)
17
+
18
+ # Scan files for feature indicators
19
+ scan_files_for_features(features)
20
+
21
+ # Analyze feature relationships
22
+ analyze_feature_relationships(features)
23
+
24
+ # Categorize features
25
+ categorize_features(features)
26
+
27
+ features
28
+ end
29
+
30
+ # Get feature-specific agent recommendations
31
+ def get_feature_agent_recommendations(feature)
32
+ {
33
+ feature: feature[:name],
34
+ primary_agent: determine_primary_agent(feature),
35
+ specialized_agents: determine_specialized_agents(feature),
36
+ analysis_priority: determine_analysis_priority(feature)
37
+ }
38
+ end
39
+
40
+ # Coordinate multi-agent analysis for a feature
41
+ def coordinate_feature_analysis(feature)
42
+ agents = get_feature_agent_recommendations(feature)
43
+
44
+ {
45
+ feature: feature[:name],
46
+ primary_analysis: {
47
+ agent: agents[:primary_agent],
48
+ focus_areas: get_agent_focus_areas(agents[:primary_agent]),
49
+ output_files: generate_output_files(feature, agents[:primary_agent])
50
+ },
51
+ specialized_analyses: agents[:specialized_agents].map do |agent|
52
+ {
53
+ agent: agent,
54
+ focus_areas: get_agent_focus_areas(agent),
55
+ output_files: generate_output_files(feature, agent)
56
+ }
57
+ end,
58
+ coordination_notes: generate_coordination_notes(feature, agents)
59
+ }
60
+ end
61
+
62
+ private
63
+
64
+ def scan_directories_for_features(features)
65
+ # Common feature directory patterns
66
+ feature_patterns = [
67
+ "features/", "modules/", "components/", "services/",
68
+ "controllers/", "models/", "views/", "api/",
69
+ "handlers/", "processors/", "managers/", "utils/"
70
+ ]
71
+
72
+ feature_patterns.each do |pattern|
73
+ pattern_path = File.join(@project_dir, pattern)
74
+ next unless Dir.exist?(pattern_path)
75
+
76
+ Dir.entries(pattern_path).each do |entry|
77
+ next if entry.start_with?(".") || entry == ".."
78
+
79
+ feature_path = File.join(pattern_path, entry)
80
+ next unless Dir.exist?(feature_path)
81
+
82
+ features << {
83
+ name: entry || "unknown",
84
+ type: "directory",
85
+ path: feature_path,
86
+ category: infer_category_from_path(pattern, entry),
87
+ complexity: estimate_complexity(feature_path)
88
+ }
89
+ end
90
+ end
91
+ end
92
+
93
+ def scan_files_for_features(features)
94
+ # Scan for feature indicators in files
95
+ feature_indicators = [
96
+ "class.*Controller", "class.*Service", "class.*Manager",
97
+ "module.*Feature", "def.*feature", "function.*feature"
98
+ ]
99
+
100
+ Dir.glob(File.join(@project_dir, "**", "*.rb")).each do |file|
101
+ next if file.include?("spec/") || file.include?("test/")
102
+
103
+ content = File.read(file)
104
+ feature_found = false
105
+
106
+ feature_indicators.each do |indicator|
107
+ next unless content.match?(/#{indicator}/i)
108
+
109
+ feature_name = extract_feature_name(file, content, indicator)
110
+ features << {
111
+ name: feature_name,
112
+ type: "file",
113
+ path: file,
114
+ category: infer_category_from_file(file, content),
115
+ complexity: estimate_file_complexity(content)
116
+ }
117
+ feature_found = true
118
+ break
119
+ end
120
+
121
+ # If no specific pattern matched, add the file as a feature using filename
122
+ next if feature_found
123
+
124
+ feature_name = File.basename(file, ".*").capitalize
125
+ features << {
126
+ name: feature_name,
127
+ type: "file",
128
+ path: file,
129
+ category: infer_category_from_file(file, content),
130
+ complexity: estimate_file_complexity(content)
131
+ }
132
+ end
133
+ end
134
+
135
+ def analyze_feature_relationships(features)
136
+ features.each do |feature|
137
+ feature[:dependencies] = find_feature_dependencies(feature)
138
+ feature[:dependents] = find_feature_dependents(feature, features)
139
+ feature[:coupling] = calculate_coupling_score(feature)
140
+ end
141
+ end
142
+
143
+ def categorize_features(features)
144
+ features.each do |feature|
145
+ feature[:category] ||= infer_category(feature)
146
+ feature[:priority] = calculate_priority(feature)
147
+ feature[:business_value] = estimate_business_value(feature)
148
+ feature[:technical_debt] = estimate_technical_debt(feature)
149
+ end
150
+ end
151
+
152
+ def determine_primary_agent(feature)
153
+ case feature[:category]
154
+ when "core_business"
155
+ "Functionality Analyst"
156
+ when "api"
157
+ "Architecture Analyst"
158
+ when "data"
159
+ "Functionality Analyst"
160
+ when "ui"
161
+ "Functionality Analyst"
162
+ when "utility"
163
+ "Static Analysis Expert"
164
+ else
165
+ "Functionality Analyst"
166
+ end
167
+ end
168
+
169
+ def determine_specialized_agents(feature)
170
+ agents = []
171
+
172
+ # Add specialized agents based on feature characteristics
173
+ agents << "Test Analyst" if feature[:complexity] > 5
174
+ agents << "Architecture Analyst" if feature[:coupling] > 0.7
175
+ agents << "Documentation Analyst" if feature[:business_value] > 0.8
176
+ agents << "Refactoring Specialist" if feature[:technical_debt] > 0.6
177
+
178
+ agents.uniq
179
+ end
180
+
181
+ def determine_analysis_priority(feature)
182
+ # Calculate priority based on business value, complexity, and technical debt
183
+ priority_score = (
184
+ feature[:business_value] * 0.4 +
185
+ feature[:complexity] * 0.3 +
186
+ feature[:technical_debt] * 0.3
187
+ )
188
+
189
+ case priority_score
190
+ when 0.8..1.0
191
+ "high"
192
+ when 0.5..0.8
193
+ "medium"
194
+ else
195
+ "low"
196
+ end
197
+ end
198
+
199
+ def get_agent_focus_areas(agent_name)
200
+ case agent_name
201
+ when "Functionality Analyst"
202
+ %w[feature_mapping complexity_analysis dead_code_identification]
203
+ when "Architecture Analyst"
204
+ %w[dependency_analysis coupling_assessment design_patterns]
205
+ when "Test Analyst"
206
+ %w[test_coverage test_quality testing_gaps]
207
+ when "Documentation Analyst"
208
+ %w[documentation_gaps documentation_quality user_needs]
209
+ when "Static Analysis Expert"
210
+ %w[code_quality tool_integration best_practices]
211
+ when "Refactoring Specialist"
212
+ %w[technical_debt code_smells refactoring_opportunities]
213
+ else
214
+ ["general_analysis"]
215
+ end
216
+ end
217
+
218
+ def generate_output_files(feature, agent_name)
219
+ base_name = (feature[:name] || "unknown").downcase.gsub(/[^a-z0-9]/, "_")
220
+ agent_suffix = agent_name.downcase.gsub(/\s+/, "_")
221
+
222
+ {
223
+ primary: "docs/#{base_name}_#{agent_suffix}_analysis.md",
224
+ secondary: "docs/#{base_name}_#{agent_suffix}_details.md",
225
+ data: "docs/#{base_name}_#{agent_suffix}_data.json"
226
+ }
227
+ end
228
+
229
+ def generate_coordination_notes(feature, agents)
230
+ notes = []
231
+
232
+ notes << "Feature: #{feature[:name] || "unknown"} (#{feature[:category] || "unknown"})"
233
+ notes << "Primary Agent: #{agents[:primary_agent]}"
234
+ notes << "Specialized Agents: #{agents[:specialized_agents].join(", ")}"
235
+ notes << "Priority: #{agents[:analysis_priority]}"
236
+ notes << "Focus Areas: #{get_agent_focus_areas(agents[:primary_agent]).join(", ")}"
237
+
238
+ notes.join("\n")
239
+ end
240
+
241
+ def infer_category_from_path(pattern, entry)
242
+ case pattern
243
+ when "controllers/"
244
+ "api"
245
+ when "models/"
246
+ "data"
247
+ when "views/"
248
+ "ui"
249
+ when "services/"
250
+ "core_business"
251
+ when "utils/"
252
+ "utility"
253
+ else
254
+ "core_business"
255
+ end
256
+ end
257
+
258
+ def infer_category_from_file(file, content)
259
+ if content.match?(/class.*Controller/i)
260
+ "api"
261
+ elsif content.match?(/class.*Model/i)
262
+ "data"
263
+ elsif content.match?(/class.*Service/i)
264
+ "core_business"
265
+ elsif content.match?(/class.*Util/i)
266
+ "utility"
267
+ else
268
+ "core_business"
269
+ end
270
+ end
271
+
272
+ def infer_category(feature)
273
+ # Default categorization logic
274
+ if feature[:name]&.match?(/controller|api|endpoint/i)
275
+ "api"
276
+ elsif feature[:name]&.match?(/model|data|entity/i)
277
+ "data"
278
+ elsif feature[:name]&.match?(/view|ui|component/i)
279
+ "ui"
280
+ elsif feature[:name]&.match?(/util|helper|tool/i)
281
+ "utility"
282
+ else
283
+ "core_business"
284
+ end
285
+ end
286
+
287
+ def estimate_complexity(path)
288
+ # Simple complexity estimation based on file count and size
289
+ file_count = Dir.glob(File.join(path, "**", "*.rb")).count
290
+ total_lines = Dir.glob(File.join(path, "**", "*.rb")).sum { |f| File.readlines(f).count }
291
+
292
+ complexity = (file_count * 0.3 + total_lines / 100.0 * 0.7).clamp(0, 10)
293
+ (complexity / 10.0).round(2)
294
+ end
295
+
296
+ def estimate_file_complexity(content)
297
+ # Simple complexity estimation based on lines of code
298
+ lines = content.lines.count
299
+ complexity = (lines / 50.0).clamp(0, 10)
300
+ (complexity / 10.0).round(2)
301
+ end
302
+
303
+ def find_feature_dependencies(feature)
304
+ # Simplified dependency detection
305
+ dependencies = []
306
+
307
+ if feature[:type] == "file"
308
+ content = File.read(feature[:path])
309
+ # Look for require/import statements
310
+ content.scan(/require\s+['"]([^'"]+)['"]/).each do |match|
311
+ dependencies << match[0]
312
+ end
313
+ end
314
+
315
+ dependencies
316
+ end
317
+
318
+ def find_feature_dependents(feature, all_features)
319
+ # Find features that depend on this feature
320
+ dependents = []
321
+
322
+ all_features.each do |other_feature|
323
+ dependents << other_feature[:name] if other_feature[:dependencies]&.include?(feature[:name])
324
+ end
325
+
326
+ dependents
327
+ end
328
+
329
+ def calculate_coupling_score(feature)
330
+ # Calculate coupling based on dependencies and dependents
331
+ dependency_count = feature[:dependencies]&.count || 0
332
+ dependent_count = feature[:dependents]&.count || 0
333
+
334
+ coupling = (dependency_count + dependent_count) / 10.0
335
+ coupling.clamp(0, 1).round(2)
336
+ end
337
+
338
+ def calculate_priority(feature)
339
+ # Calculate priority based on business value and complexity
340
+ business_value = feature[:business_value] || 0.5
341
+ complexity = feature[:complexity] || 0.5
342
+
343
+ priority = (business_value * 0.7 + complexity * 0.3)
344
+ priority.clamp(0, 1).round(2)
345
+ end
346
+
347
+ def estimate_business_value(feature)
348
+ # Simple business value estimation based on category and name
349
+ base_value = case feature[:category]
350
+ when "core_business"
351
+ 0.9
352
+ when "api"
353
+ 0.8
354
+ when "data"
355
+ 0.7
356
+ when "ui"
357
+ 0.6
358
+ when "utility"
359
+ 0.3
360
+ else
361
+ 0.5
362
+ end
363
+
364
+ # Adjust based on feature name indicators
365
+ if feature[:name]&.match?(/user|auth|payment|order/i)
366
+ base_value += 0.1
367
+ elsif feature[:name]&.match?(/util|helper|tool/i)
368
+ base_value -= 0.1
369
+ end
370
+
371
+ base_value.clamp(0, 1).round(2)
372
+ end
373
+
374
+ def estimate_technical_debt(feature)
375
+ # Simple technical debt estimation based on complexity and coupling
376
+ complexity = feature[:complexity] || 0.5
377
+ coupling = feature[:coupling] || 0.5
378
+
379
+ technical_debt = (complexity * 0.6 + coupling * 0.4)
380
+ technical_debt.clamp(0, 1).round(2)
381
+ end
382
+
383
+ def extract_feature_name(file, content, indicator)
384
+ # Extract feature name from file content
385
+ if content.match?(/class\s+(\w+)/)
386
+ ::Regexp.last_match(1)
387
+ elsif content.match?(/module\s+(\w+)/)
388
+ ::Regexp.last_match(1)
389
+ else
390
+ File.basename(file, ".*").capitalize
391
+ end
392
+ rescue
393
+ # Fallback to filename if extraction fails
394
+ File.basename(file, ".*").capitalize
395
+ end
396
+ end
397
+ end