jarvis-ai-assistant 0.3.30__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 (181) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +458 -152
  3. jarvis/jarvis_agent/agent_manager.py +17 -13
  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 +329 -0
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +628 -55
  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 +34 -10
  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 +105 -9
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +20 -22
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  31. jarvis/jarvis_agent/task_analyzer.py +31 -6
  32. jarvis/jarvis_agent/task_manager.py +11 -27
  33. jarvis/jarvis_agent/tool_executor.py +2 -3
  34. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  35. jarvis/jarvis_agent/utils.py +5 -1
  36. jarvis/jarvis_agent/web_bridge.py +189 -0
  37. jarvis/jarvis_agent/web_output_sink.py +53 -0
  38. jarvis/jarvis_agent/web_server.py +786 -0
  39. jarvis/jarvis_c2rust/__init__.py +26 -0
  40. jarvis/jarvis_c2rust/cli.py +575 -0
  41. jarvis/jarvis_c2rust/collector.py +250 -0
  42. jarvis/jarvis_c2rust/constants.py +26 -0
  43. jarvis/jarvis_c2rust/library_replacer.py +1254 -0
  44. jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
  45. jarvis/jarvis_c2rust/loaders.py +207 -0
  46. jarvis/jarvis_c2rust/models.py +28 -0
  47. jarvis/jarvis_c2rust/optimizer.py +2157 -0
  48. jarvis/jarvis_c2rust/scanner.py +1681 -0
  49. jarvis/jarvis_c2rust/transpiler.py +2983 -0
  50. jarvis/jarvis_c2rust/utils.py +385 -0
  51. jarvis/jarvis_code_agent/build_validation_config.py +132 -0
  52. jarvis/jarvis_code_agent/code_agent.py +1371 -220
  53. jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
  54. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
  60. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
  61. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
  62. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
  63. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
  64. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
  65. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
  66. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
  67. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
  68. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  69. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
  70. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  71. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  72. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  73. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  74. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  75. jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
  76. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
  77. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
  78. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
  79. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  80. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  81. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
  82. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
  83. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  84. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
  85. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  86. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
  87. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
  88. jarvis/jarvis_code_agent/lint.py +501 -8
  89. jarvis/jarvis_code_agent/utils.py +141 -0
  90. jarvis/jarvis_code_analysis/code_review.py +493 -584
  91. jarvis/jarvis_data/config_schema.json +128 -12
  92. jarvis/jarvis_git_squash/main.py +4 -5
  93. jarvis/jarvis_git_utils/git_commiter.py +82 -75
  94. jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
  95. jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
  96. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  97. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  98. jarvis/jarvis_methodology/main.py +32 -48
  99. jarvis/jarvis_multi_agent/__init__.py +287 -55
  100. jarvis/jarvis_multi_agent/main.py +36 -4
  101. jarvis/jarvis_platform/base.py +524 -202
  102. jarvis/jarvis_platform/human.py +7 -8
  103. jarvis/jarvis_platform/kimi.py +30 -36
  104. jarvis/jarvis_platform/openai.py +88 -25
  105. jarvis/jarvis_platform/registry.py +26 -10
  106. jarvis/jarvis_platform/tongyi.py +24 -25
  107. jarvis/jarvis_platform/yuanbao.py +32 -43
  108. jarvis/jarvis_platform_manager/main.py +66 -77
  109. jarvis/jarvis_platform_manager/service.py +8 -13
  110. jarvis/jarvis_rag/cli.py +53 -55
  111. jarvis/jarvis_rag/embedding_manager.py +13 -18
  112. jarvis/jarvis_rag/llm_interface.py +8 -9
  113. jarvis/jarvis_rag/query_rewriter.py +10 -21
  114. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  115. jarvis/jarvis_rag/reranker.py +4 -5
  116. jarvis/jarvis_rag/retriever.py +28 -30
  117. jarvis/jarvis_sec/__init__.py +305 -0
  118. jarvis/jarvis_sec/agents.py +143 -0
  119. jarvis/jarvis_sec/analysis.py +276 -0
  120. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  121. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  122. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  123. jarvis/jarvis_sec/cli.py +139 -0
  124. jarvis/jarvis_sec/clustering.py +1439 -0
  125. jarvis/jarvis_sec/file_manager.py +427 -0
  126. jarvis/jarvis_sec/parsers.py +73 -0
  127. jarvis/jarvis_sec/prompts.py +268 -0
  128. jarvis/jarvis_sec/report.py +336 -0
  129. jarvis/jarvis_sec/review.py +453 -0
  130. jarvis/jarvis_sec/status.py +264 -0
  131. jarvis/jarvis_sec/types.py +20 -0
  132. jarvis/jarvis_sec/utils.py +499 -0
  133. jarvis/jarvis_sec/verification.py +848 -0
  134. jarvis/jarvis_sec/workflow.py +226 -0
  135. jarvis/jarvis_smart_shell/main.py +38 -87
  136. jarvis/jarvis_stats/cli.py +2 -2
  137. jarvis/jarvis_stats/stats.py +8 -8
  138. jarvis/jarvis_stats/storage.py +15 -21
  139. jarvis/jarvis_stats/visualizer.py +1 -1
  140. jarvis/jarvis_tools/clear_memory.py +3 -20
  141. jarvis/jarvis_tools/cli/main.py +21 -23
  142. jarvis/jarvis_tools/edit_file.py +1019 -132
  143. jarvis/jarvis_tools/execute_script.py +83 -25
  144. jarvis/jarvis_tools/file_analyzer.py +6 -9
  145. jarvis/jarvis_tools/generate_new_tool.py +14 -21
  146. jarvis/jarvis_tools/lsp_client.py +1552 -0
  147. jarvis/jarvis_tools/methodology.py +2 -3
  148. jarvis/jarvis_tools/read_code.py +1736 -35
  149. jarvis/jarvis_tools/read_symbols.py +140 -0
  150. jarvis/jarvis_tools/read_webpage.py +12 -13
  151. jarvis/jarvis_tools/registry.py +427 -200
  152. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  153. jarvis/jarvis_tools/rewrite_file.py +72 -158
  154. jarvis/jarvis_tools/save_memory.py +3 -15
  155. jarvis/jarvis_tools/search_web.py +18 -18
  156. jarvis/jarvis_tools/sub_agent.py +36 -43
  157. jarvis/jarvis_tools/sub_code_agent.py +25 -26
  158. jarvis/jarvis_tools/virtual_tty.py +55 -33
  159. jarvis/jarvis_utils/clipboard.py +7 -10
  160. jarvis/jarvis_utils/config.py +232 -45
  161. jarvis/jarvis_utils/embedding.py +8 -5
  162. jarvis/jarvis_utils/fzf.py +8 -8
  163. jarvis/jarvis_utils/git_utils.py +225 -36
  164. jarvis/jarvis_utils/globals.py +3 -3
  165. jarvis/jarvis_utils/http.py +1 -1
  166. jarvis/jarvis_utils/input.py +99 -48
  167. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  168. jarvis/jarvis_utils/methodology.py +52 -48
  169. jarvis/jarvis_utils/utils.py +819 -491
  170. jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
  171. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  172. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
  173. jarvis/jarvis_agent/config.py +0 -92
  174. jarvis/jarvis_agent/edit_file_handler.py +0 -296
  175. jarvis/jarvis_platform/ai8.py +0 -332
  176. jarvis/jarvis_tools/ask_user.py +0 -54
  177. jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
  178. jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
  179. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  180. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  181. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,215 @@
1
+ """Go语言支持实现。"""
2
+
3
+ import os
4
+ import re
5
+ from typing import List, Optional, Set
6
+
7
+ from tree_sitter import Language, Node
8
+
9
+ from ..base_language import BaseLanguageSupport
10
+ from ..dependency_analyzer import Dependency, DependencyAnalyzer, DependencyGraph
11
+ from ..file_ignore import filter_walk_dirs
12
+ from ..symbol_extractor import Symbol, SymbolExtractor
13
+ from ..tree_sitter_extractor import TreeSitterExtractor
14
+
15
+
16
+ # --- Go Symbol Query ---
17
+
18
+ GO_SYMBOL_QUERY = """
19
+ (function_declaration
20
+ name: (identifier) @function.name)
21
+
22
+ (method_declaration
23
+ name: (field_identifier) @method.name)
24
+
25
+ (type_declaration
26
+ (type_spec
27
+ name: (type_identifier) @type.name))
28
+
29
+ (type_declaration
30
+ (type_spec
31
+ name: (type_identifier) @interface.name
32
+ type: (interface_type)))
33
+
34
+ (const_declaration) @const
35
+
36
+ (var_declaration) @var
37
+
38
+ (struct_type) @struct
39
+ """
40
+
41
+ # --- Go Language Setup ---
42
+
43
+ try:
44
+ import tree_sitter_go
45
+ GO_LANGUAGE: Optional[Language] = tree_sitter_go.language()
46
+ except (ImportError, Exception):
47
+ GO_LANGUAGE = None
48
+
49
+
50
+ # --- Go Symbol Extractor ---
51
+
52
+ class GoSymbolExtractor(TreeSitterExtractor):
53
+ """Extracts symbols from Go code using tree-sitter."""
54
+
55
+ def __init__(self):
56
+ if not GO_LANGUAGE:
57
+ raise RuntimeError("Go tree-sitter grammar not available.")
58
+ super().__init__(GO_LANGUAGE, GO_SYMBOL_QUERY)
59
+
60
+ def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
61
+ """Maps a tree-sitter capture to a Symbol object."""
62
+ kind_map = {
63
+ "function.name": "function",
64
+ "method.name": "method",
65
+ "type.name": "type",
66
+ "interface.name": "interface",
67
+ "const": "const",
68
+ "var": "var",
69
+ "struct": "struct",
70
+ }
71
+
72
+ symbol_kind = kind_map.get(name)
73
+ if not symbol_kind:
74
+ return None
75
+
76
+ # For const/var/struct, extract the first identifier as name
77
+ if symbol_kind in ("const", "var", "struct"):
78
+ # Try to find the first identifier in the declaration
79
+ node_text = node.text.decode('utf8').strip()
80
+ # Extract first identifier after const/var/struct keyword
81
+ if symbol_kind == "const":
82
+ match = re.search(r'const\s+(\w+)', node_text)
83
+ elif symbol_kind == "var":
84
+ match = re.search(r'var\s+(\w+)', node_text)
85
+ else: # struct
86
+ # For struct, try to find struct name or use a generic name
87
+ match = re.search(r'struct\s+(\w+)', node_text)
88
+
89
+ if match:
90
+ symbol_name = match.group(1)
91
+ else:
92
+ # Fallback: use the kind as name
93
+ symbol_name = symbol_kind
94
+ else:
95
+ symbol_name = node.text.decode('utf8')
96
+
97
+ return Symbol(
98
+ name=symbol_name,
99
+ kind=symbol_kind,
100
+ file_path=file_path,
101
+ line_start=node.start_point[0] + 1,
102
+ line_end=node.end_point[0] + 1,
103
+ )
104
+
105
+
106
+ # --- Go Dependency Analyzer ---
107
+
108
+ class GoDependencyAnalyzer(DependencyAnalyzer):
109
+ """Analyzes Go import dependencies."""
110
+
111
+ def analyze_imports(self, file_path: str, content: str) -> List[Dependency]:
112
+ """Analyzes Go import statements."""
113
+ dependencies: List[Dependency] = []
114
+
115
+ # Match import statements
116
+ # Format: import "package" or import ( "package1" "package2" )
117
+ # Also: import alias "package"
118
+ re.compile(
119
+ r'import\s+(?:\(([^)]+)\)|(?:"([^"]+)"|`([^`]+)`)|(\w+)\s+(?:"([^"]+)"|`([^`]+)`)))',
120
+ re.MULTILINE
121
+ )
122
+
123
+ # Handle single import: import "package"
124
+ single_import = re.compile(r'import\s+(?:"([^"]+)"|`([^`]+)`|(\w+)\s+(?:"([^"]+)"|`([^`]+)`))')
125
+
126
+ # Handle block import: import ( ... )
127
+ block_import = re.compile(r'import\s*\(([^)]+)\)', re.DOTALL)
128
+
129
+ # Try block import first
130
+ block_match = block_import.search(content)
131
+ if block_match:
132
+ block_content = block_match.group(1)
133
+ for line in block_content.split('\n'):
134
+ line = line.strip()
135
+ if not line or line.startswith('//'):
136
+ continue
137
+ # Extract package path
138
+ pkg_match = re.search(r'(?:"([^"]+)"|`([^`]+)`|(\w+)\s+(?:"([^"]+)"|`([^`]+)`))', line)
139
+ if pkg_match:
140
+ pkg = pkg_match.group(1) or pkg_match.group(2) or pkg_match.group(4) or pkg_match.group(5)
141
+ alias = pkg_match.group(3)
142
+ line_num = content[:block_match.start()].count('\n') + line.split('\n')[0].count('\n') + 1
143
+ dependencies.append(Dependency(
144
+ from_module=pkg,
145
+ imported_symbol=alias,
146
+ file_path=file_path,
147
+ line=line_num,
148
+ ))
149
+ else:
150
+ # Try single import
151
+ for match in single_import.finditer(content):
152
+ pkg = match.group(1) or match.group(2) or match.group(4) or match.group(5)
153
+ alias = match.group(3)
154
+ line_num = content[:match.start()].count('\n') + 1
155
+ dependencies.append(Dependency(
156
+ from_module=pkg,
157
+ imported_symbol=alias,
158
+ file_path=file_path,
159
+ line=line_num,
160
+ ))
161
+
162
+ return dependencies
163
+
164
+ def build_dependency_graph(self, project_root: str) -> DependencyGraph:
165
+ """Builds a dependency graph for a Go project."""
166
+ graph = DependencyGraph()
167
+
168
+ for root, dirs, files in os.walk(project_root):
169
+ dirs[:] = filter_walk_dirs(dirs)
170
+
171
+ for file in files:
172
+ if not file.endswith('.go'):
173
+ continue
174
+
175
+ file_path = os.path.join(root, file)
176
+ try:
177
+ with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
178
+ content = f.read()
179
+
180
+ dependencies = self.analyze_imports(file_path, content)
181
+ for dep in dependencies:
182
+ # For Go, we can resolve to vendor or standard library
183
+ # For now, just track the module name
184
+ # In a real implementation, you'd resolve using go.mod
185
+ pass
186
+ except Exception:
187
+ continue
188
+
189
+ return graph
190
+
191
+ def _is_source_file(self, file_path: str) -> bool:
192
+ """Check if a file is a Go source file."""
193
+ return file_path.endswith('.go')
194
+
195
+
196
+ class GoLanguageSupport(BaseLanguageSupport):
197
+ """Go语言支持类。"""
198
+
199
+ @property
200
+ def language_name(self) -> str:
201
+ return 'go'
202
+
203
+ @property
204
+ def file_extensions(self) -> Set[str]:
205
+ return {'.go'}
206
+
207
+ def create_symbol_extractor(self) -> Optional[SymbolExtractor]:
208
+ try:
209
+ return GoSymbolExtractor()
210
+ except RuntimeError:
211
+ return None
212
+
213
+ def create_dependency_analyzer(self) -> Optional[DependencyAnalyzer]:
214
+ return GoDependencyAnalyzer()
215
+
@@ -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
+