jarvis-ai-assistant 0.3.30__py3-none-any.whl → 0.7.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +458 -152
  3. jarvis/jarvis_agent/agent_manager.py +17 -13
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +329 -0
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +628 -55
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +34 -10
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +105 -9
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +20 -22
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  31. jarvis/jarvis_agent/task_analyzer.py +31 -6
  32. jarvis/jarvis_agent/task_manager.py +11 -27
  33. jarvis/jarvis_agent/tool_executor.py +2 -3
  34. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  35. jarvis/jarvis_agent/utils.py +5 -1
  36. jarvis/jarvis_agent/web_bridge.py +189 -0
  37. jarvis/jarvis_agent/web_output_sink.py +53 -0
  38. jarvis/jarvis_agent/web_server.py +786 -0
  39. jarvis/jarvis_c2rust/__init__.py +26 -0
  40. jarvis/jarvis_c2rust/cli.py +575 -0
  41. jarvis/jarvis_c2rust/collector.py +250 -0
  42. jarvis/jarvis_c2rust/constants.py +26 -0
  43. jarvis/jarvis_c2rust/library_replacer.py +1254 -0
  44. jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
  45. jarvis/jarvis_c2rust/loaders.py +207 -0
  46. jarvis/jarvis_c2rust/models.py +28 -0
  47. jarvis/jarvis_c2rust/optimizer.py +2157 -0
  48. jarvis/jarvis_c2rust/scanner.py +1681 -0
  49. jarvis/jarvis_c2rust/transpiler.py +2983 -0
  50. jarvis/jarvis_c2rust/utils.py +385 -0
  51. jarvis/jarvis_code_agent/build_validation_config.py +132 -0
  52. jarvis/jarvis_code_agent/code_agent.py +1371 -220
  53. jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
  54. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
  60. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
  61. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
  62. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
  63. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
  64. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
  65. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
  66. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
  67. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
  68. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  69. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
  70. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  71. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  72. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  73. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  74. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  75. jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
  76. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
  77. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
  78. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
  79. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  80. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  81. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
  82. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
  83. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  84. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
  85. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  86. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
  87. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
  88. jarvis/jarvis_code_agent/lint.py +501 -8
  89. jarvis/jarvis_code_agent/utils.py +141 -0
  90. jarvis/jarvis_code_analysis/code_review.py +493 -584
  91. jarvis/jarvis_data/config_schema.json +128 -12
  92. jarvis/jarvis_git_squash/main.py +4 -5
  93. jarvis/jarvis_git_utils/git_commiter.py +82 -75
  94. jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
  95. jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
  96. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  97. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  98. jarvis/jarvis_methodology/main.py +32 -48
  99. jarvis/jarvis_multi_agent/__init__.py +287 -55
  100. jarvis/jarvis_multi_agent/main.py +36 -4
  101. jarvis/jarvis_platform/base.py +524 -202
  102. jarvis/jarvis_platform/human.py +7 -8
  103. jarvis/jarvis_platform/kimi.py +30 -36
  104. jarvis/jarvis_platform/openai.py +88 -25
  105. jarvis/jarvis_platform/registry.py +26 -10
  106. jarvis/jarvis_platform/tongyi.py +24 -25
  107. jarvis/jarvis_platform/yuanbao.py +32 -43
  108. jarvis/jarvis_platform_manager/main.py +66 -77
  109. jarvis/jarvis_platform_manager/service.py +8 -13
  110. jarvis/jarvis_rag/cli.py +53 -55
  111. jarvis/jarvis_rag/embedding_manager.py +13 -18
  112. jarvis/jarvis_rag/llm_interface.py +8 -9
  113. jarvis/jarvis_rag/query_rewriter.py +10 -21
  114. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  115. jarvis/jarvis_rag/reranker.py +4 -5
  116. jarvis/jarvis_rag/retriever.py +28 -30
  117. jarvis/jarvis_sec/__init__.py +305 -0
  118. jarvis/jarvis_sec/agents.py +143 -0
  119. jarvis/jarvis_sec/analysis.py +276 -0
  120. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  121. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  122. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  123. jarvis/jarvis_sec/cli.py +139 -0
  124. jarvis/jarvis_sec/clustering.py +1439 -0
  125. jarvis/jarvis_sec/file_manager.py +427 -0
  126. jarvis/jarvis_sec/parsers.py +73 -0
  127. jarvis/jarvis_sec/prompts.py +268 -0
  128. jarvis/jarvis_sec/report.py +336 -0
  129. jarvis/jarvis_sec/review.py +453 -0
  130. jarvis/jarvis_sec/status.py +264 -0
  131. jarvis/jarvis_sec/types.py +20 -0
  132. jarvis/jarvis_sec/utils.py +499 -0
  133. jarvis/jarvis_sec/verification.py +848 -0
  134. jarvis/jarvis_sec/workflow.py +226 -0
  135. jarvis/jarvis_smart_shell/main.py +38 -87
  136. jarvis/jarvis_stats/cli.py +2 -2
  137. jarvis/jarvis_stats/stats.py +8 -8
  138. jarvis/jarvis_stats/storage.py +15 -21
  139. jarvis/jarvis_stats/visualizer.py +1 -1
  140. jarvis/jarvis_tools/clear_memory.py +3 -20
  141. jarvis/jarvis_tools/cli/main.py +21 -23
  142. jarvis/jarvis_tools/edit_file.py +1019 -132
  143. jarvis/jarvis_tools/execute_script.py +83 -25
  144. jarvis/jarvis_tools/file_analyzer.py +6 -9
  145. jarvis/jarvis_tools/generate_new_tool.py +14 -21
  146. jarvis/jarvis_tools/lsp_client.py +1552 -0
  147. jarvis/jarvis_tools/methodology.py +2 -3
  148. jarvis/jarvis_tools/read_code.py +1736 -35
  149. jarvis/jarvis_tools/read_symbols.py +140 -0
  150. jarvis/jarvis_tools/read_webpage.py +12 -13
  151. jarvis/jarvis_tools/registry.py +427 -200
  152. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  153. jarvis/jarvis_tools/rewrite_file.py +72 -158
  154. jarvis/jarvis_tools/save_memory.py +3 -15
  155. jarvis/jarvis_tools/search_web.py +18 -18
  156. jarvis/jarvis_tools/sub_agent.py +36 -43
  157. jarvis/jarvis_tools/sub_code_agent.py +25 -26
  158. jarvis/jarvis_tools/virtual_tty.py +55 -33
  159. jarvis/jarvis_utils/clipboard.py +7 -10
  160. jarvis/jarvis_utils/config.py +232 -45
  161. jarvis/jarvis_utils/embedding.py +8 -5
  162. jarvis/jarvis_utils/fzf.py +8 -8
  163. jarvis/jarvis_utils/git_utils.py +225 -36
  164. jarvis/jarvis_utils/globals.py +3 -3
  165. jarvis/jarvis_utils/http.py +1 -1
  166. jarvis/jarvis_utils/input.py +99 -48
  167. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  168. jarvis/jarvis_utils/methodology.py +52 -48
  169. jarvis/jarvis_utils/utils.py +819 -491
  170. jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
  171. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  172. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
  173. jarvis/jarvis_agent/config.py +0 -92
  174. jarvis/jarvis_agent/edit_file_handler.py +0 -296
  175. jarvis/jarvis_platform/ai8.py +0 -332
  176. jarvis/jarvis_tools/ask_user.py +0 -54
  177. jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
  178. jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
  179. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  180. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  181. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -14,10 +14,9 @@ import os
14
14
  import re
15
15
  import subprocess
16
16
  import sys
17
- from typing import Any, Dict, List, Set, Tuple
17
+ from typing import Any, Dict, List, Optional, Set, Tuple
18
18
 
19
19
  from jarvis.jarvis_utils.config import get_data_dir, is_confirm_before_apply_patch
20
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
21
20
  from jarvis.jarvis_utils.input import user_confirm
22
21
  from jarvis.jarvis_utils.utils import is_rag_installed
23
22
 
@@ -34,7 +33,13 @@ def find_git_root_and_cd(start_dir: str = ".") -> str:
34
33
  """
35
34
  os.chdir(start_dir)
36
35
  try:
37
- git_root = os.popen("git rev-parse --show-toplevel").read().strip()
36
+ result = subprocess.run(
37
+ ["git", "rev-parse", "--show-toplevel"],
38
+ capture_output=True,
39
+ text=True,
40
+ check=True,
41
+ )
42
+ git_root = result.stdout.strip()
38
43
  if not git_root:
39
44
  subprocess.run(["git", "init"], check=True)
40
45
  git_root = os.path.abspath(".")
@@ -105,7 +110,7 @@ def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]
105
110
  )
106
111
  if result.returncode != 0:
107
112
  error_msg = result.stderr.decode("utf-8", errors="replace")
108
- PrettyOutput.print(f"获取commit历史失败: {error_msg}", OutputType.ERROR)
113
+ print(f"获取commit历史失败: {error_msg}")
109
114
  return []
110
115
 
111
116
  output = result.stdout.decode("utf-8", errors="replace")
@@ -117,7 +122,7 @@ def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]
117
122
  return commits
118
123
 
119
124
  except Exception as e:
120
- PrettyOutput.print(f"获取commit历史异常: {str(e)}", OutputType.ERROR)
125
+ print(f"获取commit历史异常: {str(e)}")
121
126
  return []
122
127
 
123
128
 
@@ -166,6 +171,59 @@ def get_diff() -> str:
166
171
  return f"发生意外错误: {str(e)}"
167
172
 
168
173
 
174
+ def get_diff_between_commits(start_hash: str, end_hash: Optional[str] = None) -> str:
175
+ """获取两个commit之间的差异
176
+
177
+ 参数:
178
+ start_hash: 起始commit哈希值(不包含)
179
+ end_hash: 结束commit哈希值(包含),如果为None则使用HEAD
180
+
181
+ 返回:
182
+ str: 差异内容或错误信息
183
+ """
184
+ try:
185
+ if end_hash is None:
186
+ # 如果end_hash为None,使用HEAD
187
+ end_hash = "HEAD"
188
+
189
+ # 检查start_hash是否存在
190
+ start_check = subprocess.run(
191
+ ["git", "rev-parse", "--verify", start_hash],
192
+ stderr=subprocess.PIPE,
193
+ stdout=subprocess.PIPE,
194
+ )
195
+ if start_check.returncode != 0:
196
+ return f"起始commit不存在: {start_hash}"
197
+
198
+ # 检查end_hash是否存在
199
+ end_check = subprocess.run(
200
+ ["git", "rev-parse", "--verify", end_hash],
201
+ stderr=subprocess.PIPE,
202
+ stdout=subprocess.PIPE,
203
+ )
204
+ if end_check.returncode != 0:
205
+ return f"结束commit不存在: {end_hash}"
206
+
207
+ # 获取两个commit之间的差异
208
+ result = subprocess.run(
209
+ ["git", "diff", f"{start_hash}..{end_hash}"],
210
+ capture_output=True,
211
+ text=False,
212
+ check=True,
213
+ )
214
+
215
+ try:
216
+ return result.stdout.decode("utf-8")
217
+ except UnicodeDecodeError:
218
+ return result.stdout.decode("utf-8", errors="replace")
219
+
220
+ except subprocess.CalledProcessError as e:
221
+ error_msg = e.stderr.decode("utf-8", errors="replace") if e.stderr else str(e)
222
+ return f"获取commit差异失败: {error_msg}"
223
+ except Exception as e:
224
+ return f"发生意外错误: {str(e)}"
225
+
226
+
169
227
  def revert_file(filepath: str) -> None:
170
228
  """增强版git恢复,处理新文件"""
171
229
  import subprocess
@@ -185,7 +243,7 @@ def revert_file(filepath: str) -> None:
185
243
  subprocess.run(["git", "clean", "-f", "--", filepath], check=True)
186
244
  except subprocess.CalledProcessError as e:
187
245
  error_msg = e.stderr.decode("utf-8", errors="replace") if e.stderr else str(e)
188
- PrettyOutput.print(f"恢复文件失败: {error_msg}", OutputType.ERROR)
246
+ print(f"恢复文件失败: {error_msg}")
189
247
 
190
248
 
191
249
  # 修改后的恢复函数
@@ -206,7 +264,101 @@ def revert_change() -> None:
206
264
  subprocess.run(["git", "reset", "--hard", "HEAD"], check=True)
207
265
  subprocess.run(["git", "clean", "-fd"], check=True)
208
266
  except subprocess.CalledProcessError as e:
209
- PrettyOutput.print(f"恢复更改失败: {str(e)}", OutputType.ERROR)
267
+ print(f"恢复更改失败: {str(e)}")
268
+
269
+
270
+ def detect_large_code_deletion(threshold: int = 30) -> Optional[Dict[str, int]]:
271
+ """检测是否有大量代码删除
272
+
273
+ 参数:
274
+ threshold: 净删除行数阈值,默认200行
275
+
276
+ 返回:
277
+ Optional[Dict[str, int]]: 如果检测到大量删除,返回包含统计信息的字典:
278
+ {
279
+ 'insertions': int, # 新增行数
280
+ 'deletions': int, # 删除行数
281
+ 'net_deletions': int # 净删除行数
282
+ }
283
+ 如果没有大量删除或发生错误,返回None
284
+ """
285
+ try:
286
+ # 临时暂存所有文件以便获取完整的diff统计
287
+ subprocess.run(["git", "add", "-N", "."], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
288
+
289
+ # 检查是否有HEAD
290
+ head_check = subprocess.run(
291
+ ["git", "rev-parse", "--verify", "HEAD"],
292
+ stderr=subprocess.DEVNULL,
293
+ stdout=subprocess.DEVNULL,
294
+ )
295
+
296
+ if head_check.returncode == 0:
297
+ # 有HEAD,获取相对于HEAD的diff统计
298
+ diff_result = subprocess.run(
299
+ ["git", "diff", "HEAD", "--shortstat"],
300
+ capture_output=True,
301
+ text=True,
302
+ encoding="utf-8",
303
+ errors="replace",
304
+ check=False,
305
+ )
306
+ else:
307
+ # 空仓库,获取工作区diff统计
308
+ diff_result = subprocess.run(
309
+ ["git", "diff", "--shortstat"],
310
+ capture_output=True,
311
+ text=True,
312
+ encoding="utf-8",
313
+ errors="replace",
314
+ check=False,
315
+ )
316
+
317
+ # 重置暂存区
318
+ subprocess.run(["git", "reset"], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
319
+
320
+ # 解析插入和删除行数
321
+ if diff_result.returncode == 0 and diff_result.stdout:
322
+ insertions = 0
323
+ deletions = 0
324
+ insertions_match = re.search(r"(\d+)\s+insertions?\(\+\)", diff_result.stdout)
325
+ deletions_match = re.search(r"(\d+)\s+deletions?\(\-\)", diff_result.stdout)
326
+ if insertions_match:
327
+ insertions = int(insertions_match.group(1))
328
+ if deletions_match:
329
+ deletions = int(deletions_match.group(1))
330
+
331
+ # 检查是否有大量代码删除(净删除超过阈值)
332
+ net_deletions = deletions - insertions
333
+ if net_deletions > threshold:
334
+ return {
335
+ 'insertions': insertions,
336
+ 'deletions': deletions,
337
+ 'net_deletions': net_deletions
338
+ }
339
+ return None
340
+ except Exception:
341
+ # 如果检查过程中出错,返回None
342
+ return None
343
+
344
+
345
+ # confirm_large_code_deletion函数已废弃,统一使用大模型询问
346
+
347
+
348
+ def check_large_code_deletion(threshold: int = 30) -> bool:
349
+ """检查是否有大量代码删除
350
+
351
+ 参数:
352
+ threshold: 净删除行数阈值,默认200行
353
+
354
+ 返回:
355
+ bool: 始终返回True,由调用方统一处理大模型询问
356
+ """
357
+ # 检测功能现在由调用方统一处理
358
+ return True
359
+
360
+ # 直接返回True,让调用方统一处理大模型询问
361
+ return True
210
362
 
211
363
 
212
364
  def handle_commit_workflow() -> bool:
@@ -229,6 +381,10 @@ def handle_commit_workflow() -> bool:
229
381
  if not has_uncommitted_changes():
230
382
  return False
231
383
 
384
+ # 在提交前检查是否有大量代码删除
385
+ if not check_large_code_deletion():
386
+ return False
387
+
232
388
  # 获取当前分支的提交总数
233
389
  commit_result = subprocess.run(
234
390
  ["git", "rev-list", "--count", "HEAD"], capture_output=True, text=True
@@ -291,7 +447,13 @@ def get_modified_line_ranges() -> Dict[str, List[Tuple[int, int]]]:
291
447
  行号从1开始。
292
448
  """
293
449
  # 获取所有文件的Git差异
294
- diff_output = os.popen("git show").read()
450
+ # 仅用于解析修改行范围,减少上下文以降低输出体积和解析成本
451
+ proc = subprocess.run(
452
+ ["git", "show", "--no-color"],
453
+ capture_output=True,
454
+ text=True,
455
+ )
456
+ diff_output = proc.stdout
295
457
 
296
458
  # 解析差异以获取修改的文件及其行范围
297
459
  result: Dict[str, List[Tuple[int, int]]] = {}
@@ -329,7 +491,7 @@ def is_file_in_git_repo(filepath: str) -> bool:
329
491
 
330
492
  # 检查文件路径是否在仓库根目录下
331
493
  return os.path.abspath(filepath).startswith(os.path.abspath(repo_root))
332
- except:
494
+ except Exception:
333
495
  return False
334
496
 
335
497
 
@@ -394,23 +556,17 @@ def check_and_update_git_repo(repo_path: str) -> bool:
394
556
  and remote_tag_result.returncode == 0
395
557
  and local_tag_result.stdout.strip() != remote_tag_result.stdout.strip()
396
558
  ):
397
- PrettyOutput.print(
398
- f"检测到新版本tag {remote_tag_result.stdout.strip()},正在更新Jarvis...",
399
- OutputType.INFO,
400
- )
559
+ print(f"ℹ️ 检测到新版本tag {remote_tag_result.stdout.strip()},正在更新Jarvis...")
401
560
  subprocess.run(
402
561
  ["git", "checkout", remote_tag_result.stdout.strip()],
403
562
  cwd=git_root,
404
563
  check=True,
405
564
  )
406
- PrettyOutput.print(
407
- f"Jarvis已更新到tag {remote_tag_result.stdout.strip()}",
408
- OutputType.SUCCESS,
409
- )
565
+ print(f"✅ Jarvis已更新到tag {remote_tag_result.stdout.strip()}")
410
566
 
411
567
  # 执行pip安装更新代码
412
568
  try:
413
- PrettyOutput.print("正在安装更新后的代码...", OutputType.INFO)
569
+ print("ℹ️ 正在安装更新后的代码...")
414
570
 
415
571
  # 检查是否在虚拟环境中
416
572
  in_venv = hasattr(sys, "real_prefix") or (
@@ -467,7 +623,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
467
623
  )
468
624
 
469
625
  if result.returncode == 0:
470
- PrettyOutput.print("代码更新安装成功", OutputType.SUCCESS)
626
+ print("代码更新安装成功")
471
627
  return True
472
628
 
473
629
  # 处理权限错误
@@ -485,23 +641,21 @@ def check_and_update_git_repo(repo_path: str) -> bool:
485
641
  text=True,
486
642
  )
487
643
  if user_result.returncode == 0:
488
- PrettyOutput.print("用户级代码安装成功", OutputType.SUCCESS)
644
+ print("用户级代码安装成功")
489
645
  return True
490
646
  error_msg = user_result.stderr.strip()
491
647
 
492
- PrettyOutput.print(f"代码安装失败: {error_msg}", OutputType.ERROR)
648
+ print(f"代码安装失败: {error_msg}")
493
649
  return False
494
650
  except Exception as e:
495
- PrettyOutput.print(
496
- f"安装过程中发生意外错误: {str(e)}", OutputType.ERROR
497
- )
651
+ print(f"❌ 安装过程中发生意外错误: {str(e)}")
498
652
  return False
499
653
  # 更新检查日期文件
500
654
  with open(last_check_file, "w") as f:
501
655
  f.write(today_str)
502
656
  return False
503
657
  except Exception as e:
504
- PrettyOutput.print(f"Git仓库更新检查失败: {e}", OutputType.WARNING)
658
+ print(f"⚠️ Git仓库更新检查失败: {e}")
505
659
  return False
506
660
  finally:
507
661
  os.chdir(curr_dir)
@@ -528,18 +682,16 @@ def get_diff_file_list() -> List[str]:
528
682
  subprocess.run(["git", "reset"], check=True)
529
683
 
530
684
  if result.returncode != 0:
531
- PrettyOutput.print(
532
- f"获取差异文件列表失败: {result.stderr}", OutputType.ERROR
533
- )
685
+ print(f"❌ 获取差异文件列表失败: {result.stderr}")
534
686
  return []
535
687
 
536
688
  return [f for f in result.stdout.splitlines() if f]
537
689
 
538
690
  except subprocess.CalledProcessError as e:
539
- PrettyOutput.print(f"获取差异文件列表失败: {str(e)}", OutputType.ERROR)
691
+ print(f"获取差异文件列表失败: {str(e)}")
540
692
  return []
541
693
  except Exception as e:
542
- PrettyOutput.print(f"获取差异文件列表异常: {str(e)}", OutputType.ERROR)
694
+ print(f"获取差异文件列表异常: {str(e)}")
543
695
  return []
544
696
 
545
697
 
@@ -610,7 +762,7 @@ def get_recent_commits_with_files() -> List[Dict[str, Any]]:
610
762
  if files_result.returncode == 0:
611
763
  file_lines = files_result.stdout.splitlines()
612
764
  unique_files: Set[str] = set(filter(None, file_lines))
613
- commit["files"] = list(unique_files)[:20] # type: ignore[list-item] # 限制最多20个文件
765
+ commit["files"] = list(unique_files)[:20] # 限制最多20个文件
614
766
 
615
767
  return commits
616
768
 
@@ -688,10 +840,8 @@ def confirm_add_new_files() -> None:
688
840
  need_confirm = True
689
841
 
690
842
  if output_lines:
691
- PrettyOutput.print(
692
- "\n".join(output_lines),
693
- OutputType.WARNING if need_confirm else OutputType.INFO,
694
- )
843
+ emoji = "⚠️ " if need_confirm else "ℹ️ "
844
+ print(emoji + ("\n" + emoji).join(output_lines))
695
845
 
696
846
  return need_confirm
697
847
 
@@ -705,8 +855,47 @@ def confirm_add_new_files() -> None:
705
855
 
706
856
  if not user_confirm(
707
857
  "是否要添加这些变更(如果不需要请修改.gitignore文件以忽略不需要的文件)?",
708
- False,
858
+ True,
709
859
  ):
860
+ # 用户选择 N:自动将未跟踪文件列表添加到仓库根目录的 .gitignore
861
+ try:
862
+ repo_root_result = subprocess.run(
863
+ ["git", "rev-parse", "--show-toplevel"],
864
+ capture_output=True,
865
+ text=True,
866
+ check=True,
867
+ )
868
+ repo_root = repo_root_result.stdout.strip() or "."
869
+ except Exception:
870
+ repo_root = "."
871
+ gitignore_path = os.path.join(repo_root, ".gitignore")
872
+
873
+ # 仅对未跟踪的新文件进行忽略(已跟踪文件无法通过 .gitignore 忽略)
874
+ files_to_ignore = sorted(set(new_files))
875
+
876
+ # 读取已存在的 .gitignore 以避免重复添加
877
+ existing_lines: Set[str] = set()
878
+ try:
879
+ if os.path.exists(gitignore_path):
880
+ with open(gitignore_path, "r", encoding="utf-8") as f:
881
+ existing_lines = set(line.strip() for line in f if line.strip())
882
+ except Exception:
883
+ existing_lines = set()
884
+
885
+ # 追加未存在的文件路径到 .gitignore(使用相对于仓库根目录的路径)
886
+ try:
887
+ with open(gitignore_path, "a", encoding="utf-8") as f:
888
+ for file in files_to_ignore:
889
+ abs_path = os.path.abspath(file)
890
+ rel_path = os.path.relpath(abs_path, repo_root)
891
+ # 避免无效的相对路径(不应出现 .. 前缀),有则回退用原始值
892
+ entry = rel_path if not rel_path.startswith("..") else file
893
+ if entry not in existing_lines:
894
+ f.write(entry + "\n")
895
+ print("ℹ️ 已将未跟踪文件添加到 .gitignore,正在重新检测...")
896
+ except Exception as e:
897
+ print(f"⚠️ 更新 .gitignore 失败: {str(e)}")
898
+
710
899
  continue
711
900
 
712
901
  break
@@ -19,9 +19,9 @@ MAX_HISTORY_SIZE = 50
19
19
  short_term_memories: List[Dict[str, Any]] = []
20
20
  MAX_SHORT_TERM_MEMORIES = 100
21
21
 
22
- import colorama
23
- from rich.console import Console
24
- from rich.theme import Theme
22
+ import colorama # noqa: E402
23
+ from rich.console import Console # noqa: E402
24
+ from rich.theme import Theme # noqa: E402
25
25
 
26
26
  # 初始化colorama以支持跨平台的彩色文本
27
27
  colorama.init()
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- import requests
3
+ import requests # type: ignore[import-untyped]
4
4
  from typing import Any, Dict, Optional, Generator
5
5
 
6
6