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.
- checksums.yaml +4 -4
- data/README.md +128 -151
- data/bin/aidp +1 -1
- data/lib/aidp/analysis/kb_inspector.rb +471 -0
- data/lib/aidp/analysis/seams.rb +159 -0
- data/lib/aidp/analysis/tree_sitter_grammar_loader.rb +480 -0
- data/lib/aidp/analysis/tree_sitter_scan.rb +686 -0
- data/lib/aidp/analyze/error_handler.rb +2 -78
- data/lib/aidp/analyze/json_file_storage.rb +292 -0
- data/lib/aidp/analyze/progress.rb +12 -0
- data/lib/aidp/analyze/progress_visualizer.rb +12 -17
- data/lib/aidp/analyze/ruby_maat_integration.rb +13 -31
- data/lib/aidp/analyze/runner.rb +256 -87
- data/lib/aidp/analyze/steps.rb +6 -0
- data/lib/aidp/cli/jobs_command.rb +103 -435
- data/lib/aidp/cli.rb +317 -191
- data/lib/aidp/config.rb +298 -10
- data/lib/aidp/debug_logger.rb +195 -0
- data/lib/aidp/debug_mixin.rb +187 -0
- data/lib/aidp/execute/progress.rb +9 -0
- data/lib/aidp/execute/runner.rb +221 -40
- data/lib/aidp/execute/steps.rb +17 -7
- data/lib/aidp/execute/workflow_selector.rb +211 -0
- data/lib/aidp/harness/completion_checker.rb +268 -0
- data/lib/aidp/harness/condition_detector.rb +1526 -0
- data/lib/aidp/harness/config_loader.rb +373 -0
- data/lib/aidp/harness/config_manager.rb +382 -0
- data/lib/aidp/harness/config_schema.rb +1006 -0
- data/lib/aidp/harness/config_validator.rb +355 -0
- data/lib/aidp/harness/configuration.rb +477 -0
- data/lib/aidp/harness/enhanced_runner.rb +494 -0
- data/lib/aidp/harness/error_handler.rb +616 -0
- data/lib/aidp/harness/provider_config.rb +423 -0
- data/lib/aidp/harness/provider_factory.rb +306 -0
- data/lib/aidp/harness/provider_manager.rb +1269 -0
- data/lib/aidp/harness/provider_type_checker.rb +88 -0
- data/lib/aidp/harness/runner.rb +411 -0
- data/lib/aidp/harness/state/errors.rb +28 -0
- data/lib/aidp/harness/state/metrics.rb +219 -0
- data/lib/aidp/harness/state/persistence.rb +128 -0
- data/lib/aidp/harness/state/provider_state.rb +132 -0
- data/lib/aidp/harness/state/ui_state.rb +68 -0
- data/lib/aidp/harness/state/workflow_state.rb +123 -0
- data/lib/aidp/harness/state_manager.rb +586 -0
- data/lib/aidp/harness/status_display.rb +888 -0
- data/lib/aidp/harness/ui/base.rb +16 -0
- data/lib/aidp/harness/ui/enhanced_tui.rb +545 -0
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +252 -0
- data/lib/aidp/harness/ui/error_handler.rb +132 -0
- data/lib/aidp/harness/ui/frame_manager.rb +361 -0
- data/lib/aidp/harness/ui/job_monitor.rb +500 -0
- data/lib/aidp/harness/ui/navigation/main_menu.rb +311 -0
- data/lib/aidp/harness/ui/navigation/menu_formatter.rb +120 -0
- data/lib/aidp/harness/ui/navigation/menu_item.rb +142 -0
- data/lib/aidp/harness/ui/navigation/menu_state.rb +139 -0
- data/lib/aidp/harness/ui/navigation/submenu.rb +202 -0
- data/lib/aidp/harness/ui/navigation/workflow_selector.rb +176 -0
- data/lib/aidp/harness/ui/progress_display.rb +280 -0
- data/lib/aidp/harness/ui/question_collector.rb +141 -0
- data/lib/aidp/harness/ui/spinner_group.rb +184 -0
- data/lib/aidp/harness/ui/spinner_helper.rb +152 -0
- data/lib/aidp/harness/ui/status_manager.rb +312 -0
- data/lib/aidp/harness/ui/status_widget.rb +280 -0
- data/lib/aidp/harness/ui/workflow_controller.rb +312 -0
- data/lib/aidp/harness/user_interface.rb +2381 -0
- data/lib/aidp/provider_manager.rb +131 -7
- data/lib/aidp/providers/anthropic.rb +28 -109
- data/lib/aidp/providers/base.rb +170 -0
- data/lib/aidp/providers/cursor.rb +52 -183
- data/lib/aidp/providers/gemini.rb +24 -109
- data/lib/aidp/providers/macos_ui.rb +99 -5
- data/lib/aidp/providers/opencode.rb +194 -0
- data/lib/aidp/storage/csv_storage.rb +172 -0
- data/lib/aidp/storage/file_manager.rb +214 -0
- data/lib/aidp/storage/json_storage.rb +140 -0
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp.rb +56 -35
- data/templates/ANALYZE/06a_tree_sitter_scan.md +217 -0
- data/templates/COMMON/AGENT_BASE.md +11 -0
- data/templates/EXECUTE/00_PRD.md +4 -4
- data/templates/EXECUTE/02_ARCHITECTURE.md +5 -4
- data/templates/EXECUTE/07_TEST_PLAN.md +4 -1
- data/templates/EXECUTE/08_TASKS.md +4 -4
- data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +4 -4
- data/templates/README.md +279 -0
- data/templates/aidp-development.yml.example +373 -0
- data/templates/aidp-minimal.yml.example +48 -0
- data/templates/aidp-production.yml.example +475 -0
- data/templates/aidp.yml.example +598 -0
- metadata +106 -64
- data/lib/aidp/analyze/agent_personas.rb +0 -71
- data/lib/aidp/analyze/agent_tool_executor.rb +0 -445
- data/lib/aidp/analyze/data_retention_manager.rb +0 -426
- data/lib/aidp/analyze/database.rb +0 -260
- data/lib/aidp/analyze/dependencies.rb +0 -335
- data/lib/aidp/analyze/export_manager.rb +0 -425
- data/lib/aidp/analyze/focus_guidance.rb +0 -517
- data/lib/aidp/analyze/incremental_analyzer.rb +0 -543
- data/lib/aidp/analyze/language_analysis_strategies.rb +0 -897
- data/lib/aidp/analyze/large_analysis_progress.rb +0 -504
- data/lib/aidp/analyze/memory_manager.rb +0 -365
- data/lib/aidp/analyze/metrics_storage.rb +0 -336
- data/lib/aidp/analyze/parallel_processor.rb +0 -460
- data/lib/aidp/analyze/performance_optimizer.rb +0 -694
- data/lib/aidp/analyze/repository_chunker.rb +0 -704
- data/lib/aidp/analyze/static_analysis_detector.rb +0 -577
- data/lib/aidp/analyze/storage.rb +0 -662
- data/lib/aidp/analyze/tool_configuration.rb +0 -456
- data/lib/aidp/analyze/tool_modernization.rb +0 -750
- data/lib/aidp/database/pg_adapter.rb +0 -148
- data/lib/aidp/database_config.rb +0 -69
- data/lib/aidp/database_connection.rb +0 -72
- data/lib/aidp/database_migration.rb +0 -158
- data/lib/aidp/job_manager.rb +0 -41
- data/lib/aidp/jobs/base_job.rb +0 -47
- data/lib/aidp/jobs/provider_execution_job.rb +0 -96
- data/lib/aidp/project_detector.rb +0 -117
- data/lib/aidp/providers/agent_supervisor.rb +0 -348
- data/lib/aidp/providers/supervised_base.rb +0 -317
- data/lib/aidp/providers/supervised_cursor.rb +0 -22
- data/lib/aidp/sync.rb +0 -13
- 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
|