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
@@ -0,0 +1,177 @@
1
+ """CodeAgent 影响分析模块"""
2
+
3
+ from typing import Any
4
+ from typing import Dict
5
+
6
+ from jarvis.jarvis_utils.output import PrettyOutput
7
+
8
+ # -*- coding: utf-8 -*-
9
+ from typing import List
10
+ from typing import Optional
11
+ from typing import Tuple
12
+
13
+ from jarvis.jarvis_code_agent.code_analyzer import ContextManager
14
+ from jarvis.jarvis_code_agent.code_analyzer import ImpactAnalyzer
15
+ from jarvis.jarvis_code_agent.code_analyzer import parse_git_diff_to_edits
16
+ from jarvis.jarvis_code_agent.code_analyzer.impact_analyzer import Edit
17
+ from jarvis.jarvis_utils.config import is_enable_impact_analysis
18
+
19
+
20
+ class ImpactManager:
21
+ """影响分析管理器"""
22
+
23
+ def __init__(self, root_dir: str, context_manager: ContextManager):
24
+ self.root_dir = root_dir
25
+ self.context_manager = context_manager
26
+
27
+ def update_context_for_modified_files(self, modified_files: List[str]) -> None:
28
+ """更新上下文管理器:当文件被修改后,更新符号表和依赖图"""
29
+ if not modified_files:
30
+ return
31
+ PrettyOutput.auto_print("🔄 正在更新代码上下文...")
32
+ for file_path in modified_files:
33
+ import os
34
+
35
+ if os.path.exists(file_path):
36
+ try:
37
+ with open(file_path, "r", encoding="utf-8", errors="replace") as f:
38
+ content = f.read()
39
+ self.context_manager.update_context_for_file(file_path, content)
40
+ except Exception:
41
+ # 如果读取文件失败,跳过更新
42
+ pass
43
+
44
+ def analyze_edit_impact(self, modified_files: List[str]) -> Optional[Any]:
45
+ """进行影响范围分析(如果启用)
46
+
47
+ Returns:
48
+ ImpactReport: 影响分析报告,如果未启用或失败则返回None
49
+ """
50
+ if not is_enable_impact_analysis():
51
+ return None
52
+
53
+ PrettyOutput.auto_print("🔍 正在进行变更影响分析...")
54
+ try:
55
+ impact_analyzer = ImpactAnalyzer(self.context_manager)
56
+ all_edits = []
57
+ import os
58
+
59
+ for file_path in modified_files:
60
+ if os.path.exists(file_path):
61
+ edits = parse_git_diff_to_edits(file_path, self.root_dir)
62
+ all_edits.extend(edits)
63
+
64
+ if not all_edits:
65
+ return None
66
+
67
+ # 按文件分组编辑
68
+ edits_by_file: Dict[str, List[Edit]] = {}
69
+ for edit in all_edits:
70
+ if edit.file_path not in edits_by_file:
71
+ edits_by_file[edit.file_path] = []
72
+ edits_by_file[edit.file_path].append(edit)
73
+
74
+ # 对每个文件进行影响分析
75
+ impact_report = None
76
+ for file_path, edits in edits_by_file.items():
77
+ report = impact_analyzer.analyze_edit_impact(file_path, edits)
78
+ if report:
79
+ # 合并报告
80
+ if impact_report is None:
81
+ impact_report = report
82
+ else:
83
+ # 合并多个报告,去重
84
+ impact_report.affected_files = list(
85
+ set(impact_report.affected_files + report.affected_files)
86
+ )
87
+
88
+ # 合并符号(基于文件路径和名称去重)
89
+ symbol_map: Dict[Tuple[str, str, str], Any] = {}
90
+ for symbol in (
91
+ impact_report.affected_symbols + report.affected_symbols
92
+ ):
93
+ key = (
94
+ symbol.file_path,
95
+ symbol.name,
96
+ str(symbol.line_start),
97
+ )
98
+ if key not in symbol_map:
99
+ symbol_map[key] = symbol
100
+ impact_report.affected_symbols = list(symbol_map.values())
101
+
102
+ impact_report.affected_tests = list(
103
+ set(impact_report.affected_tests + report.affected_tests)
104
+ )
105
+
106
+ # 合并接口变更(基于符号名和文件路径去重)
107
+ interface_map: Dict[Tuple[str, str, str], Any] = {}
108
+ for change in (
109
+ impact_report.interface_changes + report.interface_changes
110
+ ):
111
+ key = (
112
+ change.file_path,
113
+ change.symbol_name,
114
+ str(change.change_type),
115
+ )
116
+ if key not in interface_map:
117
+ interface_map[key] = change
118
+ impact_report.interface_changes = list(interface_map.values())
119
+
120
+ impact_report.impacts.extend(report.impacts)
121
+
122
+ # 合并建议
123
+ impact_report.recommendations = list(
124
+ set(impact_report.recommendations + report.recommendations)
125
+ )
126
+
127
+ # 使用更高的风险等级
128
+ if (
129
+ report.risk_level.value == "high"
130
+ or impact_report.risk_level.value == "high"
131
+ ):
132
+ impact_report.risk_level = (
133
+ report.risk_level
134
+ if report.risk_level.value == "high"
135
+ else impact_report.risk_level
136
+ )
137
+ elif report.risk_level.value == "medium":
138
+ impact_report.risk_level = report.risk_level
139
+
140
+ return impact_report
141
+ except Exception as e:
142
+ # 影响分析失败不应该影响主流程,仅记录日志
143
+ PrettyOutput.auto_print(f"⚠️ 影响范围分析失败: {e}")
144
+ return None
145
+
146
+ def handle_impact_report(
147
+ self, impact_report: Optional[Any], agent: Any, final_ret: str
148
+ ) -> str:
149
+ """处理影响范围分析报告
150
+
151
+ Args:
152
+ impact_report: 影响分析报告
153
+ agent: Agent实例
154
+ final_ret: 当前的结果字符串
155
+
156
+ Returns:
157
+ 更新后的结果字符串
158
+ """
159
+ if not impact_report:
160
+ return final_ret
161
+
162
+ impact_summary = impact_report.to_string(self.root_dir)
163
+ final_ret += f"\n\n{impact_summary}\n"
164
+
165
+ # 如果是高风险,在提示词中提醒
166
+ if impact_report.risk_level.value == "high":
167
+ agent.set_addon_prompt(
168
+ f"{agent.get_addon_prompt() or ''}\n\n"
169
+ f"⚠️ 高风险编辑警告:\n"
170
+ f"检测到此编辑为高风险操作,请仔细检查以下内容:\n"
171
+ f"- 受影响文件: {len(impact_report.affected_files)} 个\n"
172
+ f"- 接口变更: {len(impact_report.interface_changes)} 个\n"
173
+ f"- 相关测试: {len(impact_report.affected_tests)} 个\n"
174
+ f"建议运行相关测试并检查所有受影响文件。"
175
+ )
176
+
177
+ return final_ret
@@ -0,0 +1,283 @@
1
+ """CodeAgent 静态分析模块"""
2
+
3
+ import os
4
+ import subprocess
5
+
6
+ from jarvis.jarvis_utils.output import PrettyOutput
7
+
8
+ # -*- coding: utf-8 -*-
9
+ from typing import Any
10
+ from typing import List
11
+ from typing import Optional
12
+ from typing import Tuple
13
+
14
+ from jarvis.jarvis_code_agent.lint import LINT_COMMAND_TEMPLATES_BY_FILE
15
+ from jarvis.jarvis_code_agent.lint import get_lint_commands_for_files
16
+ from jarvis.jarvis_code_agent.lint import group_commands_by_template
17
+ from jarvis.jarvis_utils.config import is_enable_static_analysis
18
+
19
+
20
+ class LintManager:
21
+ """静态分析管理器"""
22
+
23
+ def __init__(self, root_dir: str):
24
+ self.root_dir = root_dir
25
+
26
+ def run_static_analysis(
27
+ self, modified_files: List[str]
28
+ ) -> List[Tuple[str, str, int, str]]:
29
+ """执行静态分析
30
+
31
+ Args:
32
+ modified_files: 修改的文件列表
33
+
34
+ Returns:
35
+ [(file_path, command, returncode, output), ...] 格式的结果列表
36
+ 只返回有错误或警告的结果(returncode != 0)
37
+ """
38
+ if not modified_files:
39
+ return []
40
+
41
+ # 获取所有lint命令
42
+ commands = get_lint_commands_for_files(modified_files, self.root_dir)
43
+ if not commands:
44
+ return []
45
+
46
+ # 输出静态检查日志
47
+ file_count = len(modified_files)
48
+ files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
49
+ if file_count > 3:
50
+ files_str += f" 等{file_count}个文件"
51
+ # 从命令中提取工具名(第一个单词)
52
+ tool_names = list(set(cmd[1].split()[0] for cmd in commands if cmd[1].split()))
53
+ tools_str = ", ".join(tool_names[:3])
54
+ if len(tool_names) > 3:
55
+ tools_str += f" 等{len(tool_names)}个工具"
56
+ PrettyOutput.auto_print("🔍 静态检查中...")
57
+
58
+ results = []
59
+ # 记录每个文件的检查结果
60
+ file_results = [] # [(file_name, command, status, message), ...]
61
+
62
+ # 按命令模板分组,相同工具可以批量执行
63
+ grouped = group_commands_by_template(commands)
64
+
65
+ for template_key, file_commands in grouped.items():
66
+ for file_path, command in file_commands:
67
+ file_name = os.path.basename(file_path)
68
+ try:
69
+ # 检查文件是否存在
70
+ abs_file_path = (
71
+ os.path.join(self.root_dir, file_path)
72
+ if not os.path.isabs(file_path)
73
+ else file_path
74
+ )
75
+ if not os.path.exists(abs_file_path):
76
+ file_results.append((file_name, command, "跳过", "文件不存在"))
77
+ continue
78
+
79
+ # 打印执行的命令
80
+ PrettyOutput.auto_print(f"ℹ️ 执行: {command}")
81
+
82
+ # 执行命令
83
+ result = subprocess.run(
84
+ command,
85
+ shell=True,
86
+ cwd=self.root_dir,
87
+ capture_output=True,
88
+ text=True,
89
+ encoding="utf-8",
90
+ errors="replace",
91
+ timeout=600, # 600秒超时
92
+ )
93
+
94
+ # 只记录有错误或警告的结果
95
+ if result.returncode != 0:
96
+ output = result.stdout + result.stderr
97
+ if output.strip(): # 有输出才记录
98
+ results.append(
99
+ (
100
+ file_path,
101
+ command,
102
+ result.returncode,
103
+ output,
104
+ )
105
+ )
106
+ file_results.append(
107
+ (file_name, command, "失败", "发现问题")
108
+ )
109
+ # 失败时打印检查结果
110
+ output_preview = (
111
+ output[:2000] if len(output) > 2000 else output
112
+ )
113
+ PrettyOutput.auto_print(
114
+ f"⚠️ 检查失败 ({file_name}):\n{output_preview}"
115
+ )
116
+ if len(output) > 2000:
117
+ PrettyOutput.auto_print(
118
+ f"⚠️ ... (输出已截断,共 {len(output)} 字符)"
119
+ )
120
+ else:
121
+ file_results.append((file_name, command, "通过", ""))
122
+ else:
123
+ file_results.append((file_name, command, "通过", ""))
124
+
125
+ except subprocess.TimeoutExpired:
126
+ results.append((file_path, command, -1, "执行超时(600秒)"))
127
+ file_results.append(
128
+ (file_name, command, "超时", "执行超时(600秒)")
129
+ )
130
+ PrettyOutput.auto_print(
131
+ f"⚠️ 检查超时 ({file_name}): 执行超时(600秒)"
132
+ )
133
+ except FileNotFoundError:
134
+ # 工具未安装,跳过
135
+ file_results.append((file_name, command, "跳过", "工具未安装"))
136
+ continue
137
+ except Exception as e:
138
+ # 其他错误,记录但继续
139
+ PrettyOutput.auto_print(f"⚠️ 执行lint命令失败: {command}, 错误: {e}")
140
+ file_results.append(
141
+ (file_name, command, "失败", f"执行失败: {str(e)[:50]}")
142
+ )
143
+ continue
144
+
145
+ # 一次性打印所有检查结果
146
+ if file_results:
147
+ total_files = len(file_results)
148
+ passed_count = sum(
149
+ 1 for _, _, status, _ in file_results if status == "通过"
150
+ )
151
+ failed_count = sum(
152
+ 1 for _, _, status, _ in file_results if status == "失败"
153
+ )
154
+ timeout_count = sum(
155
+ 1 for _, _, status, _ in file_results if status == "超时"
156
+ )
157
+ sum(1 for _, _, status, _ in file_results if status == "跳过")
158
+
159
+ # 收缩为一行的结果摘要
160
+ summary = f"🔍 静态检查: {total_files}个文件"
161
+ if failed_count > 0:
162
+ summary += f", {failed_count}失败"
163
+ if timeout_count > 0:
164
+ summary += f", {timeout_count}超时"
165
+ if passed_count == total_files:
166
+ summary += " ✅全部通过"
167
+
168
+ if failed_count > 0 or timeout_count > 0:
169
+ PrettyOutput.auto_print(f"⚠️ {summary}")
170
+ else:
171
+ PrettyOutput.auto_print(f"✅ {summary}")
172
+ else:
173
+ PrettyOutput.auto_print("✅ 静态检查完成")
174
+
175
+ return results
176
+
177
+ def format_lint_results(self, results: List[Tuple[str, str, int, str]]) -> str:
178
+ """格式化lint结果
179
+
180
+ Args:
181
+ results: [(file_path, command, returncode, output), ...]
182
+
183
+ Returns:
184
+ 格式化的错误信息字符串
185
+ """
186
+ if not results:
187
+ return ""
188
+
189
+ lines = []
190
+ for file_path, command, returncode, output in results:
191
+ # 从命令中提取工具名(第一个单词)
192
+ tool_name = command.split()[0] if command.split() else "unknown"
193
+ lines.append(f"工具: {tool_name}")
194
+ lines.append(f"文件: {file_path}")
195
+ lines.append(f"命令: {command}")
196
+ if returncode == -1:
197
+ lines.append(f"错误: {output}")
198
+ else:
199
+ # 限制输出长度,避免过长
200
+ output_preview = output[:1000] if len(output) > 1000 else output
201
+ lines.append(f"输出:\n{output_preview}")
202
+ if len(output) > 1000:
203
+ lines.append(f"... (输出已截断,共 {len(output)} 字符)")
204
+ lines.append("") # 空行分隔
205
+
206
+ return "\n".join(lines)
207
+
208
+ def handle_static_analysis(
209
+ self,
210
+ modified_files: List[str],
211
+ build_validation_result: Optional[Any],
212
+ config: Any,
213
+ agent: Any,
214
+ final_ret: str,
215
+ ) -> str:
216
+ """处理静态分析
217
+
218
+ Returns:
219
+ 更新后的结果字符串
220
+ """
221
+ # 检查是否启用静态分析
222
+ if not is_enable_static_analysis():
223
+ PrettyOutput.auto_print("ℹ️ 静态分析已禁用,跳过静态检查")
224
+ return final_ret
225
+
226
+ # 检查是否有可用的lint工具
227
+ def get_lint_tool_names(file_path: str) -> List[str]:
228
+ """获取文件的lint工具名称列表"""
229
+ filename = os.path.basename(file_path)
230
+ filename_lower = filename.lower()
231
+ templates = LINT_COMMAND_TEMPLATES_BY_FILE.get(filename_lower, [])
232
+ if not templates:
233
+ ext = os.path.splitext(filename)[1]
234
+ if ext:
235
+ templates = LINT_COMMAND_TEMPLATES_BY_FILE.get(ext.lower(), [])
236
+ # 提取工具名(命令模板的第一个单词)
237
+ return [
238
+ template.split()[0] if template.split() else "unknown"
239
+ for template in templates
240
+ ]
241
+
242
+ lint_tools_info = "\n".join(
243
+ f" - {file}: 使用 {'、'.join(get_lint_tool_names(file))}"
244
+ for file in modified_files
245
+ if get_lint_tool_names(file)
246
+ )
247
+
248
+ if not lint_tools_info:
249
+ PrettyOutput.auto_print("ℹ️ 未找到可用的静态检查工具,跳过静态检查")
250
+ return final_ret
251
+
252
+ # 如果构建验证失败且未禁用,不进行静态分析(避免重复错误)
253
+ # 如果构建验证已禁用,则进行静态分析(因为只做了基础静态检查)
254
+ should_skip_static = (
255
+ build_validation_result
256
+ and not build_validation_result.success
257
+ and not config.is_build_validation_disabled()
258
+ )
259
+
260
+ if should_skip_static:
261
+ PrettyOutput.auto_print("ℹ️ 构建验证失败,跳过静态分析(避免重复错误)")
262
+ return final_ret
263
+
264
+ # 直接执行静态扫描
265
+ lint_results = self.run_static_analysis(modified_files)
266
+ if lint_results:
267
+ # 有错误或警告,让大模型修复
268
+ errors_summary = self.format_lint_results(lint_results)
269
+ # 打印完整的检查结果
270
+ PrettyOutput.auto_print(f"⚠️ 静态扫描发现问题:\n{errors_summary}")
271
+ addon_prompt = f"""
272
+ 静态扫描发现以下问题,请根据错误信息修复代码:
273
+
274
+ {errors_summary}
275
+
276
+ 请仔细检查并修复所有问题。
277
+ """
278
+ agent.set_addon_prompt(addon_prompt)
279
+ final_ret += "\n\n⚠️ 静态扫描发现问题,已提示修复\n"
280
+ else:
281
+ final_ret += "\n\n✅ 静态扫描通过\n"
282
+
283
+ return final_ret
@@ -0,0 +1,159 @@
1
+ """CodeAgent LLM 询问模块"""
2
+
3
+ from typing import Any
4
+ from typing import Dict
5
+
6
+ from jarvis.jarvis_utils.output import PrettyOutput
7
+
8
+ # -*- coding: utf-8 -*-
9
+ from typing import Optional
10
+
11
+ from jarvis.jarvis_platform.registry import PlatformRegistry
12
+ from jarvis.jarvis_utils.config import get_normal_model_name
13
+ from jarvis.jarvis_utils.config import get_normal_platform_name
14
+ from jarvis.jarvis_utils.globals import get_global_model_group
15
+
16
+
17
+ class LLMManager:
18
+ """LLM 询问管理器"""
19
+
20
+ def __init__(
21
+ self, parent_model: Optional[Any] = None, model_group: Optional[str] = None
22
+ ):
23
+ """初始化LLM管理器
24
+
25
+ Args:
26
+ parent_model: 父Agent的模型实例(已废弃,保留参数兼容性)
27
+ model_group: 模型组名称,如果提供则优先使用,否则使用全局模型组
28
+ """
29
+ # 保存配置信息,用于后续创建 LLM 实例
30
+ self._platform_name = None
31
+ self._model_name = None
32
+ # 优先使用传入的model_group,否则使用全局模型组
33
+ self._model_group = model_group or get_global_model_group()
34
+
35
+ # 根据 model_group 获取配置(不再从 parent_model 继承)
36
+ # 使用普通模型,LLM询问可以降低成本
37
+ if self._model_group:
38
+ try:
39
+ self._platform_name = get_normal_platform_name(self._model_group)
40
+ self._model_name = get_normal_model_name(self._model_group)
41
+ except Exception:
42
+ # 如果从 model_group 解析失败,回退到从 parent_model 获取的值
43
+ pass
44
+
45
+ # 如果仍未获取到,使用默认配置
46
+ if not self._platform_name:
47
+ self._platform_name = get_normal_platform_name(None)
48
+ if not self._model_name:
49
+ self._model_name = get_normal_model_name(None)
50
+
51
+ def _create_llm_model(self):
52
+ """创建新的 LLM 模型实例
53
+
54
+ 每次调用都创建新的实例,避免上下文窗口累积。
55
+
56
+ Returns:
57
+ LLM 模型实例
58
+
59
+ Raises:
60
+ ValueError: 如果无法创建LLM模型
61
+ """
62
+ try:
63
+ registry = PlatformRegistry.get_global_platform_registry()
64
+
65
+ # 创建平台实例
66
+ if self._platform_name:
67
+ llm_model = registry.create_platform(self._platform_name)
68
+ if llm_model is None:
69
+ # 如果创建失败,使用普通平台
70
+ llm_model = registry.get_normal_platform()
71
+ else:
72
+ llm_model = registry.get_normal_platform()
73
+
74
+ if not llm_model:
75
+ raise ValueError("无法创建LLM模型实例")
76
+
77
+ # 先设置模型组(如果从父Agent获取到),因为 model_group 可能会影响模型名称的解析
78
+ if self._model_group:
79
+ try:
80
+ llm_model.set_model_group(self._model_group)
81
+ except Exception:
82
+ pass
83
+
84
+ # 然后设置模型名称(如果从父Agent或model_group获取到)
85
+ if self._model_name:
86
+ try:
87
+ llm_model.set_model_name(self._model_name)
88
+ except Exception:
89
+ pass
90
+
91
+ # 设置抑制输出,因为这是后台任务
92
+ llm_model.set_suppress_output(True)
93
+
94
+ return llm_model
95
+ except Exception as e:
96
+ raise ValueError(f"无法创建LLM模型: {e}")
97
+
98
+ def ask_llm_about_large_deletion(
99
+ self, detection_result: Dict[str, int], preview: str
100
+ ) -> bool:
101
+ """询问大模型大量代码删除是否合理
102
+
103
+ 参数:
104
+ detection_result: 检测结果字典,包含 'insertions', 'deletions', 'net_deletions'
105
+ preview: 补丁预览内容
106
+
107
+ 返回:
108
+ bool: 如果大模型认为合理返回True,否则返回False
109
+ """
110
+ insertions = detection_result["insertions"]
111
+ deletions = detection_result["deletions"]
112
+ net_deletions = detection_result["net_deletions"]
113
+
114
+ prompt = f"""检测到大量代码删除,请判断是否合理:
115
+
116
+ 统计信息:
117
+ - 新增行数: {insertions}
118
+ - 删除行数: {deletions}
119
+ - 净删除行数: {net_deletions}
120
+
121
+ 补丁预览:
122
+ {preview}
123
+
124
+ 请仔细分析以上代码变更,判断这些大量代码删除是否合理。可能的情况包括:
125
+ 1. 重构代码,删除冗余或过时的代码
126
+ 2. 简化实现,用更简洁的代码替换复杂的实现
127
+ 3. 删除未使用的代码或功能
128
+ 4. 错误地删除了重要代码
129
+
130
+ 请使用以下协议回答(必须包含且仅包含以下标记之一):
131
+ - 如果认为这些删除是合理的,回答: <!!!YES!!!>
132
+ - 如果认为这些删除不合理或存在风险,回答: <!!!NO!!!>
133
+
134
+ 请严格按照协议格式回答,不要添加其他内容。
135
+ """
136
+
137
+ try:
138
+ PrettyOutput.auto_print("🤖 正在询问大模型判断大量代码删除是否合理...")
139
+ # 每次调用都创建新的 LLM 实例,避免上下文窗口累积
140
+ llm_model = self._create_llm_model()
141
+ response = llm_model.chat_until_success(prompt)
142
+
143
+ # 使用确定的协议标记解析回答
144
+ if "<!!!YES!!!>" in response:
145
+ PrettyOutput.auto_print("✅ 大模型确认:代码删除合理")
146
+ return True
147
+ elif "<!!!NO!!!>" in response:
148
+ PrettyOutput.auto_print("⚠️ 大模型确认:代码删除不合理")
149
+ return False
150
+ else:
151
+ # 如果无法找到协议标记,默认认为不合理(保守策略)
152
+ PrettyOutput.auto_print(
153
+ f"⚠️ 无法找到协议标记,默认认为不合理。回答内容: {response[:200]}"
154
+ )
155
+ return False
156
+ except Exception as e:
157
+ # 如果询问失败,默认认为不合理(保守策略)
158
+ PrettyOutput.auto_print(f"⚠️ 询问大模型失败: {str(e)},默认认为不合理")
159
+ return False