aidp 0.5.0 → 0.8.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +128 -151
  3. data/bin/aidp +1 -1
  4. data/lib/aidp/analysis/kb_inspector.rb +471 -0
  5. data/lib/aidp/analysis/seams.rb +159 -0
  6. data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
  7. data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
  8. data/lib/aidp/analyze/error_handler.rb +2 -78
  9. data/lib/aidp/analyze/json_file_storage.rb +292 -0
  10. data/lib/aidp/analyze/progress.rb +12 -0
  11. data/lib/aidp/analyze/progress_visualizer.rb +12 -17
  12. data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
  13. data/lib/aidp/analyze/runner.rb +256 -87
  14. data/lib/aidp/analyze/steps.rb +6 -0
  15. data/lib/aidp/cli/jobs_command.rb +103 -435
  16. data/lib/aidp/cli.rb +317 -191
  17. data/lib/aidp/config.rb +298 -10
  18. data/lib/aidp/debug_logger.rb +195 -0
  19. data/lib/aidp/debug_mixin.rb +187 -0
  20. data/lib/aidp/execute/progress.rb +9 -0
  21. data/lib/aidp/execute/runner.rb +221 -40
  22. data/lib/aidp/execute/steps.rb +17 -7
  23. data/lib/aidp/execute/workflow_selector.rb +211 -0
  24. data/lib/aidp/harness/completion_checker.rb +268 -0
  25. data/lib/aidp/harness/condition_detector.rb +1526 -0
  26. data/lib/aidp/harness/config_loader.rb +373 -0
  27. data/lib/aidp/harness/config_manager.rb +382 -0
  28. data/lib/aidp/harness/config_schema.rb +1006 -0
  29. data/lib/aidp/harness/config_validator.rb +355 -0
  30. data/lib/aidp/harness/configuration.rb +477 -0
  31. data/lib/aidp/harness/enhanced_runner.rb +494 -0
  32. data/lib/aidp/harness/error_handler.rb +616 -0
  33. data/lib/aidp/harness/provider_config.rb +423 -0
  34. data/lib/aidp/harness/provider_factory.rb +306 -0
  35. data/lib/aidp/harness/provider_manager.rb +1269 -0
  36. data/lib/aidp/harness/provider_type_checker.rb +88 -0
  37. data/lib/aidp/harness/runner.rb +411 -0
  38. data/lib/aidp/harness/state/errors.rb +28 -0
  39. data/lib/aidp/harness/state/metrics.rb +219 -0
  40. data/lib/aidp/harness/state/persistence.rb +128 -0
  41. data/lib/aidp/harness/state/provider_state.rb +132 -0
  42. data/lib/aidp/harness/state/ui_state.rb +68 -0
  43. data/lib/aidp/harness/state/workflow_state.rb +123 -0
  44. data/lib/aidp/harness/state_manager.rb +586 -0
  45. data/lib/aidp/harness/status_display.rb +888 -0
  46. data/lib/aidp/harness/ui/base.rb +16 -0
  47. data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
  48. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
  49. data/lib/aidp/harness/ui/error_handler.rb +132 -0
  50. data/lib/aidp/harness/ui/frame_manager.rb +361 -0
  51. data/lib/aidp/harness/ui/job_monitor.rb +500 -0
  52. data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
  53. data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
  54. data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
  55. data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
  56. data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
  57. data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
  58. data/lib/aidp/harness/ui/progress_display.rb +280 -0
  59. data/lib/aidp/harness/ui/question_collector.rb +141 -0
  60. data/lib/aidp/harness/ui/spinner_group.rb +184 -0
  61. data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
  62. data/lib/aidp/harness/ui/status_manager.rb +312 -0
  63. data/lib/aidp/harness/ui/status_widget.rb +280 -0
  64. data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
  65. data/lib/aidp/harness/user_interface.rb +2381 -0
  66. data/lib/aidp/provider_manager.rb +131 -7
  67. data/lib/aidp/providers/anthropic.rb +28 -109
  68. data/lib/aidp/providers/base.rb +170 -0
  69. data/lib/aidp/providers/cursor.rb +52 -183
  70. data/lib/aidp/providers/gemini.rb +24 -109
  71. data/lib/aidp/providers/macos_ui.rb +99 -5
  72. data/lib/aidp/providers/opencode.rb +194 -0
  73. data/lib/aidp/storage/csv_storage.rb +172 -0
  74. data/lib/aidp/storage/file_manager.rb +214 -0
  75. data/lib/aidp/storage/json_storage.rb +140 -0
  76. data/lib/aidp/version.rb +1 -1
  77. data/lib/aidp.rb +56 -35
  78. data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
  79. data/templates/COMMON/AGENT_BASE.md +11 -0
  80. data/templates/EXECUTE/00_PRD.md +4 -4
  81. data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
  82. data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
  83. data/templates/EXECUTE/08_TASKS.md +4 -4
  84. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
  85. data/templates/README.md +279 -0
  86. data/templates/aidp-development.yml.example +373 -0
  87. data/templates/aidp-minimal.yml.example +48 -0
  88. data/templates/aidp-production.yml.example +475 -0
  89. data/templates/aidp.yml.example +598 -0
  90. metadata +106 -64
  91. data/lib/aidp/analyze/agent_personas.rb +0 -71
  92. data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
  93. data/lib/aidp/analyze/data_retention_manager.rb +0 -426
  94. data/lib/aidp/analyze/database.rb +0 -260
  95. data/lib/aidp/analyze/dependencies.rb +0 -335
  96. data/lib/aidp/analyze/export_manager.rb +0 -425
  97. data/lib/aidp/analyze/focus_guidance.rb +0 -517
  98. data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
  99. data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
  100. data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
  101. data/lib/aidp/analyze/memory_manager.rb +0 -365
  102. data/lib/aidp/analyze/metrics_storage.rb +0 -336
  103. data/lib/aidp/analyze/parallel_processor.rb +0 -460
  104. data/lib/aidp/analyze/performance_optimizer.rb +0 -694
  105. data/lib/aidp/analyze/repository_chunker.rb +0 -704
  106. data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
  107. data/lib/aidp/analyze/storage.rb +0 -662
  108. data/lib/aidp/analyze/tool_configuration.rb +0 -456
  109. data/lib/aidp/analyze/tool_modernization.rb +0 -750
  110. data/lib/aidp/database/pg_adapter.rb +0 -148
  111. data/lib/aidp/database_config.rb +0 -69
  112. data/lib/aidp/database_connection.rb +0 -72
  113. data/lib/aidp/database_migration.rb +0 -158
  114. data/lib/aidp/job_manager.rb +0 -41
  115. data/lib/aidp/jobs/base_job.rb +0 -47
  116. data/lib/aidp/jobs/provider_execution_job.rb +0 -96
  117. data/lib/aidp/project_detector.rb +0 -117
  118. data/lib/aidp/providers/agent_supervisor.rb +0 -348
  119. data/lib/aidp/providers/supervised_base.rb +0 -317
  120. data/lib/aidp/providers/supervised_cursor.rb +0 -22
  121. data/lib/aidp/sync.rb +0 -13
  122. data/lib/aidp/workspace.rb +0 -19
@@ -1,543 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "yaml"
5
- require_relative "storage"
6
-
7
- module Aidp
8
- module Analyze
9
- class IncrementalAnalyzer
10
- # Analysis granularity levels
11
- GRANULARITY_LEVELS = %w[file directory component feature module].freeze
12
-
13
- def initialize(project_dir = Dir.pwd, config = {})
14
- @project_dir = project_dir
15
- @config = config
16
- @storage = config[:storage] || Aidp::AnalysisStorage.new(project_dir)
17
- @progress_file = config[:progress_file] || File.join(project_dir, ".aidp-incremental-progress.yml")
18
- end
19
-
20
- # Perform incremental analysis
21
- def analyze_incrementally(analysis_type, options = {})
22
- granularity = options[:granularity] || "file"
23
- force_full = options[:force_full] || false
24
-
25
- # Load previous analysis state
26
- previous_state = load_analysis_state(analysis_type)
27
-
28
- # Determine what needs to be analyzed
29
- analysis_plan = if force_full
30
- create_full_analysis_plan(analysis_type, granularity)
31
- else
32
- create_incremental_analysis_plan(analysis_type, granularity, previous_state)
33
- end
34
-
35
- # Execute analysis plan
36
- results = execute_analysis_plan(analysis_plan, options)
37
-
38
- # Update analysis state
39
- new_state = update_analysis_state(analysis_type, previous_state, results)
40
- save_analysis_state(analysis_type, new_state)
41
-
42
- {
43
- analysis_type: analysis_type,
44
- granularity: granularity,
45
- plan: analysis_plan,
46
- results: results,
47
- state: new_state,
48
- incremental: !force_full
49
- }
50
- end
51
-
52
- # Analyze specific components incrementally
53
- def analyze_components_incrementally(components, analysis_type, options = {})
54
- results = {}
55
-
56
- components.each do |component|
57
- component_result = analyze_component_incrementally(component, analysis_type, options)
58
- results[component] = component_result
59
- end
60
-
61
- {
62
- components: components,
63
- analysis_type: analysis_type,
64
- results: results,
65
- summary: generate_component_summary(results)
66
- }
67
- end
68
-
69
- # Analyze files incrementally
70
- def analyze_files_incrementally(files, analysis_type, options = {})
71
- results = {}
72
-
73
- files.each do |file|
74
- file_result = analyze_file_incrementally(file, analysis_type, options)
75
- results[file] = file_result
76
- end
77
-
78
- {
79
- files: files,
80
- analysis_type: analysis_type,
81
- results: results,
82
- summary: generate_file_summary(results)
83
- }
84
- end
85
-
86
- # Get incremental analysis status
87
- def get_incremental_status(analysis_type)
88
- state = load_analysis_state(analysis_type)
89
-
90
- {
91
- analysis_type: analysis_type,
92
- last_analysis: state[:last_analysis],
93
- total_components: state[:total_components] || 0,
94
- analyzed_components: state[:analyzed_components] || 0,
95
- pending_components: state[:pending_components] || 0,
96
- analysis_coverage: calculate_coverage(state),
97
- last_modified: state[:last_modified]
98
- }
99
- end
100
-
101
- # Get analysis recommendations
102
- def get_analysis_recommendations(analysis_type)
103
- state = load_analysis_state(analysis_type)
104
- recommendations = []
105
-
106
- # Check for outdated analysis
107
- if state[:last_analysis] && (Time.now - state[:last_analysis]) > 24 * 60 * 60 # 24 hours
108
- recommendations << {
109
- type: "outdated_analysis",
110
- priority: "medium",
111
- message: "Analysis is more than 24 hours old",
112
- action: "Consider running full analysis"
113
- }
114
- end
115
-
116
- # Check for low coverage
117
- coverage = calculate_coverage(state)
118
- if coverage < 0.8
119
- recommendations << {
120
- type: "low_coverage",
121
- priority: "high",
122
- message: "Analysis coverage is #{coverage * 100}%",
123
- action: "Run incremental analysis to improve coverage"
124
- }
125
- end
126
-
127
- # Check for pending components
128
- if state[:pending_components] && state[:pending_components] > 0
129
- recommendations << {
130
- type: "pending_components",
131
- priority: "medium",
132
- message: "#{state[:pending_components]} components pending analysis",
133
- action: "Run incremental analysis for pending components"
134
- }
135
- end
136
-
137
- recommendations
138
- end
139
-
140
- # Reset incremental analysis state
141
- def reset_incremental_state(analysis_type)
142
- state_file = get_state_file_path(analysis_type)
143
- File.delete(state_file) if File.exist?(state_file)
144
-
145
- {
146
- analysis_type: analysis_type,
147
- reset: true,
148
- timestamp: Time.now
149
- }
150
- end
151
-
152
- # Get analysis history
153
- def get_analysis_history(analysis_type, limit = 10)
154
- state = load_analysis_state(analysis_type)
155
- history = state[:history] || []
156
-
157
- history.last(limit).map do |entry|
158
- {
159
- timestamp: entry[:timestamp],
160
- type: entry[:type],
161
- components_analyzed: entry[:components_analyzed],
162
- duration: entry[:duration],
163
- coverage: entry[:coverage]
164
- }
165
- end
166
- end
167
-
168
- private
169
-
170
- def load_analysis_state(analysis_type)
171
- state_file = get_state_file_path(analysis_type)
172
- return create_initial_state(analysis_type) unless File.exist?(state_file)
173
-
174
- begin
175
- YAML.load_file(state_file) || create_initial_state(analysis_type)
176
- rescue => e
177
- puts "Warning: Could not load analysis state: #{e.message}"
178
- create_initial_state(analysis_type)
179
- end
180
- end
181
-
182
- def save_analysis_state(analysis_type, state)
183
- state_file = get_state_file_path(analysis_type)
184
- state[:last_modified] = Time.now
185
-
186
- File.write(state_file, YAML.dump(state))
187
- end
188
-
189
- def get_state_file_path(analysis_type)
190
- File.join(@project_dir, ".aidp-#{analysis_type}-state.yml")
191
- end
192
-
193
- def create_initial_state(analysis_type)
194
- {
195
- analysis_type: analysis_type,
196
- created_at: Time.now,
197
- last_analysis: nil,
198
- total_components: 0,
199
- analyzed_components: 0,
200
- pending_components: 0,
201
- components: {},
202
- history: []
203
- }
204
- end
205
-
206
- def create_full_analysis_plan(analysis_type, granularity)
207
- components = discover_components(granularity)
208
-
209
- {
210
- type: "full",
211
- granularity: granularity,
212
- components: components,
213
- total_components: components.length,
214
- estimated_duration: estimate_analysis_duration(components, analysis_type)
215
- }
216
- end
217
-
218
- def create_incremental_analysis_plan(analysis_type, granularity, previous_state)
219
- all_components = discover_components(granularity)
220
- analyzed_components = previous_state[:components] || {}
221
-
222
- # Identify components that need analysis
223
- components_to_analyze = []
224
-
225
- all_components.each do |component|
226
- component_info = analyzed_components[component]
227
-
228
- if !component_info || needs_reanalysis?(component, component_info, analysis_type)
229
- components_to_analyze << component
230
- end
231
- end
232
-
233
- {
234
- type: "incremental",
235
- granularity: granularity,
236
- components: components_to_analyze,
237
- total_components: all_components.length,
238
- components_to_analyze: components_to_analyze.length,
239
- estimated_duration: estimate_analysis_duration(components_to_analyze, analysis_type)
240
- }
241
- end
242
-
243
- def discover_components(granularity)
244
- case granularity
245
- when "file"
246
- discover_files
247
- when "directory"
248
- discover_directories
249
- when "component"
250
- discover_components_by_structure
251
- when "feature"
252
- discover_features
253
- when "module"
254
- discover_modules
255
- else
256
- discover_files # Default to file-level
257
- end
258
- end
259
-
260
- def discover_files
261
- files = []
262
-
263
- Dir.glob(File.join(@project_dir, "**", "*.rb")).each do |file|
264
- relative_path = file.sub(@project_dir + "/", "")
265
- files << relative_path unless relative_path.start_with?(".")
266
- end
267
-
268
- files
269
- end
270
-
271
- def discover_directories
272
- directories = []
273
-
274
- Dir.glob(File.join(@project_dir, "**", "*/")).each do |dir|
275
- relative_path = dir.sub(@project_dir + "/", "").chomp("/")
276
- directories << relative_path unless relative_path.start_with?(".")
277
- end
278
-
279
- directories
280
- end
281
-
282
- def discover_components_by_structure
283
- components = []
284
-
285
- # Look for common component patterns
286
- component_patterns = [
287
- "app/models/**/*.rb",
288
- "app/controllers/**/*.rb",
289
- "app/services/**/*.rb",
290
- "lib/**/*.rb",
291
- "spec/**/*.rb"
292
- ]
293
-
294
- component_patterns.each do |pattern|
295
- Dir.glob(File.join(@project_dir, pattern)).each do |file|
296
- relative_path = file.sub(@project_dir + "/", "")
297
- components << relative_path
298
- end
299
- end
300
-
301
- components
302
- end
303
-
304
- def discover_features
305
- features = []
306
-
307
- # Look for feature directories
308
- feature_dirs = [
309
- "app/features",
310
- "features",
311
- "app/views",
312
- "app/controllers"
313
- ]
314
-
315
- feature_dirs.each do |dir|
316
- full_path = File.join(@project_dir, dir)
317
- next unless Dir.exist?(full_path)
318
-
319
- Dir.entries(full_path).each do |entry|
320
- next if entry.start_with?(".")
321
-
322
- feature_path = File.join(dir, entry)
323
- features << feature_path if Dir.exist?(File.join(@project_dir, feature_path))
324
- end
325
- end
326
-
327
- features
328
- end
329
-
330
- def discover_modules
331
- modules = []
332
-
333
- # Look for module files
334
- Dir.glob(File.join(@project_dir, "**", "*.rb")).each do |file|
335
- content = File.read(file)
336
- if content.include?("module ") || content.include?("class ")
337
- relative_path = file.sub(@project_dir + "/", "")
338
- modules << relative_path
339
- end
340
- end
341
-
342
- modules
343
- end
344
-
345
- def needs_reanalysis?(component, component_info, analysis_type)
346
- return true unless component_info[:last_analysis]
347
-
348
- # Check if component has been modified since last analysis
349
- component_path = File.join(@project_dir, component)
350
- return true unless File.exist?(component_path)
351
-
352
- last_modified = File.mtime(component_path)
353
- last_analysis = component_info[:last_analysis]
354
-
355
- # Reanalyze if component was modified after last analysis
356
- last_modified > last_analysis
357
- end
358
-
359
- def execute_analysis_plan(plan, options)
360
- results = {
361
- plan: plan,
362
- start_time: Time.now,
363
- results: {},
364
- errors: []
365
- }
366
-
367
- plan[:components].each do |component|
368
- component_result = analyze_component(component, plan[:analysis_type], options)
369
- results[:results][component] = component_result
370
- rescue => e
371
- results[:errors] << {
372
- component: component,
373
- error: e.message
374
- }
375
- end
376
-
377
- results[:end_time] = Time.now
378
- results[:duration] = results[:end_time] - results[:start_time]
379
-
380
- results
381
- end
382
-
383
- def analyze_component(component, analysis_type, options)
384
- # This is a placeholder for actual analysis logic
385
- # In a real implementation, this would call the appropriate analysis tools
386
-
387
- {
388
- component: component,
389
- analysis_type: analysis_type,
390
- analyzed_at: Time.now,
391
- status: "completed",
392
- metrics: generate_component_metrics(component, analysis_type),
393
- findings: generate_component_findings(component, analysis_type)
394
- }
395
- end
396
-
397
- def analyze_component_incrementally(component, analysis_type, options)
398
- component_info = get_component_info(component)
399
-
400
- if needs_reanalysis?(component, component_info, analysis_type)
401
- result = analyze_component(component, analysis_type, options)
402
- update_component_info(component, result)
403
- result
404
- else
405
- {
406
- component: component,
407
- analysis_type: analysis_type,
408
- status: "skipped",
409
- reason: "No changes detected",
410
- last_analysis: component_info[:last_analysis]
411
- }
412
- end
413
- end
414
-
415
- def analyze_file_incrementally(file, analysis_type, options)
416
- file_info = get_file_info(file)
417
-
418
- if needs_reanalysis?(file, file_info, analysis_type)
419
- result = analyze_component(file, analysis_type, options)
420
- update_file_info(file, result)
421
- result
422
- else
423
- {
424
- file: file,
425
- analysis_type: analysis_type,
426
- status: "skipped",
427
- reason: "No changes detected",
428
- last_analysis: file_info[:last_analysis]
429
- }
430
- end
431
- end
432
-
433
- def update_analysis_state(analysis_type, previous_state, results)
434
- new_state = previous_state.dup
435
- new_state[:last_analysis] = Time.now
436
- new_state[:total_components] = results[:plan][:total_components]
437
-
438
- # Update component information
439
- results[:results].each do |component, result|
440
- new_state[:components][component] = {
441
- last_analysis: Time.now,
442
- analysis_type: analysis_type,
443
- status: result[:status],
444
- metrics: result[:metrics]
445
- }
446
- end
447
-
448
- new_state[:analyzed_components] = new_state[:components].length
449
- new_state[:pending_components] = new_state[:total_components] - new_state[:analyzed_components]
450
-
451
- # Add to history
452
- new_state[:history] ||= []
453
- new_state[:history] << {
454
- timestamp: Time.now,
455
- type: results[:plan][:type],
456
- components_analyzed: results[:results].length,
457
- duration: results[:duration],
458
- coverage: calculate_coverage(new_state)
459
- }
460
-
461
- new_state
462
- end
463
-
464
- def calculate_coverage(state)
465
- return 0.0 if state[:total_components] == 0
466
-
467
- state[:analyzed_components].to_f / state[:total_components]
468
- end
469
-
470
- def estimate_analysis_duration(components, analysis_type)
471
- # Simple estimation based on component count
472
- base_duration_per_component = case analysis_type
473
- when "static_analysis"
474
- 30 # seconds
475
- when "security_analysis"
476
- 60 # seconds
477
- when "performance_analysis"
478
- 45 # seconds
479
- else
480
- 30 # seconds
481
- end
482
-
483
- components.length * base_duration_per_component
484
- end
485
-
486
- def generate_component_metrics(component, analysis_type)
487
- # Placeholder for actual metrics generation
488
- {
489
- complexity: rand(1..10),
490
- lines_of_code: rand(10..500),
491
- maintainability_index: rand(50..100)
492
- }
493
- end
494
-
495
- def generate_component_findings(component, analysis_type)
496
- # Placeholder for actual findings generation
497
- []
498
- end
499
-
500
- def get_component_info(component)
501
- state = load_analysis_state("general")
502
- state[:components][component] || {}
503
- end
504
-
505
- def update_component_info(component, result)
506
- state = load_analysis_state("general")
507
- state[:components][component] = {
508
- last_analysis: Time.now,
509
- status: result[:status],
510
- metrics: result[:metrics]
511
- }
512
- save_analysis_state("general", state)
513
- end
514
-
515
- def get_file_info(file)
516
- get_component_info(file)
517
- end
518
-
519
- def update_file_info(file, result)
520
- update_component_info(file, result)
521
- end
522
-
523
- def generate_component_summary(results)
524
- total_components = results.length
525
- analyzed_components = results.count { |_, result| result[:status] == "completed" }
526
- skipped_components = results.count { |_, result| result[:status] == "skipped" }
527
- failed_components = results.count { |_, result| result[:status] == "failed" }
528
-
529
- {
530
- total_components: total_components,
531
- analyzed_components: analyzed_components,
532
- skipped_components: skipped_components,
533
- failed_components: failed_components,
534
- success_rate: (total_components > 0) ? (analyzed_components.to_f / total_components * 100).round(2) : 0
535
- }
536
- end
537
-
538
- def generate_file_summary(results)
539
- generate_component_summary(results)
540
- end
541
- end
542
- end
543
- end