jarvis-ai-assistant 0.7.8__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.8.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.8.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,227 @@
1
+ """CodeAgent 构建验证模块"""
2
+
3
+ from typing import Any
4
+ from typing import List
5
+
6
+ from jarvis.jarvis_utils.output import PrettyOutput
7
+
8
+ # -*- coding: utf-8 -*-
9
+ from typing import Optional
10
+ from typing import Tuple
11
+
12
+ from jarvis.jarvis_code_agent.build_validation_config import BuildValidationConfig
13
+ from jarvis.jarvis_code_agent.code_analyzer.build_validator import BuildResult
14
+ from jarvis.jarvis_code_agent.code_analyzer.build_validator import BuildValidator
15
+ from jarvis.jarvis_code_agent.code_analyzer.build_validator import (
16
+ FallbackBuildValidator,
17
+ )
18
+ from jarvis.jarvis_utils.config import get_build_validation_timeout
19
+ from jarvis.jarvis_utils.config import is_enable_build_validation
20
+ from jarvis.jarvis_utils.input import user_confirm
21
+
22
+
23
+ def format_build_error(result: BuildResult, max_len: int = 2000) -> str:
24
+ """格式化构建错误信息,限制输出长度"""
25
+ error_msg = result.error_message or ""
26
+ output = result.output or ""
27
+
28
+ full_error = f"{error_msg}\n{output}".strip()
29
+
30
+ if len(full_error) > max_len:
31
+ return full_error[:max_len] + "\n... (输出已截断)"
32
+ return full_error
33
+
34
+
35
+ class BuildValidationManager:
36
+ """构建验证管理器"""
37
+
38
+ def __init__(self, root_dir: str):
39
+ self.root_dir = root_dir
40
+
41
+ def validate_build_after_edit(
42
+ self, modified_files: List[str]
43
+ ) -> Optional[BuildResult]:
44
+ """编辑后验证构建
45
+
46
+ Args:
47
+ modified_files: 修改的文件列表
48
+
49
+ Returns:
50
+ BuildResult: 验证结果,如果验证被禁用或出错则返回None
51
+ """
52
+ if not is_enable_build_validation():
53
+ return None
54
+
55
+ # 检查项目配置,看是否已禁用构建验证
56
+ config = BuildValidationConfig(self.root_dir)
57
+ if config.is_build_validation_disabled():
58
+ # 已禁用,返回None,由调用方处理基础静态检查
59
+ return None
60
+
61
+ # 输出编译检查日志
62
+ import os
63
+
64
+ file_count = len(modified_files)
65
+ files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
66
+ if file_count > 3:
67
+ files_str += f" 等{file_count}个文件"
68
+ PrettyOutput.auto_print(f"🔨 正在进行编译检查 ({files_str})...")
69
+
70
+ try:
71
+ timeout = get_build_validation_timeout()
72
+ validator = BuildValidator(self.root_dir, timeout=timeout)
73
+ result = validator.validate(modified_files)
74
+ return result
75
+ except Exception as e:
76
+ # 构建验证失败不应该影响主流程,仅记录日志
77
+ PrettyOutput.auto_print(f"⚠️ 构建验证执行失败: {e}")
78
+ return None
79
+
80
+ def handle_build_validation_disabled(
81
+ self, modified_files: List[str], config: Any, agent: Any, final_ret: str
82
+ ) -> str:
83
+ """处理构建验证已禁用的情况
84
+
85
+ Returns:
86
+ 更新后的结果字符串
87
+ """
88
+ reason = config.get_disable_reason()
89
+ reason_text = f"(原因: {reason})" if reason else ""
90
+ final_ret += f"\n\nℹ️ 构建验证已禁用{reason_text},仅进行基础静态检查\n"
91
+
92
+ # 输出基础静态检查日志
93
+ import os
94
+
95
+ file_count = len(modified_files)
96
+ files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
97
+ if file_count > 3:
98
+ files_str += f" 等{file_count}个文件"
99
+
100
+ # 使用兜底验证器进行基础静态检查
101
+ fallback_validator = FallbackBuildValidator(
102
+ self.root_dir, timeout=get_build_validation_timeout()
103
+ )
104
+ static_check_result = fallback_validator.validate(modified_files)
105
+ if not static_check_result.success:
106
+ final_ret += f"\n⚠️ 基础静态检查失败:\n{static_check_result.error_message or static_check_result.output}\n"
107
+ agent.set_addon_prompt(
108
+ f"基础静态检查失败,请根据以下错误信息修复代码:\n{static_check_result.error_message or static_check_result.output}\n"
109
+ )
110
+ else:
111
+ final_ret += (
112
+ f"\n✅ 基础静态检查通过(耗时 {static_check_result.duration:.2f}秒)\n"
113
+ )
114
+
115
+ return final_ret
116
+
117
+ def handle_build_validation_failure(
118
+ self,
119
+ build_validation_result: Any,
120
+ config: Any,
121
+ modified_files: List[str],
122
+ agent: Any,
123
+ final_ret: str,
124
+ ) -> str:
125
+ """处理构建验证失败的情况
126
+
127
+ Returns:
128
+ 更新后的结果字符串
129
+ """
130
+ if not config.has_been_asked():
131
+ # 首次失败,询问用户
132
+ error_preview = format_build_error(build_validation_result)
133
+ PrettyOutput.auto_print(f"\n⚠️ 构建验证失败:\n{error_preview}\n")
134
+ PrettyOutput.auto_print(
135
+ "ℹ️ 提示:如果此项目需要在特殊环境(如容器)中构建,或使用独立构建脚本,"
136
+ "可以选择禁用构建验证,后续将仅进行基础静态检查。"
137
+ )
138
+
139
+ if user_confirm(
140
+ "是否要禁用构建验证,后续仅进行基础静态检查?",
141
+ default=True,
142
+ ):
143
+ # 用户选择禁用
144
+ config.disable_build_validation(
145
+ reason="用户选择禁用(项目可能需要在特殊环境中构建)"
146
+ )
147
+ config.mark_as_asked()
148
+ final_ret += "\n\nℹ️ 已禁用构建验证,后续将仅进行基础静态检查\n"
149
+
150
+ # 输出基础静态检查日志
151
+ import os
152
+
153
+ file_count = len(modified_files)
154
+ files_str = ", ".join(os.path.basename(f) for f in modified_files[:3])
155
+ if file_count > 3:
156
+ files_str += f" 等{file_count}个文件"
157
+
158
+ # 立即进行基础静态检查
159
+ fallback_validator = FallbackBuildValidator(
160
+ self.root_dir, timeout=get_build_validation_timeout()
161
+ )
162
+ static_check_result = fallback_validator.validate(modified_files)
163
+ if not static_check_result.success:
164
+ final_ret += f"\n⚠️ 基础静态检查失败:\n{static_check_result.error_message or static_check_result.output}\n"
165
+ agent.set_addon_prompt(
166
+ f"基础静态检查失败,请根据以下错误信息修复代码:\n{static_check_result.error_message or static_check_result.output}\n"
167
+ )
168
+ else:
169
+ final_ret += f"\n✅ 基础静态检查通过(耗时 {static_check_result.duration:.2f}秒)\n"
170
+ else:
171
+ # 用户选择继续验证,标记为已询问
172
+ config.mark_as_asked()
173
+ final_ret += f"\n\n⚠️ 构建验证失败:\n{format_build_error(build_validation_result)}\n"
174
+ # 如果构建失败,添加修复提示
175
+ agent.set_addon_prompt(
176
+ f"构建验证失败,请根据以下错误信息修复代码:\n{format_build_error(build_validation_result)}\n"
177
+ "请仔细检查错误信息,修复编译/构建错误后重新提交。"
178
+ )
179
+ else:
180
+ # 已经询问过,直接显示错误
181
+ final_ret += (
182
+ f"\n\n⚠️ 构建验证失败:\n{format_build_error(build_validation_result)}\n"
183
+ )
184
+ # 如果构建失败,添加修复提示
185
+ agent.set_addon_prompt(
186
+ f"构建验证失败,请根据以下错误信息修复代码:\n{format_build_error(build_validation_result)}\n"
187
+ "请仔细检查错误信息,修复编译/构建错误后重新提交。"
188
+ )
189
+
190
+ return final_ret
191
+
192
+ def handle_build_validation(
193
+ self, modified_files: List[str], agent: Any, final_ret: str
194
+ ) -> Tuple[Optional[Any], str]:
195
+ """处理构建验证
196
+
197
+ Returns:
198
+ (build_validation_result, updated_final_ret)
199
+ """
200
+ if not is_enable_build_validation():
201
+ return None, final_ret
202
+
203
+ config = BuildValidationConfig(self.root_dir)
204
+
205
+ # 检查是否已禁用构建验证
206
+ if config.is_build_validation_disabled():
207
+ final_ret = self.handle_build_validation_disabled(
208
+ modified_files, config, agent, final_ret
209
+ )
210
+ return None, final_ret
211
+
212
+ # 未禁用,进行构建验证
213
+ build_validation_result = self.validate_build_after_edit(modified_files)
214
+ if build_validation_result:
215
+ if not build_validation_result.success:
216
+ final_ret = self.handle_build_validation_failure(
217
+ build_validation_result, config, modified_files, agent, final_ret
218
+ )
219
+ else:
220
+ build_system_info = (
221
+ f" ({build_validation_result.build_system.value})"
222
+ if build_validation_result.build_system
223
+ else ""
224
+ )
225
+ final_ret += f"\n\n✅ 构建验证通过{build_system_info}(耗时 {build_validation_result.duration:.2f}秒)\n"
226
+
227
+ return build_validation_result, final_ret
@@ -0,0 +1,246 @@
1
+ # -*- coding: utf-8 -*-
2
+ """CodeAgent 代码变更预览和统计模块"""
3
+
4
+ import os
5
+ import subprocess
6
+ from typing import Dict
7
+ from typing import List
8
+ from typing import Tuple
9
+
10
+ from jarvis.jarvis_utils.config import get_diff_large_file_threshold
11
+ from jarvis.jarvis_utils.config import get_diff_show_line_numbers
12
+ from jarvis.jarvis_utils.config import get_diff_visualization_mode
13
+ from jarvis.jarvis_utils.git_utils import get_latest_commit_hash
14
+
15
+
16
+ class DiffManager:
17
+ """代码变更预览和统计管理器"""
18
+
19
+ def __init__(self, root_dir: str):
20
+ self.root_dir = root_dir
21
+ # 延迟导入,避免循环依赖
22
+ self._visualizer = None
23
+
24
+ def build_name_status_map(self) -> Dict[str, str]:
25
+ """构造按文件的状态映射与差异文本,删除文件不展示diff,仅提示删除"""
26
+ status_map = {}
27
+ try:
28
+ head_exists = bool(get_latest_commit_hash())
29
+ # 临时 -N 以包含未跟踪文件的差异检测
30
+ subprocess.run(
31
+ ["git", "add", "-N", "."],
32
+ check=False,
33
+ stdout=subprocess.DEVNULL,
34
+ stderr=subprocess.DEVNULL,
35
+ )
36
+ cmd = ["git", "diff", "--name-status"] + (["HEAD"] if head_exists else [])
37
+ res = subprocess.run(
38
+ cmd,
39
+ capture_output=True,
40
+ text=True,
41
+ encoding="utf-8",
42
+ errors="replace",
43
+ check=False,
44
+ )
45
+ finally:
46
+ subprocess.run(
47
+ ["git", "reset"],
48
+ check=False,
49
+ stdout=subprocess.DEVNULL,
50
+ stderr=subprocess.DEVNULL,
51
+ )
52
+
53
+ if res.returncode == 0 and res.stdout:
54
+ for line in res.stdout.splitlines():
55
+ if not line.strip():
56
+ continue
57
+ parts = line.split("\t")
58
+ if not parts:
59
+ continue
60
+ status = parts[0]
61
+ if status.startswith("R") or status.startswith("C"):
62
+ # 重命名/复制:使用新路径作为键
63
+ if len(parts) >= 3:
64
+ old_path, new_path = parts[1], parts[2]
65
+ status_map[new_path] = status
66
+ # 也记录旧路径,便于匹配 name-only 的结果
67
+ status_map[old_path] = status
68
+ elif len(parts) >= 2:
69
+ status_map[parts[-1]] = status
70
+ else:
71
+ if len(parts) >= 2:
72
+ status_map[parts[1]] = status
73
+ return status_map
74
+
75
+ def get_file_diff(self, file_path: str) -> str:
76
+ """获取单文件的diff,包含新增文件内容;失败时返回空字符串"""
77
+ head_exists = bool(get_latest_commit_hash())
78
+ try:
79
+ # 为了让未跟踪文件也能展示diff,临时 -N 该文件
80
+ subprocess.run(
81
+ ["git", "add", "-N", "--", file_path],
82
+ check=False,
83
+ stdout=subprocess.DEVNULL,
84
+ stderr=subprocess.DEVNULL,
85
+ )
86
+ cmd = (
87
+ ["git", "diff"] + (["HEAD"] if head_exists else []) + ["--", file_path]
88
+ )
89
+ res = subprocess.run(
90
+ cmd,
91
+ capture_output=True,
92
+ text=True,
93
+ encoding="utf-8",
94
+ errors="replace",
95
+ check=False,
96
+ )
97
+ if res.returncode == 0:
98
+ return res.stdout or ""
99
+ return ""
100
+ finally:
101
+ subprocess.run(
102
+ ["git", "reset", "--", file_path],
103
+ check=False,
104
+ stdout=subprocess.DEVNULL,
105
+ stderr=subprocess.DEVNULL,
106
+ )
107
+
108
+ def _get_visualizer(self):
109
+ """获取可视化器实例(延迟初始化)"""
110
+ if self._visualizer is None:
111
+ try:
112
+ from jarvis.jarvis_code_agent.diff_visualizer import DiffVisualizer
113
+
114
+ self._visualizer = DiffVisualizer() # type: ignore
115
+ except ImportError:
116
+ # 如果导入失败,返回 None
117
+ self._visualizer = False # type: ignore
118
+ return self._visualizer if self._visualizer is not False else None
119
+
120
+ def build_per_file_patch_preview(
121
+ self, modified_files: List[str], use_enhanced_visualization: bool = True
122
+ ) -> str:
123
+ """构建按文件的补丁预览
124
+
125
+ 参数:
126
+ modified_files: 修改的文件列表
127
+ use_enhanced_visualization: 是否使用增强的可视化(默认 True)
128
+
129
+ 返回:
130
+ str: 补丁预览的 markdown 文本(用于日志等)
131
+ """
132
+ status_map = self.build_name_status_map()
133
+ lines: List[str] = []
134
+ visualizer = self._get_visualizer() if use_enhanced_visualization else None
135
+ visualization_mode = (
136
+ get_diff_visualization_mode() if use_enhanced_visualization else "default"
137
+ )
138
+ show_line_numbers = get_diff_show_line_numbers()
139
+ large_file_threshold = get_diff_large_file_threshold()
140
+
141
+ def _get_file_numstat(file_path: str) -> Tuple[int, int]:
142
+ """获取单文件的新增/删除行数,失败时返回(0,0)"""
143
+ head_exists = bool(get_latest_commit_hash())
144
+ try:
145
+ # 让未跟踪文件也能统计到新增行数
146
+ subprocess.run(
147
+ ["git", "add", "-N", "--", file_path],
148
+ check=False,
149
+ stdout=subprocess.DEVNULL,
150
+ stderr=subprocess.DEVNULL,
151
+ )
152
+ cmd = (
153
+ ["git", "diff", "--numstat"]
154
+ + (["HEAD"] if head_exists else [])
155
+ + ["--", file_path]
156
+ )
157
+ res = subprocess.run(
158
+ cmd,
159
+ capture_output=True,
160
+ text=True,
161
+ encoding="utf-8",
162
+ errors="replace",
163
+ check=False,
164
+ )
165
+ if res.returncode == 0 and res.stdout:
166
+ for line in res.stdout.splitlines():
167
+ parts = line.strip().split("\t")
168
+ if len(parts) >= 3:
169
+ add_s, del_s = parts[0], parts[1]
170
+
171
+ def to_int(x: str) -> int:
172
+ try:
173
+ return int(x)
174
+ except Exception:
175
+ # 二进制或无法解析时显示为0
176
+ return 0
177
+
178
+ return to_int(add_s), to_int(del_s)
179
+ finally:
180
+ subprocess.run(
181
+ ["git", "reset", "--", file_path],
182
+ check=False,
183
+ stdout=subprocess.DEVNULL,
184
+ stderr=subprocess.DEVNULL,
185
+ )
186
+ return (0, 0)
187
+
188
+ for f in modified_files:
189
+ status = status_map.get(f, "")
190
+ adds, dels = _get_file_numstat(f)
191
+ total_changes = adds + dels
192
+
193
+ # 删除文件:不展示diff,仅提示(附带删除行数信息如果可用)
194
+ if (status.startswith("D")) or (not os.path.exists(f)):
195
+ if dels > 0:
196
+ lines.append(f"- {f} 文件被删除(删除{dels}行)")
197
+ else:
198
+ lines.append(f"- {f} 文件被删除")
199
+ # 使用可视化器显示统计
200
+ if visualizer:
201
+ visualizer.visualize_statistics(f, 0, dels)
202
+ continue
203
+
204
+ # 变更过大:显示统计和紧凑预览
205
+ if total_changes > large_file_threshold:
206
+ if visualizer:
207
+ # 显示统计信息
208
+ visualizer.visualize_statistics(f, adds, dels, total_changes)
209
+ # 显示紧凑预览
210
+ file_diff = self.get_file_diff(f)
211
+ if file_diff.strip():
212
+ if visualization_mode == "compact":
213
+ visualizer.visualize_compact(file_diff, f, max_lines=100)
214
+ elif visualization_mode == "syntax":
215
+ visualizer.visualize_syntax_highlighted(file_diff, f)
216
+ else:
217
+ # unified 模式也显示,但限制行数
218
+ visualizer.visualize_compact(file_diff, f, max_lines=50)
219
+ lines.append(f"- {f} 新增{adds}行/删除{dels}行(变更过大,预览已省略)")
220
+ continue
221
+
222
+ # 正常变更:展示该文件的diff
223
+ file_diff = self.get_file_diff(f)
224
+ if file_diff.strip():
225
+ # 使用增强的可视化
226
+ if visualizer:
227
+ if visualization_mode == "unified":
228
+ visualizer.visualize_unified_diff(
229
+ file_diff, f, show_line_numbers=show_line_numbers
230
+ )
231
+ elif visualization_mode == "syntax":
232
+ visualizer.visualize_syntax_highlighted(file_diff, f)
233
+ elif visualization_mode == "compact":
234
+ visualizer.visualize_compact(file_diff, f)
235
+ else:
236
+ # 默认使用 unified
237
+ visualizer.visualize_unified_diff(
238
+ file_diff, f, show_line_numbers=show_line_numbers
239
+ )
240
+
241
+ # 同时保留 markdown 格式用于日志
242
+ lines.append(f"文件: {f}\n```diff\n{file_diff}\n```")
243
+ else:
244
+ # 当无法获取到diff(例如重命名或特殊状态),避免空输出
245
+ lines.append(f"- {f} 变更已记录(无可展示的文本差异)")
246
+ return "\n".join(lines)