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,252 @@
1
+ import json
2
+ import os
3
+ from dataclasses import dataclass
4
+ from typing import Dict, List, Optional
5
+
6
+
7
+ @dataclass
8
+ class Symbol:
9
+ """表示代码中的单个符号。"""
10
+ name: str
11
+ kind: str # 例如:'function'(函数)、'class'(类)、'variable'(变量)、'import'(导入)
12
+ file_path: str
13
+ line_start: int
14
+ line_end: int
15
+ signature: Optional[str] = None
16
+ docstring: Optional[str] = None
17
+ # 根据需要添加更多字段,例如父作用域
18
+ parent: Optional[str] = None
19
+ # 定义位置(对于引用/调用,指向符号定义的位置)
20
+ definition_location: Optional['Symbol'] = None # 指向定义Symbol的引用
21
+ is_definition: bool = False # 如果此符号是定义则为True,如果是引用/调用则为False
22
+
23
+
24
+ class SymbolTable:
25
+ """存储并提供对项目中符号的访问。"""
26
+
27
+ def __init__(self, cache_dir: Optional[str] = None):
28
+ # 按名称存储符号的字典,用于快速查找
29
+ # 一个符号名可能出现在多个文件中,因此使用列表存储
30
+ self.symbols_by_name: Dict[str, List[Symbol]] = {}
31
+ # 按文件存储符号的字典
32
+ self.symbols_by_file: Dict[str, List[Symbol]] = {}
33
+ # 跟踪文件修改时间用于缓存失效
34
+ self._file_mtimes: Dict[str, float] = {}
35
+ # 持久化存储的缓存目录
36
+ self.cache_dir = cache_dir or ".jarvis/symbol_cache"
37
+ # 如果可用则加载缓存数据
38
+ self._load_from_cache()
39
+
40
+ def _get_cache_file(self) -> str:
41
+ """获取缓存文件路径。"""
42
+ return os.path.join(self.cache_dir, "symbol_table.json")
43
+
44
+ def _load_from_cache(self):
45
+ """从缓存文件加载符号表数据。"""
46
+ cache_file = self._get_cache_file()
47
+ if os.path.exists(cache_file):
48
+ try:
49
+ with open(cache_file, 'r', encoding='utf-8') as f:
50
+ data = json.load(f)
51
+ # 将JSON数据转换回Symbol对象
52
+ self.symbols_by_name = self._deserialize_symbols(data.get('symbols_by_name', {}))
53
+ self.symbols_by_file = self._deserialize_symbols(data.get('symbols_by_file', {}))
54
+ # 加载文件修改时间
55
+ self._file_mtimes = data.get('file_mtimes', {})
56
+ except Exception:
57
+ # 如果缓存加载失败,则从空表开始
58
+ pass
59
+
60
+ def _save_to_cache(self):
61
+ """将符号表数据保存到缓存文件。"""
62
+ try:
63
+ # 确保缓存目录存在
64
+ os.makedirs(self.cache_dir, exist_ok=True)
65
+ cache_file = self._get_cache_file()
66
+
67
+ # 保存前更新文件修改时间
68
+ self._update_file_mtimes()
69
+
70
+ # 序列化符号以便JSON存储
71
+ data = {
72
+ 'symbols_by_name': self._serialize_symbols(self.symbols_by_name),
73
+ 'symbols_by_file': self._serialize_symbols(self.symbols_by_file),
74
+ 'file_mtimes': self._file_mtimes
75
+ }
76
+
77
+ with open(cache_file, 'w', encoding='utf-8') as f:
78
+ json.dump(data, f, indent=2, ensure_ascii=False)
79
+ except Exception:
80
+ # 如果缓存保存失败,则继续而不缓存
81
+ pass
82
+
83
+ def _serialize_symbols(self, symbol_dict: Dict[str, List[Symbol]]) -> Dict[str, List[dict]]:
84
+ """将Symbol对象转换为可序列化的字典。"""
85
+ serialized = {}
86
+ for key, symbols in symbol_dict.items():
87
+ serialized[key] = [self._symbol_to_dict(symbol) for symbol in symbols]
88
+ return serialized
89
+
90
+ def _deserialize_symbols(self, symbol_dict: Dict[str, List[dict]]) -> Dict[str, List[Symbol]]:
91
+ """将序列化的字典转换回Symbol对象。"""
92
+ deserialized = {}
93
+ for key, symbol_data_list in symbol_dict.items():
94
+ deserialized[key] = [self._dict_to_symbol(data) for data in symbol_data_list]
95
+ return deserialized
96
+
97
+ def _symbol_to_dict(self, symbol: Symbol) -> dict:
98
+ """将Symbol对象转换为字典。"""
99
+ result = {
100
+ 'name': symbol.name,
101
+ 'kind': symbol.kind,
102
+ 'file_path': symbol.file_path,
103
+ 'line_start': symbol.line_start,
104
+ 'line_end': symbol.line_end,
105
+ 'signature': symbol.signature,
106
+ 'docstring': symbol.docstring,
107
+ 'parent': symbol.parent,
108
+ 'is_definition': getattr(symbol, 'is_definition', False),
109
+ }
110
+ # 序列化定义位置(只保存基本信息,避免循环引用)
111
+ if hasattr(symbol, 'definition_location') and symbol.definition_location:
112
+ result['definition_location'] = {
113
+ 'file_path': symbol.definition_location.file_path,
114
+ 'line_start': symbol.definition_location.line_start,
115
+ 'line_end': symbol.definition_location.line_end,
116
+ 'name': symbol.definition_location.name,
117
+ }
118
+ return result
119
+
120
+ def _dict_to_symbol(self, data: dict) -> Symbol:
121
+ """将字典转换回Symbol对象。"""
122
+ symbol = Symbol(
123
+ name=data['name'],
124
+ kind=data['kind'],
125
+ file_path=data['file_path'],
126
+ line_start=data['line_start'],
127
+ line_end=data['line_end'],
128
+ signature=data.get('signature'),
129
+ docstring=data.get('docstring'),
130
+ parent=data.get('parent'),
131
+ is_definition=data.get('is_definition', False),
132
+ )
133
+ # 恢复定义位置(创建临时 Symbol 对象)
134
+ if 'definition_location' in data and data['definition_location']:
135
+ def_loc = data['definition_location']
136
+ symbol.definition_location = Symbol(
137
+ name=def_loc['name'],
138
+ kind='', # 未知类型
139
+ file_path=def_loc['file_path'],
140
+ line_start=def_loc['line_start'],
141
+ line_end=def_loc['line_end'],
142
+ )
143
+ return symbol
144
+
145
+ def add_symbol(self, symbol: Symbol, save_to_cache: bool = False):
146
+ """向表中添加符号。
147
+
148
+ 参数:
149
+ symbol: 要添加的符号
150
+ save_to_cache: 如果为True,立即保存到缓存。默认为False以提高性能。
151
+ 批量操作后使用save_cache()一次性保存所有符号。
152
+ """
153
+ if symbol.name not in self.symbols_by_name:
154
+ self.symbols_by_name[symbol.name] = []
155
+ self.symbols_by_name[symbol.name].append(symbol)
156
+
157
+ if symbol.file_path not in self.symbols_by_file:
158
+ self.symbols_by_file[symbol.file_path] = []
159
+ self.symbols_by_file[symbol.file_path].append(symbol)
160
+
161
+ # 仅在明确请求时保存到缓存(出于性能考虑)
162
+ if save_to_cache:
163
+ self._save_to_cache()
164
+
165
+ def save_cache(self):
166
+ """将整个符号表保存到缓存。批量操作后调用此方法。"""
167
+ self._save_to_cache()
168
+
169
+ def find_symbol(self, name: str, file_path: Optional[str] = None) -> List[Symbol]:
170
+ """
171
+ 通过名称查找符号。
172
+ 如果提供了file_path,则搜索仅限于该文件。
173
+ """
174
+ if file_path:
175
+ return [
176
+ s for s in self.get_file_symbols(file_path) if s.name == name
177
+ ]
178
+ return self.symbols_by_name.get(name, [])
179
+
180
+ def get_file_symbols(self, file_path: str) -> List[Symbol]:
181
+ """获取特定文件中的所有符号。"""
182
+ return self.symbols_by_file.get(file_path, [])
183
+
184
+ def clear_file_symbols(self, file_path: str):
185
+ """移除与特定文件关联的所有符号。"""
186
+ if file_path in self.symbols_by_file:
187
+ symbols_to_remove = self.symbols_by_file.pop(file_path)
188
+ for symbol in symbols_to_remove:
189
+ if symbol.name in self.symbols_by_name:
190
+ self.symbols_by_name[symbol.name] = [
191
+ s for s in self.symbols_by_name[symbol.name]
192
+ if s.file_path != file_path
193
+ ]
194
+ if not self.symbols_by_name[symbol.name]:
195
+ del self.symbols_by_name[symbol.name]
196
+
197
+ # 移除文件修改时间跟踪
198
+ if file_path in self._file_mtimes:
199
+ del self._file_mtimes[file_path]
200
+
201
+ # 清除后保存到缓存
202
+ self._save_to_cache()
203
+
204
+ def _update_file_mtimes(self):
205
+ """更新所有跟踪文件的修改时间。"""
206
+ for file_path in list(self.symbols_by_file.keys()):
207
+ if os.path.exists(file_path):
208
+ try:
209
+ self._file_mtimes[file_path] = os.path.getmtime(file_path)
210
+ except Exception:
211
+ # 如果无法获取修改时间,则从跟踪中移除
212
+ self._file_mtimes.pop(file_path, None)
213
+
214
+ def is_file_stale(self, file_path: str) -> bool:
215
+ """检查文件自缓存后是否已被修改。
216
+
217
+ 参数:
218
+ file_path: 要检查的文件路径
219
+
220
+ 返回:
221
+ 如果文件比缓存新则为True,否则为False
222
+ """
223
+ if file_path not in self.symbols_by_file:
224
+ # 文件不在缓存中,视为已过期(需要加载)
225
+ return True
226
+
227
+ if file_path not in self._file_mtimes:
228
+ # 没有记录修改时间,视为已过期
229
+ return True
230
+
231
+ if not os.path.exists(file_path):
232
+ # 文件不存在,不算过期(将由clear_file_symbols处理)
233
+ return False
234
+
235
+ try:
236
+ current_mtime = os.path.getmtime(file_path)
237
+ cached_mtime = self._file_mtimes.get(file_path, 0)
238
+ return current_mtime > cached_mtime
239
+ except Exception:
240
+ # 如果无法获取修改时间,则假定未过期
241
+ return False
242
+
243
+
244
+ class SymbolExtractor:
245
+ """从源代码文件中提取符号。"""
246
+
247
+ def extract_symbols(self, file_path: str, content: str) -> List[Symbol]:
248
+ """
249
+ 从代码中提取符号(函数、类、变量等)。
250
+ 此方法应由特定语言的子类实现。
251
+ """
252
+ raise NotImplementedError("Subclasses must implement this method.")
@@ -0,0 +1,58 @@
1
+ from abc import abstractmethod
2
+ from typing import List, Optional
3
+
4
+ from tree_sitter import Language, Parser, Node, Query, QueryCursor
5
+
6
+ from .symbol_extractor import Symbol, SymbolExtractor
7
+
8
+
9
+ class TreeSitterExtractor(SymbolExtractor):
10
+ """
11
+ A generic symbol extractor that uses tree-sitter for parsing.
12
+ Subclasses must provide the language-specific details, such as the
13
+ tree-sitter Language object and the symbol query.
14
+ """
15
+
16
+ def __init__(self, language: Language, symbol_query: str):
17
+ # 如果传入的是 PyCapsule,需要转换为 Language 对象
18
+ if not isinstance(language, Language):
19
+ language = Language(language)
20
+ self.language = language
21
+ self.parser = Parser()
22
+ # 使用 language 属性而不是 set_language 方法
23
+ self.parser.language = self.language
24
+ self.symbol_query = symbol_query
25
+
26
+ def extract_symbols(self, file_path: str, content: str) -> List[Symbol]:
27
+ """
28
+ Parses the code with tree-sitter and extracts symbols based on the query.
29
+ """
30
+ try:
31
+ tree = self.parser.parse(bytes(content, "utf8"))
32
+ # 使用 Query 构造函数(新 API)
33
+ query = Query(self.language, self.symbol_query)
34
+ # 使用 QueryCursor 执行查询
35
+ cursor = QueryCursor(query)
36
+ matches = cursor.matches(tree.root_node)
37
+
38
+ symbols = []
39
+ # matches 返回格式: [(pattern_index, {capture_name: [nodes]})]
40
+ for pattern_index, captures_dict in matches:
41
+ for capture_name, nodes in captures_dict.items():
42
+ for node in nodes:
43
+ symbol = self._create_symbol_from_capture(node, capture_name, file_path)
44
+ if symbol:
45
+ symbols.append(symbol)
46
+ return symbols
47
+ except Exception as e:
48
+ print(f"Error extracting symbols from {file_path} with tree-sitter: {e}")
49
+ return []
50
+
51
+ @abstractmethod
52
+ def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
53
+ """
54
+ Creates a Symbol object from a tree-sitter query capture.
55
+ This method must be implemented by subclasses to map capture names
56
+ (e.g., "function.name") to Symbol attributes.
57
+ """
58
+ raise NotImplementedError