jarvis-ai-assistant 0.7.0__py3-none-any.whl → 0.7.6__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 (159) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +243 -139
  3. jarvis/jarvis_agent/agent_manager.py +5 -10
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +265 -15
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +113 -98
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +6 -12
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +77 -14
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +12 -21
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/task_analyzer.py +26 -4
  31. jarvis/jarvis_agent/task_manager.py +11 -27
  32. jarvis/jarvis_agent/tool_executor.py +2 -3
  33. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  34. jarvis/jarvis_agent/web_server.py +55 -20
  35. jarvis/jarvis_c2rust/__init__.py +5 -5
  36. jarvis/jarvis_c2rust/cli.py +461 -499
  37. jarvis/jarvis_c2rust/collector.py +45 -53
  38. jarvis/jarvis_c2rust/constants.py +26 -0
  39. jarvis/jarvis_c2rust/library_replacer.py +264 -132
  40. jarvis/jarvis_c2rust/llm_module_agent.py +162 -190
  41. jarvis/jarvis_c2rust/loaders.py +207 -0
  42. jarvis/jarvis_c2rust/models.py +28 -0
  43. jarvis/jarvis_c2rust/optimizer.py +1592 -395
  44. jarvis/jarvis_c2rust/transpiler.py +1722 -1064
  45. jarvis/jarvis_c2rust/utils.py +385 -0
  46. jarvis/jarvis_code_agent/build_validation_config.py +2 -3
  47. jarvis/jarvis_code_agent/code_agent.py +394 -320
  48. jarvis/jarvis_code_agent/code_analyzer/__init__.py +3 -0
  49. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +4 -0
  50. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +17 -2
  51. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +3 -0
  52. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +36 -4
  53. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +9 -0
  54. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +9 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +12 -1
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +22 -5
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +57 -32
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +62 -6
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +8 -9
  60. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +290 -5
  61. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -0
  62. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +21 -3
  63. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +72 -4
  64. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +35 -3
  65. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  66. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  67. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +52 -2
  68. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +73 -1
  69. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  70. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +306 -152
  71. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  72. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +193 -18
  73. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +18 -8
  74. jarvis/jarvis_code_agent/lint.py +258 -27
  75. jarvis/jarvis_code_agent/utils.py +0 -1
  76. jarvis/jarvis_code_analysis/code_review.py +19 -24
  77. jarvis/jarvis_data/config_schema.json +53 -26
  78. jarvis/jarvis_git_squash/main.py +4 -5
  79. jarvis/jarvis_git_utils/git_commiter.py +44 -49
  80. jarvis/jarvis_mcp/sse_mcp_client.py +20 -27
  81. jarvis/jarvis_mcp/stdio_mcp_client.py +11 -12
  82. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  83. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  84. jarvis/jarvis_methodology/main.py +32 -48
  85. jarvis/jarvis_multi_agent/__init__.py +79 -61
  86. jarvis/jarvis_multi_agent/main.py +3 -7
  87. jarvis/jarvis_platform/base.py +469 -199
  88. jarvis/jarvis_platform/human.py +7 -8
  89. jarvis/jarvis_platform/kimi.py +30 -36
  90. jarvis/jarvis_platform/openai.py +65 -27
  91. jarvis/jarvis_platform/registry.py +26 -10
  92. jarvis/jarvis_platform/tongyi.py +24 -25
  93. jarvis/jarvis_platform/yuanbao.py +31 -42
  94. jarvis/jarvis_platform_manager/main.py +66 -77
  95. jarvis/jarvis_platform_manager/service.py +8 -13
  96. jarvis/jarvis_rag/cli.py +49 -51
  97. jarvis/jarvis_rag/embedding_manager.py +13 -18
  98. jarvis/jarvis_rag/llm_interface.py +8 -9
  99. jarvis/jarvis_rag/query_rewriter.py +10 -21
  100. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  101. jarvis/jarvis_rag/reranker.py +4 -5
  102. jarvis/jarvis_rag/retriever.py +28 -30
  103. jarvis/jarvis_sec/__init__.py +220 -3520
  104. jarvis/jarvis_sec/agents.py +143 -0
  105. jarvis/jarvis_sec/analysis.py +276 -0
  106. jarvis/jarvis_sec/cli.py +29 -6
  107. jarvis/jarvis_sec/clustering.py +1439 -0
  108. jarvis/jarvis_sec/file_manager.py +427 -0
  109. jarvis/jarvis_sec/parsers.py +73 -0
  110. jarvis/jarvis_sec/prompts.py +268 -0
  111. jarvis/jarvis_sec/report.py +83 -4
  112. jarvis/jarvis_sec/review.py +453 -0
  113. jarvis/jarvis_sec/utils.py +499 -0
  114. jarvis/jarvis_sec/verification.py +848 -0
  115. jarvis/jarvis_sec/workflow.py +7 -0
  116. jarvis/jarvis_smart_shell/main.py +38 -87
  117. jarvis/jarvis_stats/cli.py +1 -1
  118. jarvis/jarvis_stats/stats.py +7 -7
  119. jarvis/jarvis_stats/storage.py +15 -21
  120. jarvis/jarvis_tools/clear_memory.py +3 -20
  121. jarvis/jarvis_tools/cli/main.py +20 -23
  122. jarvis/jarvis_tools/edit_file.py +1066 -0
  123. jarvis/jarvis_tools/execute_script.py +42 -21
  124. jarvis/jarvis_tools/file_analyzer.py +6 -9
  125. jarvis/jarvis_tools/generate_new_tool.py +11 -20
  126. jarvis/jarvis_tools/lsp_client.py +1552 -0
  127. jarvis/jarvis_tools/methodology.py +2 -3
  128. jarvis/jarvis_tools/read_code.py +1525 -87
  129. jarvis/jarvis_tools/read_symbols.py +2 -3
  130. jarvis/jarvis_tools/read_webpage.py +7 -10
  131. jarvis/jarvis_tools/registry.py +370 -181
  132. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  133. jarvis/jarvis_tools/rewrite_file.py +105 -0
  134. jarvis/jarvis_tools/save_memory.py +3 -15
  135. jarvis/jarvis_tools/search_web.py +3 -7
  136. jarvis/jarvis_tools/sub_agent.py +17 -6
  137. jarvis/jarvis_tools/sub_code_agent.py +14 -16
  138. jarvis/jarvis_tools/virtual_tty.py +54 -32
  139. jarvis/jarvis_utils/clipboard.py +7 -10
  140. jarvis/jarvis_utils/config.py +98 -63
  141. jarvis/jarvis_utils/embedding.py +5 -5
  142. jarvis/jarvis_utils/fzf.py +8 -8
  143. jarvis/jarvis_utils/git_utils.py +81 -67
  144. jarvis/jarvis_utils/input.py +24 -49
  145. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  146. jarvis/jarvis_utils/methodology.py +33 -35
  147. jarvis/jarvis_utils/utils.py +245 -202
  148. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/METADATA +205 -70
  149. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  150. jarvis/jarvis_agent/edit_file_handler.py +0 -584
  151. jarvis/jarvis_agent/rewrite_file_handler.py +0 -141
  152. jarvis/jarvis_agent/task_planner.py +0 -496
  153. jarvis/jarvis_platform/ai8.py +0 -332
  154. jarvis/jarvis_tools/ask_user.py +0 -54
  155. jarvis_ai_assistant-0.7.0.dist-info/RECORD +0 -192
  156. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  157. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +0 -0
  158. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  159. {jarvis_ai_assistant-0.7.0.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,212 @@
1
+ """Java语言支持实现。"""
2
+
3
+ import os
4
+ import re
5
+ from typing import List, Optional, Set
6
+
7
+ from ..base_language import BaseLanguageSupport
8
+ from ..dependency_analyzer import Dependency, DependencyAnalyzer, DependencyGraph
9
+ from ..file_ignore import filter_walk_dirs
10
+ from ..symbol_extractor import Symbol, SymbolExtractor
11
+ from ..tree_sitter_extractor import TreeSitterExtractor
12
+
13
+ try:
14
+ from tree_sitter import Language, Node
15
+ import tree_sitter_java
16
+ JAVA_LANGUAGE: Optional[Language] = tree_sitter_java.language()
17
+ except (ImportError, Exception):
18
+ JAVA_LANGUAGE = None
19
+
20
+
21
+ # --- Java Symbol Extractor ---
22
+
23
+ JAVA_SYMBOL_QUERY = """
24
+ (method_declaration
25
+ name: (identifier) @method.name)
26
+
27
+ (class_declaration
28
+ name: (identifier) @class.name)
29
+
30
+ (interface_declaration
31
+ name: (identifier) @interface.name)
32
+
33
+ (enum_declaration
34
+ name: (identifier) @enum.name)
35
+
36
+ (annotation_type_declaration
37
+ name: (identifier) @annotation.name)
38
+
39
+ (field_declaration
40
+ (variable_declarator
41
+ name: (identifier) @field.name))
42
+
43
+ (constructor_declaration
44
+ name: (identifier) @constructor.name)
45
+ """
46
+
47
+
48
+ class JavaSymbolExtractor(TreeSitterExtractor):
49
+ """Extracts symbols from Java code using tree-sitter."""
50
+
51
+ def __init__(self):
52
+ if not JAVA_LANGUAGE:
53
+ raise RuntimeError("Java tree-sitter grammar not available.")
54
+ # 如果传入的是 PyCapsule,需要转换为 Language 对象
55
+ if not isinstance(JAVA_LANGUAGE, Language):
56
+ lang = Language(JAVA_LANGUAGE)
57
+ else:
58
+ lang = JAVA_LANGUAGE
59
+ super().__init__(lang, JAVA_SYMBOL_QUERY)
60
+
61
+ def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
62
+ """Maps a tree-sitter capture to a Symbol object."""
63
+ kind_map = {
64
+ "method.name": "method",
65
+ "class.name": "class",
66
+ "interface.name": "interface",
67
+ "enum.name": "enum",
68
+ "annotation.name": "annotation",
69
+ "field.name": "field",
70
+ "constructor.name": "constructor",
71
+ }
72
+
73
+ symbol_kind = kind_map.get(name)
74
+ if not symbol_kind:
75
+ return None
76
+
77
+ return Symbol(
78
+ name=node.text.decode('utf8'),
79
+ kind=symbol_kind,
80
+ file_path=file_path,
81
+ line_start=node.start_point[0] + 1,
82
+ line_end=node.end_point[0] + 1,
83
+ )
84
+
85
+
86
+ # --- Java Dependency Analyzer ---
87
+
88
+ class JavaDependencyAnalyzer(DependencyAnalyzer):
89
+ """Analyzes Java import dependencies."""
90
+
91
+ def analyze_imports(self, file_path: str, content: str) -> List[Dependency]:
92
+ """Analyzes Java import statements."""
93
+ dependencies: List[Dependency] = []
94
+
95
+ # Java import statements
96
+ # import package.Class;
97
+ # import package.*;
98
+ # import static package.Class.method;
99
+ import_pattern = re.compile(
100
+ r'import\s+(?:static\s+)?([\w.]+)(?:\.\*)?\s*;',
101
+ re.MULTILINE
102
+ )
103
+
104
+ for line_num, line in enumerate(content.split('\n'), start=1):
105
+ import_match = import_pattern.search(line)
106
+ if import_match:
107
+ module_path = import_match.group(1)
108
+ if module_path:
109
+ # Extract class name if it's a specific import
110
+ parts = module_path.split('.')
111
+ imported_symbol = None
112
+ if len(parts) > 1 and not line.strip().endswith('.*;'):
113
+ # Specific class import
114
+ imported_symbol = parts[-1]
115
+ module_path = '.'.join(parts[:-1])
116
+
117
+ dependencies.append(Dependency(
118
+ from_module=module_path,
119
+ imported_symbol=imported_symbol,
120
+ file_path=file_path,
121
+ line=line_num,
122
+ ))
123
+
124
+ return dependencies
125
+
126
+ def build_dependency_graph(self, project_root: str) -> DependencyGraph:
127
+ """Builds a dependency graph for a Java project."""
128
+ graph = DependencyGraph()
129
+
130
+ for root, dirs, files in os.walk(project_root):
131
+ dirs[:] = filter_walk_dirs(dirs)
132
+
133
+ for file in files:
134
+ if not file.endswith('.java'):
135
+ continue
136
+
137
+ file_path = os.path.join(root, file)
138
+ try:
139
+ with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
140
+ content = f.read()
141
+
142
+ dependencies = self.analyze_imports(file_path, content)
143
+ for dep in dependencies:
144
+ # Skip java.* and javax.* standard library packages
145
+ if not dep.from_module.startswith(('java.', 'javax.')):
146
+ dep_path = self._resolve_module_path(project_root, dep.from_module, dep.imported_symbol, file_path)
147
+ if dep_path and dep_path != file_path:
148
+ graph.add_dependency(file_path, dep_path)
149
+ except Exception:
150
+ continue
151
+
152
+ return graph
153
+
154
+ def _resolve_module_path(self, project_root: str, package_name: str, class_name: Optional[str], from_file: str) -> Optional[str]:
155
+ """Resolve a Java package name to a file path."""
156
+ if not package_name:
157
+ return None
158
+
159
+ # Convert package name to directory path
160
+ package_path = package_name.replace('.', os.sep)
161
+
162
+ # Try to find the class file
163
+ if class_name:
164
+ # Specific class import
165
+ class_file = class_name + '.java'
166
+ resolved = os.path.normpath(os.path.join(project_root, 'src', 'main', 'java', package_path, class_file))
167
+ if os.path.exists(resolved) and os.path.isfile(resolved):
168
+ return resolved
169
+
170
+ # Try alternative source directories
171
+ for src_dir in ['src', 'source', 'java']:
172
+ resolved = os.path.normpath(os.path.join(project_root, src_dir, package_path, class_file))
173
+ if os.path.exists(resolved) and os.path.isfile(resolved):
174
+ return resolved
175
+ else:
176
+ # Wildcard import - try to find package-info.java or any class in the package
177
+ package_dir = os.path.normpath(os.path.join(project_root, 'src', 'main', 'java', package_path))
178
+ if os.path.exists(package_dir) and os.path.isdir(package_dir):
179
+ # Return the first .java file found (or package-info.java if exists)
180
+ for file in os.listdir(package_dir):
181
+ if file.endswith('.java'):
182
+ resolved = os.path.join(package_dir, file)
183
+ if os.path.isfile(resolved):
184
+ return resolved
185
+
186
+ return None
187
+
188
+
189
+ # --- Java Language Support ---
190
+
191
+ class JavaLanguageSupport(BaseLanguageSupport):
192
+ """Java语言支持。"""
193
+
194
+ @property
195
+ def language_name(self) -> str:
196
+ return "java"
197
+
198
+ @property
199
+ def file_extensions(self) -> Set[str]:
200
+ return {'.java'}
201
+
202
+ def create_symbol_extractor(self) -> Optional[SymbolExtractor]:
203
+ if not JAVA_LANGUAGE:
204
+ return None
205
+ try:
206
+ return JavaSymbolExtractor()
207
+ except Exception:
208
+ return None
209
+
210
+ def create_dependency_analyzer(self) -> Optional[DependencyAnalyzer]:
211
+ return JavaDependencyAnalyzer()
212
+
@@ -0,0 +1,254 @@
1
+ """JavaScript语言支持实现。"""
2
+
3
+ import os
4
+ import re
5
+ from typing import List, Optional, Set
6
+
7
+ from ..base_language import BaseLanguageSupport
8
+ from ..dependency_analyzer import Dependency, DependencyAnalyzer, DependencyGraph
9
+ from ..file_ignore import filter_walk_dirs
10
+ from ..symbol_extractor import Symbol, SymbolExtractor
11
+ from ..tree_sitter_extractor import TreeSitterExtractor
12
+
13
+ try:
14
+ from tree_sitter import Language, Node
15
+ import tree_sitter_javascript
16
+ JS_LANGUAGE: Optional[Language] = tree_sitter_javascript.language()
17
+ except (ImportError, Exception):
18
+ JS_LANGUAGE = None
19
+
20
+
21
+ # --- JavaScript Symbol Extractor ---
22
+
23
+ JS_SYMBOL_QUERY = """
24
+ (function_declaration
25
+ name: (identifier) @function.name)
26
+
27
+ (function_expression
28
+ name: (identifier) @function.name)
29
+
30
+ (generator_function_declaration
31
+ name: (identifier) @generator.name)
32
+
33
+ (generator_function
34
+ name: (identifier) @generator.name)
35
+
36
+ (arrow_function) @arrow.function
37
+
38
+ (method_definition
39
+ name: (property_identifier) @method.name)
40
+
41
+ (class_declaration
42
+ name: (identifier) @class.name)
43
+
44
+ (class_expression
45
+ name: (identifier) @class.name)
46
+
47
+ (variable_declaration
48
+ (variable_declarator
49
+ name: (identifier) @variable.name))
50
+ """
51
+
52
+
53
+ class JavaScriptSymbolExtractor(TreeSitterExtractor):
54
+ """Extracts symbols from JavaScript code using tree-sitter."""
55
+
56
+ def __init__(self):
57
+ if not JS_LANGUAGE:
58
+ raise RuntimeError("JavaScript tree-sitter grammar not available.")
59
+ # 如果传入的是 PyCapsule,需要转换为 Language 对象
60
+ if not isinstance(JS_LANGUAGE, Language):
61
+ lang = Language(JS_LANGUAGE)
62
+ else:
63
+ lang = JS_LANGUAGE
64
+ super().__init__(lang, JS_SYMBOL_QUERY)
65
+
66
+ def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
67
+ """Maps a tree-sitter capture to a Symbol object."""
68
+ kind_map = {
69
+ "function.name": "function",
70
+ "arrow.function": "function",
71
+ "generator.name": "function",
72
+ "method.name": "method",
73
+ "class.name": "class",
74
+ "variable.name": "variable",
75
+ }
76
+
77
+ symbol_kind = kind_map.get(name)
78
+ if not symbol_kind:
79
+ return None
80
+
81
+ # For arrow functions without names, use a generated name
82
+ if name == "arrow.function":
83
+ symbol_name = "<anonymous_arrow_function>"
84
+ elif name == "generator.name":
85
+ # Generator functions are also functions
86
+ symbol_name = node.text.decode('utf8')
87
+ else:
88
+ symbol_name = node.text.decode('utf8')
89
+
90
+ if not symbol_name:
91
+ return None
92
+
93
+ return Symbol(
94
+ name=symbol_name,
95
+ kind=symbol_kind,
96
+ file_path=file_path,
97
+ line_start=node.start_point[0] + 1,
98
+ line_end=node.end_point[0] + 1,
99
+ )
100
+
101
+
102
+ # --- JavaScript Dependency Analyzer ---
103
+
104
+ class JavaScriptDependencyAnalyzer(DependencyAnalyzer):
105
+ """Analyzes JavaScript import/require dependencies."""
106
+
107
+ def analyze_imports(self, file_path: str, content: str) -> List[Dependency]:
108
+ """Analyzes JavaScript import and require statements."""
109
+ dependencies: List[Dependency] = []
110
+
111
+ # ES6 import statements
112
+ # import module from 'path'
113
+ # import { symbol } from 'path'
114
+ # import * as alias from 'path'
115
+ import_pattern = re.compile(
116
+ r'import\s+(?:(?:\*\s+as\s+(\w+)|(\{[^}]*\})|(\w+))\s+from\s+)?["\']([^"\']+)["\']',
117
+ re.MULTILINE
118
+ )
119
+
120
+ # CommonJS require statements
121
+ # const module = require('path')
122
+ # require('path')
123
+ require_pattern = re.compile(
124
+ r'(?:const|let|var)\s+\w+\s*=\s*require\(["\']([^"\']+)["\']\)|require\(["\']([^"\']+)["\']\)',
125
+ re.MULTILINE
126
+ )
127
+
128
+ for line_num, line in enumerate(content.split('\n'), start=1):
129
+ # Check for ES6 imports
130
+ import_match = import_pattern.search(line)
131
+ if import_match:
132
+ module_path = import_match.group(4) or import_match.group(5)
133
+ if module_path:
134
+ # Extract imported symbols if any
135
+ symbols_group = import_match.group(2) or import_match.group(1)
136
+ imported_symbol = None
137
+ if symbols_group and symbols_group.startswith('{'):
138
+ # Extract first symbol from { symbol1, symbol2 }
139
+ symbol_match = re.search(r'\{([^}]+)\}', symbols_group)
140
+ if symbol_match:
141
+ first_symbol = symbol_match.group(1).split(',')[0].strip()
142
+ imported_symbol = first_symbol.split(' as ')[0].strip()
143
+
144
+ dependencies.append(Dependency(
145
+ from_module=module_path,
146
+ imported_symbol=imported_symbol,
147
+ file_path=file_path,
148
+ line=line_num,
149
+ ))
150
+
151
+ # Check for require statements
152
+ require_match = require_pattern.search(line)
153
+ if require_match:
154
+ module_path = require_match.group(1) or require_match.group(2)
155
+ if module_path:
156
+ dependencies.append(Dependency(
157
+ from_module=module_path,
158
+ imported_symbol=None,
159
+ file_path=file_path,
160
+ line=line_num,
161
+ ))
162
+
163
+ return dependencies
164
+
165
+ def build_dependency_graph(self, project_root: str) -> DependencyGraph:
166
+ """Builds a dependency graph for a JavaScript project."""
167
+ graph = DependencyGraph()
168
+
169
+ for root, dirs, files in os.walk(project_root):
170
+ dirs[:] = filter_walk_dirs(dirs)
171
+
172
+ for file in files:
173
+ if not file.endswith(('.js', '.jsx', '.mjs', '.cjs')):
174
+ continue
175
+
176
+ file_path = os.path.join(root, file)
177
+ try:
178
+ with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
179
+ content = f.read()
180
+
181
+ dependencies = self.analyze_imports(file_path, content)
182
+ for dep in dependencies:
183
+ # Skip node_modules and external packages
184
+ if dep.from_module.startswith(('.', '/')) or not dep.from_module.startswith('http'):
185
+ dep_path = self._resolve_module_path(project_root, dep.from_module, file_path)
186
+ if dep_path and dep_path != file_path:
187
+ graph.add_dependency(file_path, dep_path)
188
+ except Exception:
189
+ continue
190
+
191
+ return graph
192
+
193
+ def _resolve_module_path(self, project_root: str, module_name: str, from_file: str) -> Optional[str]:
194
+ """Resolve a JavaScript module name to a file path."""
195
+ if not module_name:
196
+ return None
197
+
198
+ # Handle relative imports
199
+ if module_name.startswith('.'):
200
+ base_dir = os.path.dirname(from_file)
201
+ # Resolve relative path
202
+ if module_name.endswith('.js') or module_name.endswith('.jsx'):
203
+ # Direct file reference
204
+ resolved = os.path.normpath(os.path.join(base_dir, module_name))
205
+ else:
206
+ # Try with .js extension
207
+ resolved = os.path.normpath(os.path.join(base_dir, module_name + '.js'))
208
+ if not os.path.exists(resolved):
209
+ # Try with .jsx extension
210
+ resolved = os.path.normpath(os.path.join(base_dir, module_name + '.jsx'))
211
+ if not os.path.exists(resolved):
212
+ # Try index.js
213
+ resolved = os.path.normpath(os.path.join(base_dir, module_name, 'index.js'))
214
+
215
+ if os.path.exists(resolved) and os.path.isfile(resolved):
216
+ return resolved
217
+
218
+ # Handle absolute imports (from project root)
219
+ if module_name.startswith('/'):
220
+ resolved = os.path.normpath(os.path.join(project_root, module_name.lstrip('/')))
221
+ if not resolved.endswith(('.js', '.jsx')):
222
+ resolved += '.js'
223
+ if os.path.exists(resolved) and os.path.isfile(resolved):
224
+ return resolved
225
+
226
+ # For node_modules and external packages, we can't resolve without package.json
227
+ # Return None to skip them
228
+ return None
229
+
230
+
231
+ # --- JavaScript Language Support ---
232
+
233
+ class JavaScriptLanguageSupport(BaseLanguageSupport):
234
+ """JavaScript语言支持。"""
235
+
236
+ @property
237
+ def language_name(self) -> str:
238
+ return "javascript"
239
+
240
+ @property
241
+ def file_extensions(self) -> Set[str]:
242
+ return {'.js', '.jsx', '.mjs', '.cjs'}
243
+
244
+ def create_symbol_extractor(self) -> Optional[SymbolExtractor]:
245
+ if not JS_LANGUAGE:
246
+ return None
247
+ try:
248
+ return JavaScriptSymbolExtractor()
249
+ except Exception:
250
+ return None
251
+
252
+ def create_dependency_analyzer(self) -> Optional[DependencyAnalyzer]:
253
+ return JavaScriptDependencyAnalyzer()
254
+
@@ -16,22 +16,72 @@ class PythonSymbolExtractor(SymbolExtractor):
16
16
  def extract_symbols(self, file_path: str, content: str) -> List[Symbol]:
17
17
  symbols: List[Symbol] = []
18
18
  try:
19
+ # 对于超大文件,限制解析内容长度(避免内存和性能问题)
20
+ # 只解析前 50000 行,通常足够提取主要符号
21
+ max_lines = 50000
22
+ lines = content.split('\n')
23
+ if len(lines) > max_lines:
24
+ content = '\n'.join(lines[:max_lines])
25
+
19
26
  tree = ast.parse(content, filename=file_path)
20
27
  self._traverse_node(tree, file_path, symbols, parent_name=None)
21
- except SyntaxError as e:
22
- print(f"Error parsing Python file {file_path}: {e}")
28
+ except SyntaxError:
29
+ # 静默跳过语法错误文件(可能是部分代码片段或测试文件)
30
+ pass
31
+ except Exception:
32
+ # 静默跳过其他解析错误(如内存不足等)
33
+ pass
23
34
  return symbols
24
35
 
25
36
  def _traverse_node(self, node: ast.AST, file_path: str, symbols: List[Symbol], parent_name: Optional[str]):
26
37
  if isinstance(node, ast.FunctionDef):
38
+ # Extract decorators before the function
39
+ if node.decorator_list:
40
+ for decorator in node.decorator_list:
41
+ if isinstance(decorator, ast.Name):
42
+ decorator_symbol = Symbol(
43
+ name=decorator.id,
44
+ kind='decorator',
45
+ file_path=file_path,
46
+ line_start=decorator.lineno,
47
+ line_end=decorator.lineno,
48
+ parent=parent_name,
49
+ )
50
+ symbols.append(decorator_symbol)
27
51
  symbol = self._create_symbol_from_func(node, file_path, parent_name)
28
52
  symbols.append(symbol)
29
53
  parent_name = node.name
30
54
  elif isinstance(node, ast.AsyncFunctionDef):
55
+ # Extract decorators before the async function
56
+ if node.decorator_list:
57
+ for decorator in node.decorator_list:
58
+ if isinstance(decorator, ast.Name):
59
+ decorator_symbol = Symbol(
60
+ name=decorator.id,
61
+ kind='decorator',
62
+ file_path=file_path,
63
+ line_start=decorator.lineno,
64
+ line_end=decorator.lineno,
65
+ parent=parent_name,
66
+ )
67
+ symbols.append(decorator_symbol)
31
68
  symbol = self._create_symbol_from_func(node, file_path, parent_name, is_async=True)
32
69
  symbols.append(symbol)
33
70
  parent_name = node.name
34
71
  elif isinstance(node, ast.ClassDef):
72
+ # Extract decorators before the class
73
+ if node.decorator_list:
74
+ for decorator in node.decorator_list:
75
+ if isinstance(decorator, ast.Name):
76
+ decorator_symbol = Symbol(
77
+ name=decorator.id,
78
+ kind='decorator',
79
+ file_path=file_path,
80
+ line_start=decorator.lineno,
81
+ line_end=decorator.lineno,
82
+ parent=parent_name,
83
+ )
84
+ symbols.append(decorator_symbol)
35
85
  symbol = self._create_symbol_from_class(node, file_path, parent_name)
36
86
  symbols.append(symbol)
37
87
  parent_name = node.name
@@ -30,6 +30,28 @@ RUST_SYMBOL_QUERY = """
30
30
 
31
31
  (mod_item
32
32
  name: (identifier) @module.name)
33
+
34
+ (enum_item
35
+ name: (type_identifier) @enum.name)
36
+
37
+ (union_item
38
+ name: (type_identifier) @union.name)
39
+
40
+ (macro_definition
41
+ name: (identifier) @macro.name)
42
+
43
+ (const_item
44
+ name: (identifier) @const.name)
45
+
46
+ (static_item
47
+ name: (identifier) @static.name)
48
+
49
+ (type_item
50
+ name: (type_identifier) @type.name)
51
+
52
+ (extern_block) @extern
53
+
54
+ (attribute_item) @attribute
33
55
  """
34
56
 
35
57
  # --- Rust Language Setup ---
@@ -59,14 +81,64 @@ class RustSymbolExtractor(TreeSitterExtractor):
59
81
  "trait.name": "trait",
60
82
  "impl.name": "impl",
61
83
  "module.name": "module",
84
+ "enum.name": "enum",
85
+ "union.name": "union",
86
+ "macro.name": "macro",
87
+ "const.name": "const",
88
+ "static.name": "static",
89
+ "type.name": "type",
90
+ "extern": "extern",
91
+ "attribute": "attribute",
62
92
  }
63
93
 
64
94
  symbol_kind = kind_map.get(name)
65
95
  if not symbol_kind:
66
96
  return None
67
97
 
98
+ # 对于 attribute,提取属性内容作为名称
99
+ if symbol_kind == "attribute":
100
+ # 提取属性文本
101
+ attr_text = node.text.decode('utf8').strip()
102
+ # 移除开头的 # 或 #!
103
+ if attr_text.startswith('#!'):
104
+ attr_text = attr_text[2:].strip()
105
+ elif attr_text.startswith('#'):
106
+ attr_text = attr_text[1:].strip()
107
+ # 移除外层的 []
108
+ if attr_text.startswith('[') and attr_text.endswith(']'):
109
+ attr_text = attr_text[1:-1].strip()
110
+
111
+ # 提取属性名称(可能是 test, derive(Debug), cfg(test) 等)
112
+ # 对于简单属性如 #[test],直接使用 test
113
+ # 对于复杂属性如 #[derive(Debug)],使用 derive
114
+ # 对于路径属性如 #[cfg(test)],使用 cfg
115
+ attr_name = attr_text.split('(')[0].split('[')[0].split('=')[0].split(',')[0].strip()
116
+
117
+ # 如果属性名称为空或只包含空白,使用整个属性文本(去掉括号)
118
+ if not attr_name or attr_name == '':
119
+ symbol_name = attr_text if attr_text else "attribute"
120
+ else:
121
+ # 使用属性名称,但保留完整文本用于显示
122
+ symbol_name = attr_name
123
+ elif symbol_kind == "extern":
124
+ # 对于 extern 块,提取 extern 关键字后的内容作为名称
125
+ extern_text = node.text.decode('utf8').strip()
126
+ # 提取 extern "C" 或 extern "Rust" 等
127
+ if '"' in extern_text:
128
+ # 提取引号中的内容
129
+ start = extern_text.find('"')
130
+ end = extern_text.find('"', start + 1)
131
+ if end > start:
132
+ symbol_name = f"extern_{extern_text[start+1:end]}"
133
+ else:
134
+ symbol_name = "extern"
135
+ else:
136
+ symbol_name = "extern"
137
+ else:
138
+ symbol_name = node.text.decode('utf8')
139
+
68
140
  return Symbol(
69
- name=node.text.decode('utf8'),
141
+ name=symbol_name,
70
142
  kind=symbol_kind,
71
143
  file_path=file_path,
72
144
  line_start=node.start_point[0] + 1,