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,525 @@
1
+ """CodeAgent Git 操作模块"""
2
+
3
+ import os
4
+ import subprocess
5
+
6
+ from jarvis.jarvis_utils.output import PrettyOutput
7
+
8
+ # -*- coding: utf-8 -*-
9
+ import sys
10
+ from typing import Any
11
+ from typing import List
12
+ from typing import Optional
13
+ from typing import Tuple
14
+
15
+ from jarvis.jarvis_git_utils.git_commiter import GitCommitTool
16
+ from jarvis.jarvis_utils.git_utils import confirm_add_new_files
17
+ from jarvis.jarvis_utils.git_utils import find_git_root_and_cd
18
+ from jarvis.jarvis_utils.git_utils import get_commits_between
19
+ from jarvis.jarvis_utils.git_utils import has_uncommitted_changes
20
+ from jarvis.jarvis_utils.globals import get_global_model_group
21
+ from jarvis.jarvis_utils.input import user_confirm
22
+ from jarvis.jarvis_utils.output import OutputType
23
+
24
+
25
+ class GitManager:
26
+ """Git 操作管理器"""
27
+
28
+ def __init__(self, root_dir: str):
29
+ self.root_dir = root_dir
30
+
31
+ def check_git_config(self) -> None:
32
+ """检查 git username 和 email 是否已设置,如果没有则提示并退出"""
33
+ try:
34
+ # 检查 git user.name
35
+ result = subprocess.run(
36
+ ["git", "config", "--get", "user.name"],
37
+ capture_output=True,
38
+ text=True,
39
+ check=False,
40
+ )
41
+ username = result.stdout.strip()
42
+
43
+ # 检查 git user.email
44
+ result = subprocess.run(
45
+ ["git", "config", "--get", "user.email"],
46
+ capture_output=True,
47
+ text=True,
48
+ check=False,
49
+ )
50
+ email = result.stdout.strip()
51
+
52
+ # 如果任一配置未设置,提示并退出
53
+ if not username or not email:
54
+ missing_configs = []
55
+ if not username:
56
+ missing_configs.append(
57
+ ' git config --global user.name "Your Name"'
58
+ )
59
+ if not email:
60
+ missing_configs.append(
61
+ ' git config --global user.email "your.email@example.com"'
62
+ )
63
+
64
+ message = "❌ Git 配置不完整\n\n请运行以下命令配置 Git:\n" + "\n".join(
65
+ missing_configs
66
+ )
67
+ PrettyOutput.auto_print(f"❌ {message}")
68
+ sys.exit(1)
69
+
70
+ except FileNotFoundError:
71
+ PrettyOutput.auto_print("❌ 未找到 git 命令,请先安装 Git")
72
+ sys.exit(1)
73
+ except Exception as e:
74
+ PrettyOutput.auto_print(f"❌ 检查 Git 配置时出错: {str(e)}")
75
+ sys.exit(1)
76
+
77
+ def find_git_root(self) -> str:
78
+ """查找并切换到git根目录
79
+
80
+ 返回:
81
+ str: git根目录路径
82
+ """
83
+ curr_dir = os.getcwd()
84
+ git_dir = find_git_root_and_cd(curr_dir)
85
+ self.root_dir = git_dir
86
+ return git_dir
87
+
88
+ def update_gitignore(self, git_dir: str) -> None:
89
+ """检查并更新.gitignore文件,确保忽略.jarvis目录,并追加常用语言的忽略规则(若缺失)
90
+
91
+ 参数:
92
+ git_dir: git根目录路径
93
+ """
94
+ gitignore_path = os.path.join(git_dir, ".gitignore")
95
+
96
+ # 常用忽略规则(按语言/场景分组)
97
+ # 注意:以 / 开头的路径表示只在根目录匹配,避免误忽略子目录中的源码
98
+ sections = {
99
+ "General": [
100
+ ".jarvis",
101
+ ".DS_Store",
102
+ "Thumbs.db",
103
+ "*.log",
104
+ "*.tmp",
105
+ "*.swp",
106
+ "*.swo",
107
+ ".idea/",
108
+ ".vscode/",
109
+ ],
110
+ "Python": [
111
+ "__pycache__/", # 任何目录下的 __pycache__
112
+ "*.py[cod]", # 任何目录下的编译文件
113
+ "*$py.class",
114
+ ".Python",
115
+ "env/", # 只在根目录
116
+ "venv/", # 只在根目录
117
+ ".venv/", # 只在根目录
118
+ "build/", # 只在根目录
119
+ "dist/", # 只在根目录
120
+ "develop-eggs/",
121
+ "downloads/",
122
+ "eggs/",
123
+ ".eggs/",
124
+ # 注意:不忽略 lib/ 和 lib64/,因为这些目录可能存放源码
125
+ "parts/",
126
+ "sdist/",
127
+ "var/",
128
+ "wheels/",
129
+ "pip-wheel-metadata/",
130
+ "share/python-wheels/",
131
+ "*.egg-info/",
132
+ ".installed.cfg",
133
+ "*.egg",
134
+ "MANIFEST",
135
+ ".mypy_cache/",
136
+ ".pytest_cache/",
137
+ ".ruff_cache/",
138
+ ".tox/",
139
+ ".coverage",
140
+ ".coverage.*",
141
+ "htmlcov/",
142
+ ".hypothesis/",
143
+ ".ipynb_checkpoints",
144
+ ".pyre/",
145
+ ".pytype/",
146
+ ],
147
+ "Rust": [
148
+ "target/", # 只在根目录
149
+ ],
150
+ "Node": [
151
+ "node_modules/", # 只在根目录
152
+ "npm-debug.log*",
153
+ "yarn-debug.log*",
154
+ "yarn-error.log*",
155
+ "pnpm-debug.log*",
156
+ "lerna-debug.log*",
157
+ "dist/", # 只在根目录
158
+ "coverage/",
159
+ ".turbo/", # 只在根目录
160
+ ".next/", # 只在根目录
161
+ ".nuxt/", # 只在根目录
162
+ "out/", # 只在根目录
163
+ ],
164
+ "Go": [
165
+ # 注意:不忽略 bin/,因为这个目录可能存放源码
166
+ "vendor/", # 只在根目录
167
+ "coverage.out",
168
+ ],
169
+ "Java": [
170
+ "target/", # 只在根目录
171
+ "*.class", # 任何目录下的编译文件
172
+ ".gradle/", # 只在根目录
173
+ "build/", # 只在根目录
174
+ "out/", # 只在根目录
175
+ ],
176
+ "C/C++": [
177
+ "build/", # 只在根目录
178
+ "cmake-build-*/", # 只在根目录
179
+ "*.o", # 任何目录下的编译文件
180
+ "*.a",
181
+ "*.so",
182
+ "*.obj",
183
+ "*.dll",
184
+ "*.dylib",
185
+ "*.exe",
186
+ "*.pdb",
187
+ ],
188
+ ".NET": [
189
+ # 注意:不忽略 bin/,因为这个目录可能存放源码
190
+ "obj/", # 只在根目录
191
+ ],
192
+ }
193
+
194
+ existing_content = ""
195
+ if os.path.exists(gitignore_path):
196
+ with open(gitignore_path, "r", encoding="utf-8", errors="replace") as f:
197
+ existing_content = f.read()
198
+
199
+ # 已存在的忽略项(去除注释与空行)
200
+ existing_set = set(
201
+ ln.strip()
202
+ for ln in existing_content.splitlines()
203
+ if ln.strip() and not ln.strip().startswith("#")
204
+ )
205
+
206
+ # 计算缺失项并准备追加内容
207
+ new_lines: List[str] = []
208
+ for name, patterns in sections.items():
209
+ missing = [p for p in patterns if p not in existing_set]
210
+ if missing:
211
+ new_lines.append(f"# {name}")
212
+ new_lines.extend(missing)
213
+ new_lines.append("") # 分组空行
214
+
215
+ if not os.path.exists(gitignore_path):
216
+ # 新建 .gitignore(仅包含缺失项;此处即为全部常用规则)
217
+ with open(gitignore_path, "w", encoding="utf-8", newline="\n") as f:
218
+ content_to_write = "\n".join(new_lines).rstrip()
219
+ if content_to_write:
220
+ f.write(content_to_write + "\n")
221
+ PrettyOutput.auto_print("✅ 已创建 .gitignore 并添加常用忽略规则")
222
+ else:
223
+ if new_lines:
224
+ # 追加缺失的规则
225
+ with open(gitignore_path, "a", encoding="utf-8", newline="\n") as f:
226
+ # 若原文件不以换行结尾,先补一行
227
+ if existing_content and not existing_content.endswith("\n"):
228
+ f.write("\n")
229
+ f.write("\n".join(new_lines).rstrip() + "\n")
230
+ PrettyOutput.auto_print("✅ 已更新 .gitignore,追加常用忽略规则")
231
+
232
+ def handle_git_changes(self, prefix: str, suffix: str, agent: Any) -> None:
233
+ """处理git仓库中的未提交修改"""
234
+ if has_uncommitted_changes():
235
+ git_commiter = GitCommitTool()
236
+ git_commiter.execute(
237
+ {
238
+ "prefix": prefix,
239
+ "suffix": suffix,
240
+ "agent": agent,
241
+ # 使用全局模型组(不再从 agent 继承)
242
+ "model_group": get_global_model_group(),
243
+ }
244
+ )
245
+
246
+ def init_env(self, prefix: str, suffix: str, agent: Any) -> None:
247
+ """初始化环境,组合以下功能:
248
+ 1. 查找git根目录
249
+ 2. 检查并更新.gitignore文件
250
+ 3. 处理未提交的修改
251
+ 4. 配置git对换行符变化不敏感
252
+ """
253
+ git_dir = self.find_git_root()
254
+ self.update_gitignore(git_dir)
255
+ self.handle_git_changes(prefix, suffix, agent)
256
+ # 配置git对换行符变化不敏感
257
+ self.configure_line_ending_settings()
258
+
259
+ def configure_line_ending_settings(self) -> None:
260
+ """配置git对换行符变化不敏感,只在当前设置与目标设置不一致时修改"""
261
+ target_settings = {
262
+ "core.autocrlf": "false",
263
+ "core.safecrlf": "false",
264
+ "core.whitespace": "cr-at-eol", # 忽略行尾的CR
265
+ }
266
+
267
+ # 获取当前设置并检查是否需要修改
268
+ need_change = False
269
+ current_settings = {}
270
+ for key, target_value in target_settings.items():
271
+ result = subprocess.run(
272
+ ["git", "config", "--get", key],
273
+ capture_output=True,
274
+ text=True,
275
+ check=False,
276
+ )
277
+ current_value = result.stdout.strip()
278
+ current_settings[key] = current_value
279
+ if current_value != target_value:
280
+ need_change = True
281
+
282
+ if not need_change:
283
+ return
284
+
285
+ PrettyOutput.auto_print(
286
+ "⚠️ 正在修改git换行符敏感设置,这会影响所有文件的换行符处理方式"
287
+ )
288
+ # 避免在循环中逐条打印,先拼接后统一打印
289
+ lines = ["将进行以下设置:"]
290
+ for key, value in target_settings.items():
291
+ current = current_settings.get(key, "未设置")
292
+ lines.append(f"{key}: {current} -> {value}")
293
+ joined_lines = "\n".join(lines)
294
+ PrettyOutput.auto_print(f"ℹ️ {joined_lines}")
295
+
296
+ # 直接执行设置,不需要用户确认
297
+ for key, value in target_settings.items():
298
+ subprocess.run(["git", "config", key, value], check=True)
299
+
300
+ # 对于Windows系统,提示用户可以创建.gitattributes文件
301
+ if sys.platform.startswith("win"):
302
+ self.handle_windows_line_endings()
303
+
304
+ PrettyOutput.auto_print("✅ git换行符敏感设置已更新")
305
+
306
+ def handle_windows_line_endings(self) -> None:
307
+ """在Windows系统上处理换行符问题,提供建议而非强制修改"""
308
+ gitattributes_path = os.path.join(self.root_dir, ".gitattributes")
309
+
310
+ # 检查是否已存在.gitattributes文件
311
+ if os.path.exists(gitattributes_path):
312
+ with open(gitattributes_path, "r", encoding="utf-8") as f:
313
+ content = f.read()
314
+ # 如果已经有换行符相关配置,就不再提示
315
+ if any(keyword in content for keyword in ["text=", "eol=", "binary"]):
316
+ return
317
+
318
+ PrettyOutput.auto_print(
319
+ "ℹ️ 提示:在Windows系统上,建议配置 .gitattributes 文件来避免换行符问题。"
320
+ )
321
+ PrettyOutput.auto_print(
322
+ "ℹ️ 这可以防止仅因换行符不同而导致整个文件被标记为修改。"
323
+ )
324
+
325
+ if user_confirm("是否要创建一个最小化的.gitattributes文件?", False):
326
+ # 最小化的内容,只影响特定类型的文件
327
+ minimal_content = """# Jarvis建议的最小化换行符配置
328
+ # 默认所有文本文件使用LF,只有Windows特定文件使用CRLF
329
+
330
+ # 默认所有文本文件使用LF
331
+ * text=auto eol=lf
332
+
333
+ # Windows批处理文件需要CRLF
334
+ *.bat text eol=crlf
335
+ *.cmd text eol=crlf
336
+ *.ps1 text eol=crlf
337
+ """
338
+
339
+ if not os.path.exists(gitattributes_path):
340
+ with open(gitattributes_path, "w", encoding="utf-8", newline="\n") as f:
341
+ f.write(minimal_content)
342
+ PrettyOutput.auto_print("✅ 已创建最小化的 .gitattributes 文件")
343
+ else:
344
+ PrettyOutput.auto_print("ℹ️ 将以下内容追加到现有 .gitattributes 文件:")
345
+ PrettyOutput.print(
346
+ minimal_content, OutputType.CODE, lang="text"
347
+ ) # 保留语法高亮
348
+ if user_confirm("是否追加到现有文件?", True):
349
+ with open(
350
+ gitattributes_path, "a", encoding="utf-8", newline="\n"
351
+ ) as f:
352
+ f.write("\n" + minimal_content)
353
+ PrettyOutput.auto_print("✅ 已更新 .gitattributes 文件")
354
+ else:
355
+ PrettyOutput.auto_print(
356
+ "ℹ️ 跳过 .gitattributes 文件创建。如遇换行符问题,可手动创建此文件。"
357
+ )
358
+
359
+ def record_code_changes_stats(self, diff_text: str) -> None:
360
+ """记录代码变更的统计信息。
361
+
362
+ Args:
363
+ diff_text: git diff的文本输出
364
+ """
365
+ import re
366
+
367
+ from jarvis.jarvis_stats.stats import StatsManager
368
+
369
+ # 匹配插入行数
370
+ insertions_match = re.search(r"(\d+)\s+insertions?\(\+\)", diff_text)
371
+ if insertions_match:
372
+ insertions = int(insertions_match.group(1))
373
+ StatsManager.increment(
374
+ "code_lines_inserted", amount=insertions, group="code_agent"
375
+ )
376
+
377
+ # 匹配删除行数
378
+ deletions_match = re.search(r"(\d+)\s+deletions?\(\-\)", diff_text)
379
+ if deletions_match:
380
+ deletions = int(deletions_match.group(1))
381
+ StatsManager.increment(
382
+ "code_lines_deleted", amount=deletions, group="code_agent"
383
+ )
384
+
385
+ def handle_uncommitted_changes(self) -> None:
386
+ """处理未提交的修改,包括:
387
+ 1. 提示用户确认是否提交
388
+ 2. 如果确认,则检查新增文件数量
389
+ 3. 如果新增文件超过20个,让用户确认是否添加
390
+ 4. 如果用户拒绝添加大量文件,提示修改.gitignore并重新检测
391
+ 5. 暂存并提交所有修改
392
+ """
393
+ if has_uncommitted_changes():
394
+ # 获取代码变更统计
395
+ try:
396
+ diff_result = subprocess.run(
397
+ ["git", "diff", "HEAD", "--shortstat"],
398
+ capture_output=True,
399
+ text=True,
400
+ encoding="utf-8",
401
+ errors="replace",
402
+ check=True,
403
+ )
404
+ if diff_result.returncode == 0 and diff_result.stdout:
405
+ self.record_code_changes_stats(diff_result.stdout)
406
+ except subprocess.CalledProcessError:
407
+ pass
408
+
409
+ PrettyOutput.auto_print("⚠️ 检测到未提交的修改,是否要提交?")
410
+ if not user_confirm("是否要提交?", True):
411
+ return
412
+
413
+ try:
414
+ confirm_add_new_files()
415
+
416
+ if not has_uncommitted_changes():
417
+ return
418
+
419
+ # 获取当前分支的提交总数
420
+ # 兼容空仓库或无 HEAD 的场景:失败时将提交计数视为 0,继续执行提交流程
421
+ commit_count = 0
422
+ try:
423
+ commit_result = subprocess.run(
424
+ ["git", "rev-list", "--count", "HEAD"],
425
+ capture_output=True,
426
+ text=True,
427
+ encoding="utf-8",
428
+ errors="replace",
429
+ check=False,
430
+ )
431
+ if commit_result.returncode == 0:
432
+ out = commit_result.stdout.strip()
433
+ if out.isdigit():
434
+ commit_count = int(out)
435
+ except Exception:
436
+ commit_count = 0
437
+
438
+ # 暂存所有修改
439
+ subprocess.run(["git", "add", "."], check=True)
440
+
441
+ # 提交变更
442
+ subprocess.run(
443
+ ["git", "commit", "-m", f"CheckPoint #{commit_count + 1}"],
444
+ check=True,
445
+ )
446
+ except subprocess.CalledProcessError as e:
447
+ PrettyOutput.auto_print(f"❌ 提交失败: {str(e)}")
448
+
449
+ def show_commit_history(
450
+ self, start_commit: Optional[str], end_commit: Optional[str]
451
+ ) -> List[Tuple[str, str]]:
452
+ """显示两个提交之间的提交历史
453
+
454
+ 参数:
455
+ start_commit: 起始提交hash
456
+ end_commit: 结束提交hash
457
+
458
+ 返回:
459
+ 包含(commit_hash, commit_message)的元组列表
460
+ """
461
+ if start_commit and end_commit:
462
+ commits = get_commits_between(start_commit, end_commit)
463
+ else:
464
+ commits = []
465
+
466
+ if commits:
467
+ # 统计生成的commit数量
468
+ from jarvis.jarvis_stats.stats import StatsManager
469
+
470
+ StatsManager.increment("commits_generated", group="code_agent")
471
+
472
+ commit_messages = "检测到以下提交记录:\n" + "\n".join(
473
+ f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits
474
+ )
475
+ PrettyOutput.auto_print(f"ℹ️ {commit_messages}")
476
+ return commits
477
+
478
+ def handle_commit_confirmation(
479
+ self,
480
+ commits: List[Tuple[str, str]],
481
+ start_commit: Optional[str],
482
+ prefix: str,
483
+ suffix: str,
484
+ agent: Any,
485
+ post_process_func: Any,
486
+ ) -> None:
487
+ """处理提交确认和可能的重置"""
488
+ if commits and user_confirm("是否接受以上提交记录?", True):
489
+ # 统计接受的commit数量
490
+ from jarvis.jarvis_stats.stats import StatsManager
491
+
492
+ StatsManager.increment("commits_accepted", group="code_agent")
493
+
494
+ subprocess.run(
495
+ ["git", "reset", "--mixed", str(start_commit)],
496
+ stdout=subprocess.DEVNULL,
497
+ stderr=subprocess.DEVNULL,
498
+ check=True,
499
+ )
500
+
501
+ # 检测变更文件并格式化
502
+ from jarvis.jarvis_utils.git_utils import get_diff_file_list
503
+
504
+ modified_files = get_diff_file_list()
505
+ if modified_files:
506
+ post_process_func(modified_files)
507
+
508
+ git_commiter = GitCommitTool()
509
+ git_commiter.execute(
510
+ {
511
+ "prefix": prefix,
512
+ "suffix": suffix,
513
+ "agent": agent,
514
+ # 使用全局模型组(不再从 agent 继承)
515
+ "model_group": get_global_model_group(),
516
+ }
517
+ )
518
+
519
+ # 在用户接受commit后,根据配置决定是否保存记忆
520
+ if getattr(agent, "force_save_memory", False):
521
+ agent.memory_manager.prompt_memory_save()
522
+ elif start_commit and commits:
523
+ if user_confirm("是否要重置到初始提交?", True):
524
+ os.system(f"git reset --hard {str(start_commit)}") # 确保转换为字符串
525
+ PrettyOutput.auto_print("ℹ️ 已重置到初始提交")