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
@@ -0,0 +1,480 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tree_sitter"
4
+ require "fileutils"
5
+
6
+ module Aidp
7
+ module Analysis
8
+ class TreeSitterGrammarLoader
9
+ # Default grammar configurations
10
+ GRAMMAR_CONFIGS = {
11
+ "ruby" => {
12
+ name: "tree-sitter-ruby",
13
+ version: "0.20.0",
14
+ source: "https://github.com/tree-sitter/tree-sitter-ruby",
15
+ file_patterns: ["**/*.rb"],
16
+ node_types: {
17
+ class: "class",
18
+ module: "module",
19
+ method: "method",
20
+ call: "call",
21
+ require: "call",
22
+ require_relative: "call"
23
+ }
24
+ },
25
+ "javascript" => {
26
+ name: "tree-sitter-javascript",
27
+ version: "0.20.0",
28
+ source: "https://github.com/tree-sitter/tree-sitter-javascript",
29
+ file_patterns: ["**/*.js", "**/*.jsx"],
30
+ node_types: {
31
+ class: "class_declaration",
32
+ function: "function_declaration",
33
+ call: "call_expression",
34
+ import: "import_statement"
35
+ }
36
+ },
37
+ "typescript" => {
38
+ name: "tree-sitter-typescript",
39
+ version: "0.20.0",
40
+ source: "https://github.com/tree-sitter/tree-sitter-typescript",
41
+ file_patterns: ["**/*.ts", "**/*.tsx"],
42
+ node_types: {
43
+ class: "class_declaration",
44
+ function: "function_declaration",
45
+ call: "call_expression",
46
+ import: "import_statement"
47
+ }
48
+ },
49
+ "python" => {
50
+ name: "tree-sitter-python",
51
+ version: "0.20.0",
52
+ source: "https://github.com/tree-sitter/tree-sitter-python",
53
+ file_patterns: ["**/*.py"],
54
+ node_types: {
55
+ class: "class_definition",
56
+ function: "function_definition",
57
+ call: "call",
58
+ import: "import_statement"
59
+ }
60
+ }
61
+ }.freeze
62
+
63
+ def initialize(project_dir = Dir.pwd)
64
+ @project_dir = project_dir
65
+ @grammars_dir = File.join(project_dir, ".aidp", "grammars")
66
+ @loaded_grammars = {}
67
+ end
68
+
69
+ # Load grammar for a specific language
70
+ def load_grammar(language)
71
+ return @loaded_grammars[language] if @loaded_grammars[language]
72
+
73
+ config = GRAMMAR_CONFIGS[language]
74
+ raise "Unsupported language: #{language}" unless config
75
+
76
+ ensure_grammar_available(language, config)
77
+ @loaded_grammars[language] = create_parser(language, config)
78
+ end
79
+
80
+ # Get file patterns for a language
81
+ def file_patterns_for_language(language)
82
+ config = GRAMMAR_CONFIGS[language]
83
+ return [] unless config
84
+
85
+ config[:file_patterns]
86
+ end
87
+
88
+ private
89
+
90
+ def ensure_grammar_available(language, config)
91
+ grammar_path = File.join(@grammars_dir, language)
92
+
93
+ unless File.exist?(grammar_path)
94
+ puts "Installing Tree-sitter grammar for #{language}..."
95
+ install_grammar(language, config)
96
+ end
97
+ end
98
+
99
+ def install_grammar(language, config)
100
+ FileUtils.mkdir_p(@grammars_dir)
101
+
102
+ # For now, we'll use the system-installed grammars
103
+ # In a production setup, you might want to download and compile grammars
104
+ grammar_path = File.join(@grammars_dir, language)
105
+
106
+ # Create a placeholder file to indicate the grammar is "installed"
107
+ # The actual grammar loading will be handled by tree_sitter
108
+ FileUtils.mkdir_p(grammar_path)
109
+ require "json"
110
+ File.write(File.join(grammar_path, "grammar.json"), JSON.generate(config))
111
+
112
+ puts "Grammar for #{language} marked as available"
113
+ end
114
+
115
+ def create_parser(language, config)
116
+ # Create a Tree-sitter parser for the language
117
+ # This is a simplified version - in practice you'd need to handle
118
+ # the actual grammar loading from the ruby_tree_sitter gem
119
+
120
+ {
121
+ language: language,
122
+ config: config,
123
+ parser: create_tree_sitter_parser(language)
124
+ }
125
+ end
126
+
127
+ def create_tree_sitter_parser(language)
128
+ create_real_parser(language)
129
+ end
130
+
131
+ def create_real_parser(language)
132
+ parser = TreeSitter::Parser.new
133
+ language_obj = TreeSitter.lang(language)
134
+ parser.language = language_obj
135
+
136
+ {
137
+ parse: ->(source_code) { parse_with_tree_sitter(parser, source_code) },
138
+ language: language,
139
+ real: true
140
+ }
141
+ rescue TreeSitter::ParserNotFoundError => e
142
+ puts "Warning: Tree-sitter parser not found for #{language}: #{e.message}"
143
+ create_mock_parser(language)
144
+ end
145
+
146
+ def create_mock_parser(language)
147
+ case language
148
+ when "ruby"
149
+ create_ruby_parser
150
+ when "javascript"
151
+ create_javascript_parser
152
+ when "typescript"
153
+ create_typescript_parser
154
+ when "python"
155
+ create_python_parser
156
+ else
157
+ raise "Unsupported language: #{language}"
158
+ end
159
+ end
160
+
161
+ def parse_with_tree_sitter(parser, source_code)
162
+ tree = parser.parse_string(nil, source_code)
163
+ root = tree.root_node
164
+
165
+ # Convert Tree-sitter AST to our internal format
166
+ convert_tree_sitter_ast(root, source_code)
167
+ end
168
+
169
+ def convert_tree_sitter_ast(node, source_code)
170
+ children = []
171
+
172
+ node.each do |child|
173
+ child_data = {
174
+ type: child.type,
175
+ name: extract_node_name(child, source_code),
176
+ line: child.start_point.row + 1,
177
+ start_column: child.start_point.column,
178
+ end_column: child.end_point.column
179
+ }
180
+
181
+ # Recursively process child nodes
182
+ if child.child_count > 0
183
+ child_data[:children] = convert_tree_sitter_ast(child, source_code)
184
+ end
185
+
186
+ children << child_data
187
+ end
188
+
189
+ {
190
+ type: node.type,
191
+ children: children
192
+ }
193
+ end
194
+
195
+ def extract_node_name(node, source_code)
196
+ # Extract meaningful names from nodes
197
+ case node.type.to_s
198
+ when "class", "module"
199
+ # Look for the class/module name in the source, handling nested constants
200
+ lines = source_code.lines
201
+ line_content = lines[node.start_point.row] || ""
202
+ # Handle patterns like: class Foo, class Foo::Bar, module A::B::C
203
+ if (match = line_content.match(/(?:class|module)\s+((?:\w+::)*\w+)/))
204
+ match[1]
205
+ else
206
+ node.type.to_s
207
+ end
208
+ when "method"
209
+ # Look for method name, handling various definition styles
210
+ lines = source_code.lines
211
+ line_content = lines[node.start_point.row] || ""
212
+ # Handle: def foo, def foo(args), def foo=(value), def []=(key, value)
213
+ if (match = line_content.match(/def\s+([\w\[\]=!?]+)/))
214
+ match[1]
215
+ else
216
+ node.type.to_s
217
+ end
218
+ when "singleton_method"
219
+ # Look for singleton method name, handling class methods
220
+ lines = source_code.lines
221
+ line_content = lines[node.start_point.row] || ""
222
+ # Handle: def self.foo, def ClassName.foo, def obj.method_name
223
+ if (match = line_content.match(/def\s+(?:self|[\w:]+)\.([\w\[\]=!?]+)/))
224
+ match[1]
225
+ else
226
+ node.type.to_s
227
+ end
228
+ when "constant"
229
+ # Extract constant names
230
+ lines = source_code.lines
231
+ line_content = lines[node.start_point.row] || ""
232
+ # Handle: CONSTANT = value, A::B::CONSTANT = value
233
+ if (match = line_content.match(/((?:\w+::)*[A-Z_][A-Z0-9_]*)\s*=/))
234
+ match[1]
235
+ else
236
+ node.type.to_s
237
+ end
238
+ else
239
+ node.type.to_s
240
+ end
241
+ end
242
+
243
+ def create_ruby_parser
244
+ # Mock Ruby parser - fallback when Tree-sitter is not available
245
+ {
246
+ parse: ->(source_code) { parse_ruby_source(source_code) },
247
+ language: "ruby",
248
+ real: false
249
+ }
250
+ end
251
+
252
+ def create_javascript_parser
253
+ {
254
+ parse: ->(source_code) { parse_javascript_source(source_code) },
255
+ language: "javascript",
256
+ real: false
257
+ }
258
+ end
259
+
260
+ def create_typescript_parser
261
+ {
262
+ parse: ->(source_code) { parse_typescript_source(source_code) },
263
+ language: "typescript",
264
+ real: false
265
+ }
266
+ end
267
+
268
+ def create_python_parser
269
+ {
270
+ parse: ->(source_code) { parse_python_source(source_code) },
271
+ language: "python",
272
+ real: false
273
+ }
274
+ end
275
+
276
+ # Mock parsing methods - these would be replaced with actual Tree-sitter parsing
277
+ def parse_ruby_source(source_code)
278
+ # This would use the actual ruby_tree_sitter gem
279
+ # For now, return a mock AST structure
280
+ {
281
+ type: "program",
282
+ children: extract_ruby_nodes(source_code)
283
+ }
284
+ end
285
+
286
+ def parse_javascript_source(source_code)
287
+ {
288
+ type: "program",
289
+ children: extract_javascript_nodes(source_code)
290
+ }
291
+ end
292
+
293
+ def parse_typescript_source(source_code)
294
+ {
295
+ type: "program",
296
+ children: extract_typescript_nodes(source_code)
297
+ }
298
+ end
299
+
300
+ def parse_python_source(source_code)
301
+ {
302
+ type: "module",
303
+ children: extract_python_nodes(source_code)
304
+ }
305
+ end
306
+
307
+ # Simple regex-based extraction for demonstration
308
+ # In practice, these would be replaced with actual Tree-sitter node extraction
309
+ def extract_ruby_nodes(source_code)
310
+ nodes = []
311
+ lines = source_code.lines
312
+
313
+ lines.each_with_index do |line, index|
314
+ line_number = index + 1
315
+
316
+ # Extract class definitions (including nested constants)
317
+ if (match = line.match(/^\s*class\s+((?:\w+::)*\w+)/))
318
+ nodes << {
319
+ type: "class",
320
+ name: match[1],
321
+ line: line_number,
322
+ start_column: line.index(match[0]),
323
+ end_column: line.index(match[0]) + match[0].length
324
+ }
325
+ end
326
+
327
+ # Extract module definitions (including nested constants)
328
+ if (match = line.match(/^\s*module\s+((?:\w+::)*\w+)/))
329
+ nodes << {
330
+ type: "module",
331
+ name: match[1],
332
+ line: line_number,
333
+ start_column: line.index(match[0]),
334
+ end_column: line.index(match[0]) + match[0].length
335
+ }
336
+ end
337
+
338
+ # Extract method definitions (including special methods)
339
+ if (match = line.match(/^\s*def\s+([\w\[\]=!?]+)/))
340
+ nodes << {
341
+ type: "method",
342
+ name: match[1],
343
+ line: line_number,
344
+ start_column: line.index(match[0]),
345
+ end_column: line.index(match[0]) + match[0].length
346
+ }
347
+ end
348
+
349
+ # Extract singleton/class method definitions
350
+ if (match = line.match(/^\s*def\s+(?:self|[\w:]+)\.([\w\[\]=!?]+)/))
351
+ nodes << {
352
+ type: "singleton_method",
353
+ name: match[1],
354
+ line: line_number,
355
+ start_column: line.index(match[0]),
356
+ end_column: line.index(match[0]) + match[0].length
357
+ }
358
+ end
359
+
360
+ # Extract require statements
361
+ if (match = line.match(/^\s*require\s+['"]([^'"]+)['"]/))
362
+ nodes << {
363
+ type: "require",
364
+ target: match[1],
365
+ line: line_number,
366
+ start_column: line.index(match[0]),
367
+ end_column: line.index(match[0]) + match[0].length
368
+ }
369
+ end
370
+
371
+ # Extract require_relative statements
372
+ if (match = line.match(/^\s*require_relative\s+['"]([^'"]+)['"]/))
373
+ nodes << {
374
+ type: "require_relative",
375
+ target: match[1],
376
+ line: line_number,
377
+ start_column: line.index(match[0]),
378
+ end_column: line.index(match[0]) + match[0].length
379
+ }
380
+ end
381
+ end
382
+
383
+ nodes
384
+ end
385
+
386
+ def extract_javascript_nodes(source_code)
387
+ nodes = []
388
+ lines = source_code.lines
389
+
390
+ lines.each_with_index do |line, index|
391
+ line_number = index + 1
392
+
393
+ # Extract class declarations
394
+ if (match = line.match(/class\s+(\w+)/))
395
+ nodes << {
396
+ type: "class",
397
+ name: match[1],
398
+ line: line_number,
399
+ start_column: line.index(match[0]),
400
+ end_column: line.index(match[0]) + match[0].length
401
+ }
402
+ end
403
+
404
+ # Extract function declarations
405
+ if (match = line.match(/function\s+(\w+)/))
406
+ nodes << {
407
+ type: "function",
408
+ name: match[1],
409
+ line: line_number,
410
+ start_column: line.index(match[0]),
411
+ end_column: line.index(match[0]) + match[0].length
412
+ }
413
+ end
414
+
415
+ # Extract import statements
416
+ if (match = line.match(/import\s+.*from\s+['"]([^'"]+)['"]/))
417
+ nodes << {
418
+ type: "import",
419
+ target: match[1],
420
+ line: line_number,
421
+ start_column: line.index(match[0]),
422
+ end_column: line.index(match[0]) + match[0].length
423
+ }
424
+ end
425
+ end
426
+
427
+ nodes
428
+ end
429
+
430
+ def extract_typescript_nodes(source_code)
431
+ # Similar to JavaScript but with TypeScript-specific patterns
432
+ extract_javascript_nodes(source_code)
433
+ end
434
+
435
+ def extract_python_nodes(source_code)
436
+ nodes = []
437
+ lines = source_code.lines
438
+
439
+ lines.each_with_index do |line, index|
440
+ line_number = index + 1
441
+
442
+ # Extract class definitions
443
+ if (match = line.match(/class\s+(\w+)/))
444
+ nodes << {
445
+ type: "class",
446
+ name: match[1],
447
+ line: line_number,
448
+ start_column: line.index(match[0]),
449
+ end_column: line.index(match[0]) + match[0].length
450
+ }
451
+ end
452
+
453
+ # Extract function definitions
454
+ if (match = line.match(/def\s+(\w+)/))
455
+ nodes << {
456
+ type: "function",
457
+ name: match[1],
458
+ line: line_number,
459
+ start_column: line.index(match[0]),
460
+ end_column: line.index(match[0]) + match[0].length
461
+ }
462
+ end
463
+
464
+ # Extract import statements
465
+ if (match = line.match(/import\s+([^#\n]+)/))
466
+ nodes << {
467
+ type: "import",
468
+ target: match[1].strip,
469
+ line: line_number,
470
+ start_column: line.index(match[0]),
471
+ end_column: line.index(match[0]) + match[0].length
472
+ }
473
+ end
474
+ end
475
+
476
+ nodes
477
+ end
478
+ end
479
+ end
480
+ end