jarvis-ai-assistant 0.7.16__py3-none-any.whl → 1.0.2__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 (279) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +567 -222
  3. jarvis/jarvis_agent/agent_manager.py +19 -12
  4. jarvis/jarvis_agent/builtin_input_handler.py +79 -11
  5. jarvis/jarvis_agent/config_editor.py +7 -2
  6. jarvis/jarvis_agent/event_bus.py +24 -13
  7. jarvis/jarvis_agent/events.py +19 -1
  8. jarvis/jarvis_agent/file_context_handler.py +67 -64
  9. jarvis/jarvis_agent/file_methodology_manager.py +38 -24
  10. jarvis/jarvis_agent/jarvis.py +186 -114
  11. jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
  12. jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
  13. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
  14. jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
  15. jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
  16. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
  17. jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
  18. jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
  19. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
  20. jarvis/jarvis_agent/language_support_info.py +250 -219
  21. jarvis/jarvis_agent/main.py +19 -23
  22. jarvis/jarvis_agent/memory_manager.py +9 -6
  23. jarvis/jarvis_agent/methodology_share_manager.py +21 -15
  24. jarvis/jarvis_agent/output_handler.py +4 -2
  25. jarvis/jarvis_agent/prompt_builder.py +7 -6
  26. jarvis/jarvis_agent/prompt_manager.py +113 -8
  27. jarvis/jarvis_agent/prompts.py +317 -85
  28. jarvis/jarvis_agent/protocols.py +5 -2
  29. jarvis/jarvis_agent/run_loop.py +192 -32
  30. jarvis/jarvis_agent/session_manager.py +7 -3
  31. jarvis/jarvis_agent/share_manager.py +23 -13
  32. jarvis/jarvis_agent/shell_input_handler.py +12 -8
  33. jarvis/jarvis_agent/stdio_redirect.py +25 -26
  34. jarvis/jarvis_agent/task_analyzer.py +29 -23
  35. jarvis/jarvis_agent/task_list.py +869 -0
  36. jarvis/jarvis_agent/task_manager.py +26 -23
  37. jarvis/jarvis_agent/tool_executor.py +6 -5
  38. jarvis/jarvis_agent/tool_share_manager.py +24 -14
  39. jarvis/jarvis_agent/user_interaction.py +3 -3
  40. jarvis/jarvis_agent/utils.py +9 -1
  41. jarvis/jarvis_agent/web_bridge.py +37 -17
  42. jarvis/jarvis_agent/web_output_sink.py +5 -2
  43. jarvis/jarvis_agent/web_server.py +165 -36
  44. jarvis/jarvis_c2rust/__init__.py +1 -1
  45. jarvis/jarvis_c2rust/cli.py +260 -141
  46. jarvis/jarvis_c2rust/collector.py +37 -18
  47. jarvis/jarvis_c2rust/constants.py +60 -0
  48. jarvis/jarvis_c2rust/library_replacer.py +242 -1010
  49. jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
  50. jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
  51. jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
  52. jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
  53. jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
  54. jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
  55. jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
  56. jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
  57. jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
  58. jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
  59. jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
  60. jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
  61. jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
  62. jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
  63. jarvis/jarvis_c2rust/loaders.py +28 -10
  64. jarvis/jarvis_c2rust/models.py +5 -2
  65. jarvis/jarvis_c2rust/optimizer.py +192 -1974
  66. jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
  67. jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
  68. jarvis/jarvis_c2rust/optimizer_config.py +49 -0
  69. jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
  70. jarvis/jarvis_c2rust/optimizer_options.py +48 -0
  71. jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
  72. jarvis/jarvis_c2rust/optimizer_report.py +52 -0
  73. jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
  74. jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
  75. jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
  76. jarvis/jarvis_c2rust/scanner.py +229 -166
  77. jarvis/jarvis_c2rust/transpiler.py +531 -2732
  78. jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
  79. jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
  80. jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
  81. jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
  82. jarvis/jarvis_c2rust/transpiler_config.py +178 -0
  83. jarvis/jarvis_c2rust/transpiler_context.py +122 -0
  84. jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
  85. jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
  86. jarvis/jarvis_c2rust/transpiler_git.py +163 -0
  87. jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
  88. jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
  89. jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
  90. jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
  91. jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
  92. jarvis/jarvis_c2rust/utils.py +269 -79
  93. jarvis/jarvis_code_agent/after_change.py +233 -0
  94. jarvis/jarvis_code_agent/build_validation_config.py +37 -30
  95. jarvis/jarvis_code_agent/builtin_rules.py +68 -0
  96. jarvis/jarvis_code_agent/code_agent.py +976 -1517
  97. jarvis/jarvis_code_agent/code_agent_build.py +227 -0
  98. jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
  99. jarvis/jarvis_code_agent/code_agent_git.py +525 -0
  100. jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
  101. jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
  102. jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
  103. jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
  104. jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
  105. jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
  106. jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
  107. jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
  108. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
  109. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
  110. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
  111. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
  112. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
  113. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
  114. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
  115. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
  116. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
  117. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
  118. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
  119. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
  120. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
  121. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
  122. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
  123. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
  124. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
  125. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
  126. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
  127. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
  128. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
  129. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
  130. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
  131. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
  132. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
  133. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
  134. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
  135. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
  136. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
  137. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
  138. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
  139. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
  140. jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
  141. jarvis/jarvis_code_agent/lint.py +223 -524
  142. jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
  143. jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
  144. jarvis/jarvis_code_agent/rules/code_review.md +115 -0
  145. jarvis/jarvis_code_agent/rules/documentation.md +165 -0
  146. jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
  147. jarvis/jarvis_code_agent/rules/performance.md +158 -0
  148. jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
  149. jarvis/jarvis_code_agent/rules/security.md +160 -0
  150. jarvis/jarvis_code_agent/rules/tdd.md +78 -0
  151. jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
  152. jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
  153. jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
  154. jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
  155. jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
  156. jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
  157. jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
  158. jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
  159. jarvis/jarvis_code_agent/utils.py +36 -26
  160. jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
  161. jarvis/jarvis_code_analysis/code_review.py +64 -33
  162. jarvis/jarvis_data/config_schema.json +285 -192
  163. jarvis/jarvis_git_squash/main.py +8 -6
  164. jarvis/jarvis_git_utils/git_commiter.py +53 -76
  165. jarvis/jarvis_mcp/__init__.py +5 -2
  166. jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
  167. jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
  168. jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
  169. jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
  170. jarvis/jarvis_methodology/main.py +48 -39
  171. jarvis/jarvis_multi_agent/__init__.py +56 -23
  172. jarvis/jarvis_multi_agent/main.py +15 -18
  173. jarvis/jarvis_platform/base.py +179 -111
  174. jarvis/jarvis_platform/human.py +27 -16
  175. jarvis/jarvis_platform/kimi.py +52 -45
  176. jarvis/jarvis_platform/openai.py +101 -40
  177. jarvis/jarvis_platform/registry.py +51 -33
  178. jarvis/jarvis_platform/tongyi.py +68 -38
  179. jarvis/jarvis_platform/yuanbao.py +59 -43
  180. jarvis/jarvis_platform_manager/main.py +68 -76
  181. jarvis/jarvis_platform_manager/service.py +24 -14
  182. jarvis/jarvis_rag/README_CONFIG.md +314 -0
  183. jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
  184. jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
  185. jarvis/jarvis_rag/__init__.py +57 -4
  186. jarvis/jarvis_rag/cache.py +3 -1
  187. jarvis/jarvis_rag/cli.py +48 -68
  188. jarvis/jarvis_rag/embedding_interface.py +39 -0
  189. jarvis/jarvis_rag/embedding_manager.py +7 -230
  190. jarvis/jarvis_rag/embeddings/__init__.py +41 -0
  191. jarvis/jarvis_rag/embeddings/base.py +114 -0
  192. jarvis/jarvis_rag/embeddings/cohere.py +66 -0
  193. jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
  194. jarvis/jarvis_rag/embeddings/local.py +260 -0
  195. jarvis/jarvis_rag/embeddings/openai.py +62 -0
  196. jarvis/jarvis_rag/embeddings/registry.py +293 -0
  197. jarvis/jarvis_rag/llm_interface.py +8 -6
  198. jarvis/jarvis_rag/query_rewriter.py +8 -9
  199. jarvis/jarvis_rag/rag_pipeline.py +61 -52
  200. jarvis/jarvis_rag/reranker.py +7 -75
  201. jarvis/jarvis_rag/reranker_interface.py +32 -0
  202. jarvis/jarvis_rag/rerankers/__init__.py +41 -0
  203. jarvis/jarvis_rag/rerankers/base.py +109 -0
  204. jarvis/jarvis_rag/rerankers/cohere.py +67 -0
  205. jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
  206. jarvis/jarvis_rag/rerankers/jina.py +79 -0
  207. jarvis/jarvis_rag/rerankers/local.py +89 -0
  208. jarvis/jarvis_rag/rerankers/registry.py +293 -0
  209. jarvis/jarvis_rag/retriever.py +58 -43
  210. jarvis/jarvis_sec/__init__.py +66 -141
  211. jarvis/jarvis_sec/agents.py +21 -17
  212. jarvis/jarvis_sec/analysis.py +80 -33
  213. jarvis/jarvis_sec/checkers/__init__.py +7 -13
  214. jarvis/jarvis_sec/checkers/c_checker.py +356 -164
  215. jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
  216. jarvis/jarvis_sec/cli.py +43 -21
  217. jarvis/jarvis_sec/clustering.py +430 -272
  218. jarvis/jarvis_sec/file_manager.py +99 -55
  219. jarvis/jarvis_sec/parsers.py +9 -6
  220. jarvis/jarvis_sec/prompts.py +4 -3
  221. jarvis/jarvis_sec/report.py +44 -22
  222. jarvis/jarvis_sec/review.py +180 -107
  223. jarvis/jarvis_sec/status.py +50 -41
  224. jarvis/jarvis_sec/types.py +3 -0
  225. jarvis/jarvis_sec/utils.py +160 -83
  226. jarvis/jarvis_sec/verification.py +411 -181
  227. jarvis/jarvis_sec/workflow.py +132 -21
  228. jarvis/jarvis_smart_shell/main.py +28 -41
  229. jarvis/jarvis_stats/cli.py +14 -12
  230. jarvis/jarvis_stats/stats.py +28 -19
  231. jarvis/jarvis_stats/storage.py +14 -8
  232. jarvis/jarvis_stats/visualizer.py +12 -7
  233. jarvis/jarvis_tools/base.py +5 -2
  234. jarvis/jarvis_tools/clear_memory.py +13 -9
  235. jarvis/jarvis_tools/cli/main.py +23 -18
  236. jarvis/jarvis_tools/edit_file.py +572 -873
  237. jarvis/jarvis_tools/execute_script.py +10 -7
  238. jarvis/jarvis_tools/file_analyzer.py +7 -8
  239. jarvis/jarvis_tools/meta_agent.py +287 -0
  240. jarvis/jarvis_tools/methodology.py +5 -3
  241. jarvis/jarvis_tools/read_code.py +305 -1438
  242. jarvis/jarvis_tools/read_symbols.py +50 -17
  243. jarvis/jarvis_tools/read_webpage.py +19 -18
  244. jarvis/jarvis_tools/registry.py +435 -156
  245. jarvis/jarvis_tools/retrieve_memory.py +16 -11
  246. jarvis/jarvis_tools/save_memory.py +8 -6
  247. jarvis/jarvis_tools/search_web.py +31 -31
  248. jarvis/jarvis_tools/sub_agent.py +32 -28
  249. jarvis/jarvis_tools/sub_code_agent.py +44 -60
  250. jarvis/jarvis_tools/task_list_manager.py +1811 -0
  251. jarvis/jarvis_tools/virtual_tty.py +29 -19
  252. jarvis/jarvis_utils/__init__.py +4 -0
  253. jarvis/jarvis_utils/builtin_replace_map.py +2 -1
  254. jarvis/jarvis_utils/clipboard.py +9 -8
  255. jarvis/jarvis_utils/collections.py +331 -0
  256. jarvis/jarvis_utils/config.py +699 -194
  257. jarvis/jarvis_utils/dialogue_recorder.py +294 -0
  258. jarvis/jarvis_utils/embedding.py +6 -3
  259. jarvis/jarvis_utils/file_processors.py +7 -1
  260. jarvis/jarvis_utils/fzf.py +9 -3
  261. jarvis/jarvis_utils/git_utils.py +71 -42
  262. jarvis/jarvis_utils/globals.py +116 -32
  263. jarvis/jarvis_utils/http.py +6 -2
  264. jarvis/jarvis_utils/input.py +318 -83
  265. jarvis/jarvis_utils/jsonnet_compat.py +119 -104
  266. jarvis/jarvis_utils/methodology.py +37 -28
  267. jarvis/jarvis_utils/output.py +201 -44
  268. jarvis/jarvis_utils/utils.py +986 -628
  269. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
  270. jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
  271. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
  272. jarvis/jarvis_tools/generate_new_tool.py +0 -205
  273. jarvis/jarvis_tools/lsp_client.py +0 -1552
  274. jarvis/jarvis_tools/rewrite_file.py +0 -105
  275. jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -2,18 +2,28 @@
2
2
 
3
3
  import os
4
4
  import re
5
- from typing import List, Optional, Set
5
+ from typing import List
6
+ from typing import Optional
7
+ from typing import Set
8
+ from typing import cast
6
9
 
7
10
  from ..base_language import BaseLanguageSupport
8
- from ..dependency_analyzer import Dependency, DependencyAnalyzer, DependencyGraph
11
+ from ..dependency_analyzer import Dependency
12
+ from ..dependency_analyzer import DependencyAnalyzer
13
+ from ..dependency_analyzer import DependencyGraph
9
14
  from ..file_ignore import filter_walk_dirs
10
- from ..symbol_extractor import Symbol, SymbolExtractor
15
+ from ..symbol_extractor import Symbol
16
+ from ..symbol_extractor import SymbolExtractor
11
17
  from ..tree_sitter_extractor import TreeSitterExtractor
12
18
 
13
19
  try:
14
- from tree_sitter import Language, Node
15
20
  import tree_sitter_javascript
16
- JS_LANGUAGE: Optional[Language] = tree_sitter_javascript.language()
21
+ from tree_sitter import Language
22
+ from tree_sitter import Node
23
+
24
+ JS_LANGUAGE: Optional[Language] = cast(
25
+ Optional[Language], tree_sitter_javascript.language()
26
+ )
17
27
  except (ImportError, Exception):
18
28
  JS_LANGUAGE = None
19
29
 
@@ -57,13 +67,12 @@ class JavaScriptSymbolExtractor(TreeSitterExtractor):
57
67
  if not JS_LANGUAGE:
58
68
  raise RuntimeError("JavaScript tree-sitter grammar not available.")
59
69
  # 如果传入的是 PyCapsule,需要转换为 Language 对象
60
- if not isinstance(JS_LANGUAGE, Language):
61
- lang = Language(JS_LANGUAGE)
62
- else:
63
- lang = JS_LANGUAGE
70
+ lang = Language(JS_LANGUAGE)
64
71
  super().__init__(lang, JS_SYMBOL_QUERY)
65
72
 
66
- def _create_symbol_from_capture(self, node: Node, name: str, file_path: str) -> Optional[Symbol]:
73
+ def _create_symbol_from_capture(
74
+ self, node: Node, name: str, file_path: str
75
+ ) -> Optional[Symbol]:
67
76
  """Maps a tree-sitter capture to a Symbol object."""
68
77
  kind_map = {
69
78
  "function.name": "function",
@@ -73,7 +82,7 @@ class JavaScriptSymbolExtractor(TreeSitterExtractor):
73
82
  "class.name": "class",
74
83
  "variable.name": "variable",
75
84
  }
76
-
85
+
77
86
  symbol_kind = kind_map.get(name)
78
87
  if not symbol_kind:
79
88
  return None
@@ -83,10 +92,14 @@ class JavaScriptSymbolExtractor(TreeSitterExtractor):
83
92
  symbol_name = "<anonymous_arrow_function>"
84
93
  elif name == "generator.name":
85
94
  # Generator functions are also functions
86
- symbol_name = node.text.decode('utf8')
95
+ if node.text is None:
96
+ return None
97
+ symbol_name = node.text.decode("utf8")
87
98
  else:
88
- symbol_name = node.text.decode('utf8')
89
-
99
+ if node.text is None:
100
+ return None
101
+ symbol_name = node.text.decode("utf8")
102
+
90
103
  if not symbol_name:
91
104
  return None
92
105
 
@@ -101,31 +114,32 @@ class JavaScriptSymbolExtractor(TreeSitterExtractor):
101
114
 
102
115
  # --- JavaScript Dependency Analyzer ---
103
116
 
117
+
104
118
  class JavaScriptDependencyAnalyzer(DependencyAnalyzer):
105
119
  """Analyzes JavaScript import/require dependencies."""
106
120
 
107
121
  def analyze_imports(self, file_path: str, content: str) -> List[Dependency]:
108
122
  """Analyzes JavaScript import and require statements."""
109
123
  dependencies: List[Dependency] = []
110
-
124
+
111
125
  # ES6 import statements
112
126
  # import module from 'path'
113
127
  # import { symbol } from 'path'
114
128
  # import * as alias from 'path'
115
129
  import_pattern = re.compile(
116
130
  r'import\s+(?:(?:\*\s+as\s+(\w+)|(\{[^}]*\})|(\w+))\s+from\s+)?["\']([^"\']+)["\']',
117
- re.MULTILINE
131
+ re.MULTILINE,
118
132
  )
119
-
133
+
120
134
  # CommonJS require statements
121
135
  # const module = require('path')
122
136
  # require('path')
123
137
  require_pattern = re.compile(
124
138
  r'(?:const|let|var)\s+\w+\s*=\s*require\(["\']([^"\']+)["\']\)|require\(["\']([^"\']+)["\']\)',
125
- re.MULTILINE
139
+ re.MULTILINE,
126
140
  )
127
-
128
- for line_num, line in enumerate(content.split('\n'), start=1):
141
+
142
+ for line_num, line in enumerate(content.split("\n"), start=1):
129
143
  # Check for ES6 imports
130
144
  import_match = import_pattern.search(line)
131
145
  if import_match:
@@ -134,95 +148,111 @@ class JavaScriptDependencyAnalyzer(DependencyAnalyzer):
134
148
  # Extract imported symbols if any
135
149
  symbols_group = import_match.group(2) or import_match.group(1)
136
150
  imported_symbol = None
137
- if symbols_group and symbols_group.startswith('{'):
151
+ if symbols_group and symbols_group.startswith("{"):
138
152
  # Extract first symbol from { symbol1, symbol2 }
139
- symbol_match = re.search(r'\{([^}]+)\}', symbols_group)
153
+ symbol_match = re.search(r"\{([^}]+)\}", symbols_group)
140
154
  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
-
155
+ first_symbol = symbol_match.group(1).split(",")[0].strip()
156
+ imported_symbol = first_symbol.split(" as ")[0].strip()
157
+
158
+ dependencies.append(
159
+ Dependency(
160
+ from_module=module_path,
161
+ imported_symbol=imported_symbol,
162
+ file_path=file_path,
163
+ line=line_num,
164
+ )
165
+ )
166
+
151
167
  # Check for require statements
152
168
  require_match = require_pattern.search(line)
153
169
  if require_match:
154
170
  module_path = require_match.group(1) or require_match.group(2)
155
171
  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
-
172
+ dependencies.append(
173
+ Dependency(
174
+ from_module=module_path,
175
+ imported_symbol=None,
176
+ file_path=file_path,
177
+ line=line_num,
178
+ )
179
+ )
180
+
163
181
  return dependencies
164
182
 
165
183
  def build_dependency_graph(self, project_root: str) -> DependencyGraph:
166
184
  """Builds a dependency graph for a JavaScript project."""
167
185
  graph = DependencyGraph()
168
-
186
+
169
187
  for root, dirs, files in os.walk(project_root):
170
188
  dirs[:] = filter_walk_dirs(dirs)
171
-
189
+
172
190
  for file in files:
173
- if not file.endswith(('.js', '.jsx', '.mjs', '.cjs')):
191
+ if not file.endswith((".js", ".jsx", ".mjs", ".cjs")):
174
192
  continue
175
-
193
+
176
194
  file_path = os.path.join(root, file)
177
195
  try:
178
- with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
196
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
179
197
  content = f.read()
180
-
198
+
181
199
  dependencies = self.analyze_imports(file_path, content)
182
200
  for dep in dependencies:
183
201
  # 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)
202
+ if dep.from_module.startswith(
203
+ (".", "/")
204
+ ) or not dep.from_module.startswith("http"):
205
+ dep_path = self._resolve_module_path(
206
+ project_root, dep.from_module, file_path
207
+ )
186
208
  if dep_path and dep_path != file_path:
187
209
  graph.add_dependency(file_path, dep_path)
188
210
  except Exception:
189
211
  continue
190
-
212
+
191
213
  return graph
192
214
 
193
- def _resolve_module_path(self, project_root: str, module_name: str, from_file: str) -> Optional[str]:
215
+ def _resolve_module_path(
216
+ self, project_root: str, module_name: str, from_file: str
217
+ ) -> Optional[str]:
194
218
  """Resolve a JavaScript module name to a file path."""
195
219
  if not module_name:
196
220
  return None
197
-
221
+
198
222
  # Handle relative imports
199
- if module_name.startswith('.'):
223
+ if module_name.startswith("."):
200
224
  base_dir = os.path.dirname(from_file)
201
225
  # Resolve relative path
202
- if module_name.endswith('.js') or module_name.endswith('.jsx'):
226
+ if module_name.endswith(".js") or module_name.endswith(".jsx"):
203
227
  # Direct file reference
204
228
  resolved = os.path.normpath(os.path.join(base_dir, module_name))
205
229
  else:
206
230
  # Try with .js extension
207
- resolved = os.path.normpath(os.path.join(base_dir, module_name + '.js'))
231
+ resolved = os.path.normpath(os.path.join(base_dir, module_name + ".js"))
208
232
  if not os.path.exists(resolved):
209
233
  # Try with .jsx extension
210
- resolved = os.path.normpath(os.path.join(base_dir, module_name + '.jsx'))
234
+ resolved = os.path.normpath(
235
+ os.path.join(base_dir, module_name + ".jsx")
236
+ )
211
237
  if not os.path.exists(resolved):
212
238
  # Try index.js
213
- resolved = os.path.normpath(os.path.join(base_dir, module_name, 'index.js'))
214
-
239
+ resolved = os.path.normpath(
240
+ os.path.join(base_dir, module_name, "index.js")
241
+ )
242
+
215
243
  if os.path.exists(resolved) and os.path.isfile(resolved):
216
244
  return resolved
217
-
245
+
218
246
  # 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'
247
+ if module_name.startswith("/"):
248
+ resolved = os.path.normpath(
249
+ os.path.join(project_root, module_name.lstrip("/"))
250
+ )
251
+ if not resolved.endswith((".js", ".jsx")):
252
+ resolved += ".js"
223
253
  if os.path.exists(resolved) and os.path.isfile(resolved):
224
254
  return resolved
225
-
255
+
226
256
  # For node_modules and external packages, we can't resolve without package.json
227
257
  # Return None to skip them
228
258
  return None
@@ -230,6 +260,7 @@ class JavaScriptDependencyAnalyzer(DependencyAnalyzer):
230
260
 
231
261
  # --- JavaScript Language Support ---
232
262
 
263
+
233
264
  class JavaScriptLanguageSupport(BaseLanguageSupport):
234
265
  """JavaScript语言支持。"""
235
266
 
@@ -239,7 +270,7 @@ class JavaScriptLanguageSupport(BaseLanguageSupport):
239
270
 
240
271
  @property
241
272
  def file_extensions(self) -> Set[str]:
242
- return {'.js', '.jsx', '.mjs', '.cjs'}
273
+ return {".js", ".jsx", ".mjs", ".cjs"}
243
274
 
244
275
  def create_symbol_extractor(self) -> Optional[SymbolExtractor]:
245
276
  if not JS_LANGUAGE:
@@ -251,4 +282,3 @@ class JavaScriptLanguageSupport(BaseLanguageSupport):
251
282
 
252
283
  def create_dependency_analyzer(self) -> Optional[DependencyAnalyzer]:
253
284
  return JavaScriptDependencyAnalyzer()
254
-
@@ -2,12 +2,18 @@
2
2
 
3
3
  import ast
4
4
  import os
5
- from typing import List, Optional, Set, Union
5
+ from typing import List
6
+ from typing import Optional
7
+ from typing import Set
8
+ from typing import Union
6
9
 
7
10
  from ..base_language import BaseLanguageSupport
8
- from ..dependency_analyzer import Dependency, DependencyAnalyzer, DependencyGraph
11
+ from ..dependency_analyzer import Dependency
12
+ from ..dependency_analyzer import DependencyAnalyzer
13
+ from ..dependency_analyzer import DependencyGraph
9
14
  from ..file_ignore import filter_walk_dirs
10
- from ..symbol_extractor import Symbol, SymbolExtractor
15
+ from ..symbol_extractor import Symbol
16
+ from ..symbol_extractor import SymbolExtractor
11
17
 
12
18
 
13
19
  class PythonSymbolExtractor(SymbolExtractor):
@@ -19,10 +25,10 @@ class PythonSymbolExtractor(SymbolExtractor):
19
25
  # 对于超大文件,限制解析内容长度(避免内存和性能问题)
20
26
  # 只解析前 50000 行,通常足够提取主要符号
21
27
  max_lines = 50000
22
- lines = content.split('\n')
28
+ lines = content.split("\n")
23
29
  if len(lines) > max_lines:
24
- content = '\n'.join(lines[:max_lines])
25
-
30
+ content = "\n".join(lines[:max_lines])
31
+
26
32
  tree = ast.parse(content, filename=file_path)
27
33
  self._traverse_node(tree, file_path, symbols, parent_name=None)
28
34
  except SyntaxError:
@@ -33,7 +39,13 @@ class PythonSymbolExtractor(SymbolExtractor):
33
39
  pass
34
40
  return symbols
35
41
 
36
- def _traverse_node(self, node: ast.AST, file_path: str, symbols: List[Symbol], parent_name: Optional[str]):
42
+ def _traverse_node(
43
+ self,
44
+ node: ast.AST,
45
+ file_path: str,
46
+ symbols: List[Symbol],
47
+ parent_name: Optional[str],
48
+ ):
37
49
  if isinstance(node, ast.FunctionDef):
38
50
  # Extract decorators before the function
39
51
  if node.decorator_list:
@@ -41,7 +53,7 @@ class PythonSymbolExtractor(SymbolExtractor):
41
53
  if isinstance(decorator, ast.Name):
42
54
  decorator_symbol = Symbol(
43
55
  name=decorator.id,
44
- kind='decorator',
56
+ kind="decorator",
45
57
  file_path=file_path,
46
58
  line_start=decorator.lineno,
47
59
  line_end=decorator.lineno,
@@ -58,14 +70,16 @@ class PythonSymbolExtractor(SymbolExtractor):
58
70
  if isinstance(decorator, ast.Name):
59
71
  decorator_symbol = Symbol(
60
72
  name=decorator.id,
61
- kind='decorator',
73
+ kind="decorator",
62
74
  file_path=file_path,
63
75
  line_start=decorator.lineno,
64
76
  line_end=decorator.lineno,
65
77
  parent=parent_name,
66
78
  )
67
79
  symbols.append(decorator_symbol)
68
- symbol = self._create_symbol_from_func(node, file_path, parent_name, is_async=True)
80
+ symbol = self._create_symbol_from_func(
81
+ node, file_path, parent_name, is_async=True
82
+ )
69
83
  symbols.append(symbol)
70
84
  parent_name = node.name
71
85
  elif isinstance(node, ast.ClassDef):
@@ -75,7 +89,7 @@ class PythonSymbolExtractor(SymbolExtractor):
75
89
  if isinstance(decorator, ast.Name):
76
90
  decorator_symbol = Symbol(
77
91
  name=decorator.id,
78
- kind='decorator',
92
+ kind="decorator",
79
93
  file_path=file_path,
80
94
  line_start=decorator.lineno,
81
95
  line_end=decorator.lineno,
@@ -86,16 +100,24 @@ class PythonSymbolExtractor(SymbolExtractor):
86
100
  symbols.append(symbol)
87
101
  parent_name = node.name
88
102
  elif isinstance(node, (ast.Import, ast.ImportFrom)):
89
- symbols.extend(self._create_symbols_from_import(node, file_path, parent_name))
103
+ symbols.extend(
104
+ self._create_symbols_from_import(node, file_path, parent_name)
105
+ )
90
106
 
91
107
  for child in ast.iter_child_nodes(node):
92
108
  self._traverse_node(child, file_path, symbols, parent_name=parent_name)
93
109
 
94
- def _create_symbol_from_func(self, node: Union[ast.FunctionDef, ast.AsyncFunctionDef], file_path: str, parent: Optional[str], is_async: bool = False) -> Symbol:
110
+ def _create_symbol_from_func(
111
+ self,
112
+ node: Union[ast.FunctionDef, ast.AsyncFunctionDef],
113
+ file_path: str,
114
+ parent: Optional[str],
115
+ is_async: bool = False,
116
+ ) -> Symbol:
95
117
  signature = f"{'async ' if is_async else ''}def {node.name}(...)"
96
118
  return Symbol(
97
119
  name=node.name,
98
- kind='function',
120
+ kind="function",
99
121
  file_path=file_path,
100
122
  line_start=node.lineno,
101
123
  line_end=node.end_lineno or node.lineno,
@@ -104,28 +126,37 @@ class PythonSymbolExtractor(SymbolExtractor):
104
126
  parent=parent,
105
127
  )
106
128
 
107
- def _create_symbol_from_class(self, node: ast.ClassDef, file_path: str, parent: Optional[str]) -> Symbol:
129
+ def _create_symbol_from_class(
130
+ self, node: ast.ClassDef, file_path: str, parent: Optional[str]
131
+ ) -> Symbol:
108
132
  return Symbol(
109
133
  name=node.name,
110
- kind='class',
134
+ kind="class",
111
135
  file_path=file_path,
112
136
  line_start=node.lineno,
113
137
  line_end=node.end_lineno or node.lineno,
114
138
  docstring=ast.get_docstring(node),
115
139
  parent=parent,
116
140
  )
117
-
118
- def _create_symbols_from_import(self, node: Union[ast.Import, ast.ImportFrom], file_path: str, parent: Optional[str]) -> List[Symbol]:
141
+
142
+ def _create_symbols_from_import(
143
+ self,
144
+ node: Union[ast.Import, ast.ImportFrom],
145
+ file_path: str,
146
+ parent: Optional[str],
147
+ ) -> List[Symbol]:
119
148
  symbols = []
120
149
  for alias in node.names:
121
- symbols.append(Symbol(
122
- name=alias.asname or alias.name,
123
- kind='import',
124
- file_path=file_path,
125
- line_start=node.lineno,
126
- line_end=node.end_lineno or node.lineno,
127
- parent=parent,
128
- ))
150
+ symbols.append(
151
+ Symbol(
152
+ name=alias.asname or alias.name,
153
+ kind="import",
154
+ file_path=file_path,
155
+ line_start=node.lineno,
156
+ line_end=node.end_lineno or node.lineno,
157
+ parent=parent,
158
+ )
159
+ )
129
160
  return symbols
130
161
 
131
162
 
@@ -135,119 +166,127 @@ class PythonDependencyAnalyzer(DependencyAnalyzer):
135
166
  def analyze_imports(self, file_path: str, content: str) -> List[Dependency]:
136
167
  """Analyzes Python import statements."""
137
168
  dependencies: List[Dependency] = []
138
-
169
+
139
170
  try:
140
171
  tree = ast.parse(content, filename=file_path)
141
172
  for node in ast.walk(tree):
142
173
  if isinstance(node, ast.Import):
143
174
  # import module
144
175
  for alias in node.names:
145
- dependencies.append(Dependency(
146
- from_module=alias.name,
147
- imported_symbol=None,
148
- file_path=file_path,
149
- line=node.lineno,
150
- ))
176
+ dependencies.append(
177
+ Dependency(
178
+ from_module=alias.name,
179
+ imported_symbol=None,
180
+ file_path=file_path,
181
+ line=node.lineno,
182
+ )
183
+ )
151
184
  elif isinstance(node, ast.ImportFrom):
152
185
  # from module import symbol
153
186
  module = node.module or ""
154
187
  for alias in node.names:
155
- dependencies.append(Dependency(
156
- from_module=module,
157
- imported_symbol=alias.name,
158
- file_path=file_path,
159
- line=node.lineno,
160
- ))
188
+ dependencies.append(
189
+ Dependency(
190
+ from_module=module,
191
+ imported_symbol=alias.name,
192
+ file_path=file_path,
193
+ line=node.lineno,
194
+ )
195
+ )
161
196
  except SyntaxError:
162
197
  # Skip files with syntax errors
163
198
  pass
164
-
199
+
165
200
  return dependencies
166
201
 
167
202
  def build_dependency_graph(self, project_root: str) -> DependencyGraph:
168
203
  """Builds a dependency graph for a Python project."""
169
204
  graph = DependencyGraph()
170
-
205
+
171
206
  # Walk through all Python files
172
207
  for root, dirs, files in os.walk(project_root):
173
208
  # Skip hidden directories and common ignore patterns
174
209
  dirs[:] = filter_walk_dirs(dirs)
175
-
210
+
176
211
  for file in files:
177
- if not file.endswith('.py'):
212
+ if not file.endswith(".py"):
178
213
  continue
179
-
214
+
180
215
  file_path = os.path.join(root, file)
181
216
  try:
182
- with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
217
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
183
218
  content = f.read()
184
-
219
+
185
220
  dependencies = self.analyze_imports(file_path, content)
186
221
  for dep in dependencies:
187
222
  # Resolve module to file path
188
- dep_path = self._resolve_module_path(project_root, dep.from_module, file_path)
223
+ dep_path = self._resolve_module_path(
224
+ project_root, dep.from_module, file_path
225
+ )
189
226
  if dep_path and dep_path != file_path:
190
227
  graph.add_dependency(file_path, dep_path)
191
228
  except Exception:
192
229
  continue
193
-
230
+
194
231
  return graph
195
232
 
196
- def _resolve_module_path(self, project_root: str, module_name: str, from_file: str) -> Optional[str]:
233
+ def _resolve_module_path(
234
+ self, project_root: str, module_name: str, from_file: str
235
+ ) -> Optional[str]:
197
236
  """Resolve a Python module name to a file path."""
198
237
  if not module_name:
199
238
  return None
200
-
239
+
201
240
  # Handle relative imports
202
- if module_name.startswith('.'):
241
+ if module_name.startswith("."):
203
242
  # Relative import - resolve from the importing file's directory
204
243
  base_dir = os.path.dirname(from_file)
205
- parts = module_name.split('.')
206
- depth = len([p for p in parts if p == ''])
244
+ parts = module_name.split(".")
245
+ depth = len([p for p in parts if p == ""])
207
246
  module_parts = [p for p in parts if p]
208
-
247
+
209
248
  # Navigate up directories
210
249
  current_dir = base_dir
211
250
  for _ in range(depth - 1):
212
251
  current_dir = os.path.dirname(current_dir)
213
-
252
+
214
253
  if module_parts:
215
254
  module_path = os.path.join(current_dir, *module_parts)
216
255
  else:
217
256
  module_path = current_dir
218
-
257
+
219
258
  # Try to find the module file
220
259
  if os.path.isdir(module_path):
221
- init_path = os.path.join(module_path, '__init__.py')
260
+ init_path = os.path.join(module_path, "__init__.py")
222
261
  if os.path.exists(init_path):
223
262
  return init_path
224
- elif os.path.exists(module_path + '.py'):
225
- return module_path + '.py'
263
+ elif os.path.exists(module_path + ".py"):
264
+ return module_path + ".py"
226
265
  else:
227
266
  # Absolute import
228
- parts = module_name.split('.')
229
-
267
+ parts = module_name.split(".")
268
+
230
269
  # Search in project root
231
270
  for root, dirs, files in os.walk(project_root):
232
271
  # Skip hidden directories and common ignore patterns
233
272
  dirs[:] = filter_walk_dirs(dirs)
234
-
273
+
235
274
  if parts[0] in dirs or f"{parts[0]}.py" in files:
236
275
  module_path = os.path.join(root, *parts)
237
-
276
+
238
277
  if os.path.isdir(module_path):
239
- init_path = os.path.join(module_path, '__init__.py')
278
+ init_path = os.path.join(module_path, "__init__.py")
240
279
  if os.path.exists(init_path):
241
280
  return init_path
242
- elif os.path.exists(module_path + '.py'):
243
- return module_path + '.py'
281
+ elif os.path.exists(module_path + ".py"):
282
+ return module_path + ".py"
244
283
  break
245
-
284
+
246
285
  return None
247
286
 
248
287
  def _is_source_file(self, file_path: str) -> bool:
249
288
  """Check if a file is a Python source file."""
250
- return file_path.endswith('.py')
289
+ return file_path.endswith(".py")
251
290
 
252
291
 
253
292
  class PythonLanguageSupport(BaseLanguageSupport):
@@ -255,15 +294,14 @@ class PythonLanguageSupport(BaseLanguageSupport):
255
294
 
256
295
  @property
257
296
  def language_name(self) -> str:
258
- return 'python'
297
+ return "python"
259
298
 
260
299
  @property
261
300
  def file_extensions(self) -> Set[str]:
262
- return {'.py', '.pyw', '.pyi'}
301
+ return {".py", ".pyw", ".pyi"}
263
302
 
264
303
  def create_symbol_extractor(self) -> Optional[SymbolExtractor]:
265
304
  return PythonSymbolExtractor()
266
305
 
267
306
  def create_dependency_analyzer(self) -> Optional[DependencyAnalyzer]:
268
307
  return PythonDependencyAnalyzer()
269
-