agentcrew-ai 0.8.12__py3-none-any.whl → 0.8.13__py3-none-any.whl

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 (38) hide show
  1. AgentCrew/__init__.py +1 -1
  2. AgentCrew/main.py +55 -3
  3. AgentCrew/modules/agents/local_agent.py +25 -0
  4. AgentCrew/modules/code_analysis/__init__.py +8 -0
  5. AgentCrew/modules/code_analysis/parsers/__init__.py +67 -0
  6. AgentCrew/modules/code_analysis/parsers/base.py +93 -0
  7. AgentCrew/modules/code_analysis/parsers/cpp_parser.py +127 -0
  8. AgentCrew/modules/code_analysis/parsers/csharp_parser.py +162 -0
  9. AgentCrew/modules/code_analysis/parsers/generic_parser.py +63 -0
  10. AgentCrew/modules/code_analysis/parsers/go_parser.py +154 -0
  11. AgentCrew/modules/code_analysis/parsers/java_parser.py +103 -0
  12. AgentCrew/modules/code_analysis/parsers/javascript_parser.py +268 -0
  13. AgentCrew/modules/code_analysis/parsers/kotlin_parser.py +84 -0
  14. AgentCrew/modules/code_analysis/parsers/php_parser.py +107 -0
  15. AgentCrew/modules/code_analysis/parsers/python_parser.py +60 -0
  16. AgentCrew/modules/code_analysis/parsers/ruby_parser.py +46 -0
  17. AgentCrew/modules/code_analysis/parsers/rust_parser.py +72 -0
  18. AgentCrew/modules/code_analysis/service.py +231 -897
  19. AgentCrew/modules/command_execution/constants.py +2 -2
  20. AgentCrew/modules/console/confirmation_handler.py +4 -4
  21. AgentCrew/modules/console/console_ui.py +20 -1
  22. AgentCrew/modules/console/conversation_browser.py +557 -0
  23. AgentCrew/modules/console/diff_display.py +22 -51
  24. AgentCrew/modules/console/display_handlers.py +22 -22
  25. AgentCrew/modules/console/tool_display.py +4 -6
  26. AgentCrew/modules/file_editing/service.py +8 -8
  27. AgentCrew/modules/file_editing/tool.py +65 -67
  28. AgentCrew/modules/gui/components/tool_handlers.py +0 -2
  29. AgentCrew/modules/gui/widgets/diff_widget.py +30 -61
  30. AgentCrew/modules/llm/constants.py +5 -5
  31. AgentCrew/modules/memory/context_persistent.py +1 -0
  32. AgentCrew/modules/memory/tool.py +1 -1
  33. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/METADATA +1 -1
  34. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/RECORD +38 -24
  35. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/WHEEL +1 -1
  36. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/entry_points.txt +0 -0
  37. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/licenses/LICENSE +0 -0
  38. {agentcrew_ai-0.8.12.dist-info → agentcrew_ai-0.8.13.dist-info}/top_level.txt +0 -0
AgentCrew/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.8.12"
1
+ __version__ = "0.8.13"
AgentCrew/main.py CHANGED
@@ -86,7 +86,7 @@ def check_and_update():
86
86
  current_version = get_current_version()
87
87
 
88
88
  click.echo(f"Current version: {current_version}\nChecking for updates...")
89
- latest_version = get_latest_github_version()
89
+ latest_version, release_notes = get_latest_release_info()
90
90
 
91
91
  if not current_version or not latest_version:
92
92
  click.echo("⚠️ Could not determine version information", err=True)
@@ -98,8 +98,20 @@ def check_and_update():
98
98
  system = platform.system().lower()
99
99
 
100
100
  if system == "linux" or system == "darwin":
101
+ click.echo("\n" + "=" * 60)
102
+ click.echo("🔄 New version available!")
103
+ click.echo("=" * 60)
104
+
105
+ if release_notes:
106
+ click.echo("\n📝 Release Notes:")
107
+ click.echo("-" * 40)
108
+ click.echo(release_notes)
109
+ click.echo("-" * 40 + "\n")
110
+ else:
111
+ click.echo("\n⚠️ Could not fetch release notes.")
112
+
101
113
  if click.confirm(
102
- "🔄 New version available! Do you want to update now?",
114
+ "\nDo you want to update now?",
103
115
  default=False,
104
116
  ):
105
117
  click.echo("🔄 Starting update...")
@@ -109,7 +121,18 @@ def check_and_update():
109
121
  click.echo("⏭️ Skipping update. Starting application...")
110
122
  else:
111
123
  command = "uv tool install --python=3.12 --reinstall agentcrew-ai[cpu]@latest --index https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match"
112
- click.echo(f"🔄 New version available!\nRun {command} to update.")
124
+
125
+ click.echo("\n" + "=" * 60)
126
+ click.echo("🔄 New version available!")
127
+ click.echo("=" * 60)
128
+
129
+ if release_notes:
130
+ click.echo("\n📝 Release Notes:")
131
+ click.echo("-" * 40)
132
+ click.echo(release_notes)
133
+ click.echo("-" * 40 + "\n")
134
+
135
+ click.echo(f"Run the following command to update:\n\n{command}")
113
136
  else:
114
137
  click.echo("✅ You are running the latest version")
115
138
 
@@ -157,6 +180,35 @@ def get_latest_github_version():
157
180
  return None
158
181
 
159
182
 
183
+ def get_latest_release_info():
184
+ """Get the latest release information including version and release notes from GitHub
185
+
186
+ Returns:
187
+ tuple: (version, release_notes) where both can be None if not found.
188
+ """
189
+ try:
190
+ api_url = (
191
+ "https://api.github.com/repos/saigontechnology/AgentCrew/releases/latest"
192
+ )
193
+ response = requests.get(api_url, timeout=10)
194
+
195
+ if response.status_code == 200:
196
+ release_data = response.json()
197
+ tag_name = release_data.get("tag_name", "").lstrip("v")
198
+ name = release_data.get("name", "")
199
+ body = release_data.get("body", "")
200
+
201
+ release_notes = None
202
+ if body:
203
+ release_notes = f"## {name or tag_name}\n\n{body}"
204
+
205
+ return tag_name, release_notes
206
+
207
+ return None, None
208
+ except Exception:
209
+ return None, None
210
+
211
+
160
212
  def version_is_older(current: str, latest: str) -> bool:
161
213
  """
162
214
  Compare two semantic version strings to check if current is older than latest.
@@ -604,6 +604,21 @@ Whenever condition on `when` clause in a **Behavior** matches, tailor your respo
604
604
  )
605
605
  return adaptive_messages
606
606
 
607
+ def _get_directory_structure(self) -> str:
608
+ try:
609
+ cwd = os.getcwd()
610
+ entries = []
611
+ for entry in sorted(os.listdir(cwd)):
612
+ full_path = os.path.join(cwd, entry)
613
+ if os.path.isdir(full_path):
614
+ entries.append(f"{entry}/")
615
+ else:
616
+ entries.append(entry)
617
+ return "\n".join(entries) if entries else ""
618
+ except Exception as e:
619
+ logger.warning(f"Failed to get directory structure: {e}")
620
+ return ""
621
+
607
622
  def _enhance_agent_context_messages(self, final_messages: List[Dict[str, Any]]):
608
623
  last_user_index = next(
609
624
  (
@@ -651,6 +666,16 @@ Whenever condition on `when` clause in a **Behavior** matches, tailor your respo
651
666
  - Skip agent evaluation if user request is when...,[action]... related to adaptive behaviors call `adapt` tool instead.""",
652
667
  },
653
668
  )
669
+ if last_user_index == 0:
670
+ dir_structure = self._get_directory_structure()
671
+ if dir_structure:
672
+ adaptive_messages["content"].append(
673
+ {
674
+ "type": "text",
675
+ "text": f"Cwd structure:\n{dir_structure}",
676
+ }
677
+ )
678
+
654
679
  if len(adaptive_messages["content"]) > 0:
655
680
  final_messages.insert(last_user_index, adaptive_messages)
656
681
 
@@ -6,7 +6,15 @@ capabilities with platform-specific optimizations.
6
6
  """
7
7
 
8
8
  from .service import CodeAnalysisService
9
+ from .parsers import (
10
+ BaseLanguageParser,
11
+ get_parser_for_language,
12
+ LANGUAGE_PARSER_MAP,
13
+ )
9
14
 
10
15
  __all__ = [
11
16
  "CodeAnalysisService",
17
+ "BaseLanguageParser",
18
+ "get_parser_for_language",
19
+ "LANGUAGE_PARSER_MAP",
12
20
  ]
@@ -0,0 +1,67 @@
1
+ """
2
+ Language-specific parsers for code analysis.
3
+
4
+ This module provides a unified interface for parsing different programming languages
5
+ using tree-sitter.
6
+ """
7
+
8
+ from .base import BaseLanguageParser
9
+ from .python_parser import PythonParser
10
+ from .javascript_parser import JavaScriptParser
11
+ from .java_parser import JavaParser
12
+ from .cpp_parser import CppParser
13
+ from .ruby_parser import RubyParser
14
+ from .go_parser import GoParser
15
+ from .rust_parser import RustParser
16
+ from .php_parser import PhpParser
17
+ from .csharp_parser import CSharpParser
18
+ from .kotlin_parser import KotlinParser
19
+ from .generic_parser import GenericParser
20
+
21
+ LANGUAGE_PARSER_MAP = {
22
+ "python": PythonParser,
23
+ "javascript": JavaScriptParser,
24
+ "typescript": JavaScriptParser,
25
+ "java": JavaParser,
26
+ "cpp": CppParser,
27
+ "ruby": RubyParser,
28
+ "go": GoParser,
29
+ "rust": RustParser,
30
+ "php": PhpParser,
31
+ "c-sharp": CSharpParser,
32
+ "kotlin": KotlinParser,
33
+ }
34
+
35
+
36
+ def get_parser_for_language(language: str) -> BaseLanguageParser:
37
+ """
38
+ Get the appropriate parser for a given language.
39
+
40
+ Args:
41
+ language: The programming language name
42
+
43
+ Returns:
44
+ A parser instance for the language
45
+ """
46
+ parser_class = LANGUAGE_PARSER_MAP.get(language)
47
+ if parser_class:
48
+ return parser_class()
49
+ return GenericParser(language)
50
+
51
+
52
+ __all__ = [
53
+ "BaseLanguageParser",
54
+ "PythonParser",
55
+ "JavaScriptParser",
56
+ "JavaParser",
57
+ "CppParser",
58
+ "RubyParser",
59
+ "GoParser",
60
+ "RustParser",
61
+ "PhpParser",
62
+ "CSharpParser",
63
+ "KotlinParser",
64
+ "GenericParser",
65
+ "LANGUAGE_PARSER_MAP",
66
+ "get_parser_for_language",
67
+ ]
@@ -0,0 +1,93 @@
1
+ """
2
+ Base class for language-specific parsers.
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ class BaseLanguageParser(ABC):
10
+ """Abstract base class for language-specific code parsers."""
11
+
12
+ @property
13
+ @abstractmethod
14
+ def language_name(self) -> str:
15
+ """Return the language name for this parser."""
16
+ pass
17
+
18
+ @staticmethod
19
+ def extract_node_text(node, source_code: bytes) -> str:
20
+ """Extract text from a tree-sitter node."""
21
+ return source_code[node.start_byte : node.end_byte].decode("utf-8")
22
+
23
+ @abstractmethod
24
+ def process_node(
25
+ self, node, source_code: bytes, process_children_callback
26
+ ) -> Optional[Dict[str, Any]]:
27
+ """
28
+ Process a tree-sitter node and extract relevant information.
29
+
30
+ Args:
31
+ node: The tree-sitter node to process
32
+ source_code: The source code as bytes
33
+ process_children_callback: Callback to process children nodes recursively
34
+
35
+ Returns:
36
+ Dictionary with node information or None if not relevant
37
+ """
38
+ pass
39
+
40
+ def _create_base_result(self, node) -> Dict[str, Any]:
41
+ """Create the base result dictionary with common fields."""
42
+ return {
43
+ "type": node.type,
44
+ "start_line": node.start_point[0] + 1,
45
+ "end_line": node.end_point[0] + 1,
46
+ }
47
+
48
+ def _process_children_default(
49
+ self, node, source_code: bytes, process_children_callback
50
+ ) -> List[Dict[str, Any]]:
51
+ """Default implementation for processing children nodes."""
52
+ children = []
53
+ for child in node.children:
54
+ child_result = process_children_callback(child)
55
+ if child_result and self._is_significant_node(child_result):
56
+ children.append(child_result)
57
+ return children
58
+
59
+ def _is_significant_node(self, result: Dict[str, Any]) -> bool:
60
+ """Check if a node result is significant and should be included."""
61
+ significant_types = {
62
+ "class_definition",
63
+ "function_definition",
64
+ "class_declaration",
65
+ "method_definition",
66
+ "function_declaration",
67
+ "interface_declaration",
68
+ "method_declaration",
69
+ "constructor_declaration",
70
+ "class_specifier",
71
+ "struct_specifier",
72
+ "struct_declaration",
73
+ "class",
74
+ "method",
75
+ "singleton_method",
76
+ "module",
77
+ "type_declaration",
78
+ "struct_item",
79
+ "impl_item",
80
+ "fn_item",
81
+ "trait_item",
82
+ "trait_declaration",
83
+ "property_declaration",
84
+ "field_declaration",
85
+ "public_field_definition",
86
+ "const_declaration",
87
+ "object_definition",
88
+ "trait_definition",
89
+ "def_definition",
90
+ "variable_declaration",
91
+ "arrow_function",
92
+ }
93
+ return result.get("type") in significant_types or "children" in result
@@ -0,0 +1,127 @@
1
+ """
2
+ C++ language parser for code analysis.
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from .base import BaseLanguageParser
8
+
9
+
10
+ class CppParser(BaseLanguageParser):
11
+ """Parser for C++ source code."""
12
+
13
+ @property
14
+ def language_name(self) -> str:
15
+ return "cpp"
16
+
17
+ def process_node(
18
+ self, node, source_code: bytes, process_children_callback
19
+ ) -> Optional[Dict[str, Any]]:
20
+ result = self._create_base_result(node)
21
+
22
+ if node.type in ["class_specifier", "struct_specifier"]:
23
+ for child in node.children:
24
+ if child.type in ["identifier", "type_identifier"]:
25
+ result["name"] = self.extract_node_text(child, source_code)
26
+ elif child.type == "field_declaration_list":
27
+ children = []
28
+ for body_child in child.children:
29
+ child_result = process_children_callback(body_child)
30
+ if child_result and self._is_significant_node(child_result):
31
+ children.append(child_result)
32
+ if children:
33
+ result["children"] = children
34
+ return result
35
+
36
+ elif node.type == "function_definition":
37
+ for child in node.children:
38
+ if child.type in ["identifier", "field_identifier"]:
39
+ result["name"] = self.extract_node_text(child, source_code)
40
+ return result
41
+ elif child.type == "function_declarator":
42
+ for subchild in child.children:
43
+ if subchild.type in ["identifier", "field_identifier"]:
44
+ result["name"] = self.extract_node_text(
45
+ subchild, source_code
46
+ )
47
+ return result
48
+ return result
49
+
50
+ elif node.type == "field_declaration":
51
+ return self._handle_field_declaration(node, source_code, result)
52
+
53
+ elif node.type in ["declaration", "variable_declaration"]:
54
+ return self._handle_declaration(node, source_code, result)
55
+
56
+ children = []
57
+ for child in node.children:
58
+ child_result = process_children_callback(child)
59
+ if child_result and self._is_significant_node(child_result):
60
+ children.append(child_result)
61
+
62
+ if children:
63
+ result["children"] = children
64
+
65
+ return result
66
+
67
+ def _handle_field_declaration(
68
+ self, node, source_code: bytes, result: Dict[str, Any]
69
+ ) -> Dict[str, Any]:
70
+ field_name = None
71
+ field_type = None
72
+
73
+ for child in node.children:
74
+ if child.type in [
75
+ "primitive_type",
76
+ "type_identifier",
77
+ "qualified_identifier",
78
+ "template_type",
79
+ ]:
80
+ field_type = self.extract_node_text(child, source_code)
81
+ elif child.type == "field_identifier":
82
+ field_name = self.extract_node_text(child, source_code)
83
+
84
+ if field_name:
85
+ result["type"] = "field_declaration"
86
+ if field_type:
87
+ result["name"] = f"{field_type} {field_name}"
88
+ else:
89
+ result["name"] = field_name
90
+
91
+ return result
92
+
93
+ def _handle_declaration(
94
+ self, node, source_code: bytes, result: Dict[str, Any]
95
+ ) -> Dict[str, Any]:
96
+ var_name = None
97
+ var_type = None
98
+
99
+ for child in node.children:
100
+ if child.type in [
101
+ "primitive_type",
102
+ "type_identifier",
103
+ "qualified_identifier",
104
+ "template_type",
105
+ ]:
106
+ var_type = self.extract_node_text(child, source_code)
107
+ elif child.type in ["init_declarator", "declarator"]:
108
+ for subchild in child.children:
109
+ if subchild.type == "identifier":
110
+ var_name = self.extract_node_text(subchild, source_code)
111
+ break
112
+ elif subchild.type == "pointer_declarator":
113
+ for ptr_child in subchild.children:
114
+ if ptr_child.type == "identifier":
115
+ var_name = self.extract_node_text(
116
+ ptr_child, source_code
117
+ )
118
+ break
119
+
120
+ if var_name:
121
+ result["type"] = "variable_declaration"
122
+ if var_type:
123
+ result["name"] = f"{var_type} {var_name}"
124
+ else:
125
+ result["name"] = var_name
126
+
127
+ return result
@@ -0,0 +1,162 @@
1
+ """
2
+ C# language parser for code analysis.
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from .base import BaseLanguageParser
8
+
9
+
10
+ class CSharpParser(BaseLanguageParser):
11
+ """Parser for C# source code."""
12
+
13
+ @property
14
+ def language_name(self) -> str:
15
+ return "c-sharp"
16
+
17
+ def process_node(
18
+ self, node, source_code: bytes, process_children_callback
19
+ ) -> Optional[Dict[str, Any]]:
20
+ result = self._create_base_result(node)
21
+
22
+ if node.type == "class_declaration":
23
+ self._handle_class_declaration(node, source_code, result)
24
+
25
+ elif node.type == "method_declaration":
26
+ self._handle_method_declaration(node, source_code, result)
27
+
28
+ elif node.type == "property_declaration":
29
+ self._handle_property_declaration(node, source_code, result)
30
+
31
+ elif node.type == "field_declaration":
32
+ self._handle_field_declaration(node, source_code, result)
33
+
34
+ children = []
35
+ for child in node.children:
36
+ child_result = process_children_callback(child)
37
+ if child_result and self._is_significant_node(child_result):
38
+ children.append(child_result)
39
+
40
+ if children:
41
+ result["children"] = children
42
+
43
+ return result
44
+
45
+ def _handle_class_declaration(
46
+ self, node, source_code: bytes, result: Dict[str, Any]
47
+ ) -> None:
48
+ for child in node.children:
49
+ if child.type == "identifier":
50
+ result["name"] = self.extract_node_text(child, source_code)
51
+ elif child.type == "base_list":
52
+ if len(child.children) > 1:
53
+ result["base_class"] = self.extract_node_text(
54
+ child.children[1], source_code
55
+ )
56
+
57
+ def _handle_method_declaration(
58
+ self, node, source_code: bytes, result: Dict[str, Any]
59
+ ) -> None:
60
+ method_name = None
61
+ parameters = []
62
+ access_modifiers = []
63
+
64
+ for child in node.children:
65
+ if child.type == "identifier":
66
+ method_name = self.extract_node_text(child, source_code)
67
+ result["name"] = method_name
68
+ elif child.type == "parameter_list":
69
+ for param in child.children:
70
+ if param.type == "parameter":
71
+ param_type = ""
72
+ param_name = None
73
+
74
+ type_node = param.child_by_field_name("type")
75
+ name_node = param.child_by_field_name("name")
76
+
77
+ if type_node:
78
+ param_type = self.extract_node_text(type_node, source_code)
79
+ if name_node:
80
+ param_name = self.extract_node_text(name_node, source_code)
81
+
82
+ if param_name:
83
+ parameters.append(param_type + " " + param_name)
84
+
85
+ if parameters:
86
+ result["parameters"] = parameters
87
+ elif child.type == "modifier":
88
+ modifier = self.extract_node_text(child, source_code)
89
+ access_modifiers.append(modifier)
90
+
91
+ if access_modifiers:
92
+ result["modifiers"] = access_modifiers
93
+
94
+ def _handle_property_declaration(
95
+ self, node, source_code: bytes, result: Dict[str, Any]
96
+ ) -> None:
97
+ property_name = None
98
+ property_type = None
99
+ modifiers = []
100
+
101
+ for child in node.children:
102
+ if child.type == "modifier":
103
+ modifiers.append(self.extract_node_text(child, source_code))
104
+ elif child.type in [
105
+ "predefined_type",
106
+ "nullable_type",
107
+ "generic_name",
108
+ "array_type",
109
+ ]:
110
+ property_type = self.extract_node_text(child, source_code)
111
+ elif child.type == "identifier":
112
+ if property_type is None:
113
+ property_type = self.extract_node_text(child, source_code)
114
+ else:
115
+ property_name = self.extract_node_text(child, source_code)
116
+
117
+ if property_name:
118
+ result["type"] = "property_declaration"
119
+ if property_type:
120
+ result["name"] = f"{property_type} {property_name}"
121
+ else:
122
+ result["name"] = property_name
123
+ if modifiers:
124
+ result["modifiers"] = modifiers
125
+
126
+ def _handle_field_declaration(
127
+ self, node, source_code: bytes, result: Dict[str, Any]
128
+ ) -> None:
129
+ field_name = None
130
+ field_type = None
131
+ modifiers = []
132
+
133
+ for child in node.children:
134
+ if child.type == "modifier":
135
+ modifiers.append(self.extract_node_text(child, source_code))
136
+ elif child.type == "variable_declaration":
137
+ for subchild in child.children:
138
+ if subchild.type in [
139
+ "predefined_type",
140
+ "nullable_type",
141
+ "generic_name",
142
+ "array_type",
143
+ ]:
144
+ field_type = self.extract_node_text(subchild, source_code)
145
+ elif subchild.type == "identifier" and field_type is None:
146
+ field_type = self.extract_node_text(subchild, source_code)
147
+ elif subchild.type == "variable_declarator":
148
+ for var_child in subchild.children:
149
+ if var_child.type == "identifier":
150
+ field_name = self.extract_node_text(
151
+ var_child, source_code
152
+ )
153
+ break
154
+
155
+ if field_name:
156
+ result["type"] = "field_declaration"
157
+ if field_type:
158
+ result["name"] = f"{field_type} {field_name}"
159
+ else:
160
+ result["name"] = field_name
161
+ if modifiers:
162
+ result["modifiers"] = modifiers
@@ -0,0 +1,63 @@
1
+ """
2
+ Generic language parser for code analysis (fallback for unsupported languages).
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from .base import BaseLanguageParser
8
+
9
+
10
+ class GenericParser(BaseLanguageParser):
11
+ """Generic parser for languages without specific implementation."""
12
+
13
+ def __init__(self, language: str = "unknown"):
14
+ self._language = language
15
+
16
+ @property
17
+ def language_name(self) -> str:
18
+ return self._language
19
+
20
+ def process_node(
21
+ self, node, source_code: bytes, process_children_callback
22
+ ) -> Optional[Dict[str, Any]]:
23
+ result = self._create_base_result(node)
24
+
25
+ if node.type in [
26
+ "type_declaration",
27
+ "function_declaration",
28
+ "method_declaration",
29
+ "interface_declaration",
30
+ ]:
31
+ for child in node.children:
32
+ if child.type in ["identifier", "field_identifier"]:
33
+ result["name"] = self.extract_node_text(child, source_code)
34
+ result["first_line"] = (
35
+ self.extract_node_text(node, source_code)
36
+ .split("\n")[0]
37
+ .strip("{")
38
+ )
39
+ return result
40
+ return result
41
+
42
+ elif node.type in ["var_declaration", "const_declaration"]:
43
+ for child in node.children:
44
+ if child.type in ["var_spec", "const_spec"]:
45
+ for subchild in child.children:
46
+ if subchild.type == "identifier":
47
+ result["type"] = "variable_declaration"
48
+ result["name"] = self.extract_node_text(
49
+ subchild, source_code
50
+ )
51
+ return result
52
+ return result
53
+
54
+ children = []
55
+ for child in node.children:
56
+ child_result = process_children_callback(child)
57
+ if child_result and self._is_significant_node(child_result):
58
+ children.append(child_result)
59
+
60
+ if children:
61
+ result["children"] = children
62
+
63
+ return result