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,294 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 对话记录器模块
5
+
6
+ 提供对话记录和管理功能,支持多会话、JSONL格式存储、自动清理等功能。
7
+ """
8
+
9
+ import atexit
10
+ import json
11
+ import uuid
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Any
15
+ from typing import Dict
16
+ from typing import List
17
+ from typing import Optional
18
+
19
+ from .config import get_data_dir
20
+
21
+ # 全局实例变量
22
+ _global_recorder: Optional["DialogueRecorder"] = None
23
+
24
+
25
+ class DialogueRecorder:
26
+ """对话记录器类
27
+
28
+ 用于记录和管理对话历史,支持多会话、JSONL格式存储。
29
+
30
+ 特性:
31
+ - 自动生成会话ID
32
+ - JSONL格式存储,每行一个消息记录
33
+ - 支持元数据扩展
34
+ - 自动清理旧会话
35
+ - 进程安全的文件写入
36
+ """
37
+
38
+ def __init__(self, session_id: Optional[str] = None):
39
+ """初始化对话记录器
40
+
41
+ Args:
42
+ session_id: 会话ID,如果为None则使用当前会话ID
43
+ """
44
+ self.data_dir = Path(get_data_dir()) / "dialogues"
45
+ self._session_id_cache: Optional[str] = None
46
+ self.session_id = session_id or self._get_current_session_id()
47
+ self._ensure_data_dir()
48
+ self._register_cleanup_hook()
49
+
50
+ def __del__(self):
51
+ """析构函数,确保清理资源"""
52
+ if hasattr(self, "_cleanup_registered"):
53
+ self._unregister_cleanup_hook()
54
+
55
+ def start_recording(self) -> str:
56
+ """开始新的对话记录
57
+
58
+ Returns:
59
+ str: 新生成的会话ID
60
+ """
61
+ new_session_id = str(uuid.uuid4())
62
+ return new_session_id
63
+
64
+ def record_message(
65
+ self, role: str, content: str, metadata: Optional[Dict[str, Any]] = None
66
+ ) -> None:
67
+ """记录消息
68
+
69
+ Args:
70
+ role: 消息角色(如'user', 'assistant', 'system')
71
+ content: 消息内容
72
+ metadata: 可选的元数据
73
+ """
74
+ record = {
75
+ "timestamp": datetime.now().isoformat(),
76
+ "role": role,
77
+ "content": content,
78
+ "metadata": metadata or {},
79
+ }
80
+ self._write_record(record)
81
+
82
+ def get_session_file_path(self) -> str:
83
+ """获取当前会话文件路径
84
+
85
+ Returns:
86
+ str: 当前会话文件完整路径
87
+ """
88
+ return str(self._get_session_path(self.session_id))
89
+
90
+ def get_all_sessions(self) -> List[str]:
91
+ """获取所有会话ID列表
92
+
93
+ Returns:
94
+ List[str]: 所有会话ID列表
95
+ """
96
+ if not self.data_dir.exists():
97
+ return []
98
+
99
+ session_files = self.data_dir.glob("*.jsonl")
100
+ sessions = []
101
+ for file_path in session_files:
102
+ session_id = file_path.stem
103
+ if file_path.is_file() and session_id:
104
+ sessions.append(session_id)
105
+
106
+ return sorted(sessions)
107
+
108
+ def read_session(self, session_id: str) -> List[Dict[str, Any]]:
109
+ """读取指定会话内容
110
+
111
+ Args:
112
+ session_id: 会话ID
113
+
114
+ Returns:
115
+ List[Dict[str, Any]]: 会话消息列表
116
+ """
117
+ file_path = self._get_session_path(session_id)
118
+ if not file_path.exists():
119
+ return []
120
+
121
+ messages = []
122
+ try:
123
+ with open(file_path, "r", encoding="utf-8") as f:
124
+ for line in f:
125
+ line = line.strip()
126
+ if line:
127
+ try:
128
+ message = json.loads(line)
129
+ messages.append(message)
130
+ except json.JSONDecodeError:
131
+ continue
132
+ except Exception:
133
+ pass
134
+ return messages
135
+
136
+ def cleanup_session(self, session_id: Optional[str] = None) -> None:
137
+ """清理指定会话文件
138
+
139
+ Args:
140
+ session_id: 会话ID,如果为None则清理当前会话
141
+ """
142
+ session_to_cleanup = session_id or self.session_id
143
+ file_path = self._get_session_path(session_to_cleanup)
144
+
145
+ if file_path.exists():
146
+ try:
147
+ file_path.unlink()
148
+ except Exception:
149
+ pass
150
+
151
+ def cleanup_all_sessions(self) -> None:
152
+ """清理所有会话文件"""
153
+ if not self.data_dir.exists():
154
+ return
155
+
156
+ session_files = list(self.data_dir.glob("*.jsonl"))
157
+ if not session_files:
158
+ return
159
+
160
+ for file_path in session_files:
161
+ try:
162
+ file_path.unlink()
163
+ except Exception:
164
+ pass
165
+
166
+ def get_session_count(self) -> int:
167
+ """获取总会话数量
168
+
169
+ Returns:
170
+ int: 总会话数量
171
+ """
172
+ return len(self.get_all_sessions())
173
+
174
+ def _ensure_data_dir(self) -> None:
175
+ """确保数据目录存在"""
176
+ try:
177
+ self.data_dir.mkdir(parents=True, exist_ok=True)
178
+ except Exception:
179
+ pass
180
+
181
+ def _get_current_session_id(self) -> str:
182
+ """获取当前会话ID"""
183
+ # 从环境变量或配置中获取当前会话ID
184
+ # 如果没有则生成一个新的
185
+ if self._session_id_cache is not None:
186
+ return self._session_id_cache
187
+
188
+ # 检查是否有现有的会话文件
189
+ existing_sessions = self.get_all_sessions()
190
+ if existing_sessions:
191
+ # 使用最新的会话
192
+ session_id = existing_sessions[-1]
193
+ self._session_id_cache = session_id
194
+ return session_id
195
+
196
+ # 生成新的会话ID
197
+ new_session_id = str(uuid.uuid4())
198
+ self._session_id_cache = new_session_id
199
+ return new_session_id
200
+
201
+ def _write_record(self, record: Dict[str, Any]) -> None:
202
+ """写入单条记录到文件"""
203
+ file_path = self._get_session_path(self.session_id)
204
+
205
+ try:
206
+ with open(file_path, "a", encoding="utf-8") as f:
207
+ json.dump(record, f, ensure_ascii=False)
208
+ f.write("\n")
209
+ except Exception:
210
+ pass
211
+
212
+ def _get_session_path(self, session_id: str) -> Path:
213
+ """获取会话文件路径"""
214
+ return self.data_dir / f"{session_id}.jsonl"
215
+
216
+ def _register_cleanup_hook(self) -> None:
217
+ """注册atexit清理钩子"""
218
+ if not hasattr(self, "_cleanup_registered"):
219
+ atexit.register(self._cleanup_on_exit)
220
+ self._cleanup_registered = True
221
+
222
+ def _unregister_cleanup_hook(self) -> None:
223
+ """注销atexit清理钩子"""
224
+ if hasattr(self, "_cleanup_registered"):
225
+ try:
226
+ atexit.unregister(self._cleanup_on_exit)
227
+ except ValueError:
228
+ # 钩子可能已被注销或从未注册
229
+ pass
230
+ del self._cleanup_registered
231
+
232
+ def _cleanup_on_exit(self) -> None:
233
+ """进程退出时的清理函数
234
+
235
+ 确保在进程正常和异常退出时清理当前会话文件
236
+ """
237
+ try:
238
+ # 只清理当前会话,避免影响其他会话
239
+ self.cleanup_session()
240
+ except Exception:
241
+ # 清理失败时不抛出异常,避免影响进程退出
242
+ pass
243
+
244
+
245
+ def get_global_recorder() -> DialogueRecorder:
246
+ """获取全局单例对话记录器
247
+
248
+ Returns:
249
+ DialogueRecorder: 全局对话记录器实例
250
+ """
251
+ global _global_recorder
252
+ if _global_recorder is None:
253
+ _global_recorder = DialogueRecorder()
254
+ return _global_recorder
255
+
256
+
257
+ def record_user_message(
258
+ content: str, metadata: Optional[Dict[str, Any]] = None
259
+ ) -> None:
260
+ """便捷函数:记录用户消息
261
+
262
+ Args:
263
+ content: 消息内容
264
+ metadata: 可选元数据
265
+ """
266
+ recorder = get_global_recorder()
267
+ recorder.record_message("user", content, metadata)
268
+
269
+
270
+ def record_assistant_message(
271
+ content: str, metadata: Optional[Dict[str, Any]] = None
272
+ ) -> None:
273
+ """便捷函数:记录助手消息
274
+
275
+ Args:
276
+ content: 消息内容
277
+ metadata: 可选元数据
278
+ """
279
+ recorder = get_global_recorder()
280
+ recorder.record_message("assistant", content, metadata)
281
+
282
+
283
+ def get_current_session_path() -> str:
284
+ """便捷函数:获取当前会话文件路径
285
+
286
+ Returns:
287
+ str: 当前会话文件路径
288
+ """
289
+ recorder = get_global_recorder()
290
+ return recorder.get_session_file_path()
291
+
292
+
293
+ # 在模块导入时自动初始化和注册清理钩子
294
+ _global_recorder = get_global_recorder()
@@ -3,6 +3,7 @@ import os
3
3
  from pathlib import Path
4
4
  from typing import List
5
5
 
6
+ from jarvis.jarvis_utils.output import PrettyOutput
6
7
 
7
8
  # 设置tiktoken缓存目录
8
9
  script_dir = Path(__file__).parent
@@ -30,8 +31,10 @@ def get_context_token_count(text: str) -> int:
30
31
  # 调整token计算为原来的10/7倍
31
32
  return int(len(encoding.encode(text)) * 10 / 7)
32
33
  except Exception as e:
33
- print(f"⚠️ 计算token失败: {str(e)}")
34
- return int(len(text) // 4 * 10 / 7) # 每个token大约4个字符的粗略估计,调整为10/7倍
34
+ PrettyOutput.auto_print(f"⚠️ 计算token失败: {str(e)}")
35
+ return int(
36
+ len(text) // 4 * 10 / 7
37
+ ) # 每个token大约4个字符的粗略估计,调整为10/7倍
35
38
 
36
39
 
37
40
  def split_text_into_chunks(
@@ -80,6 +83,6 @@ def split_text_into_chunks(
80
83
  return chunks
81
84
 
82
85
  except Exception as e:
83
- print(f"⚠️ 文本分割失败: {str(e)}")
86
+ PrettyOutput.auto_print(f"⚠️ 文本分割失败: {str(e)}")
84
87
  # 发生错误时回退到简单的字符分割
85
88
  return [text[i : i + max_length] for i in range(0, len(text), max_length)]
@@ -75,7 +75,13 @@ class TextFileProcessor(FileProcessor):
75
75
  continue
76
76
 
77
77
  if not detected_encoding:
78
- raise UnicodeDecodeError(f"Failed to decode file with supported encodings: {file_path}") # type: ignore
78
+ raise UnicodeDecodeError(
79
+ "utf-8",
80
+ b"",
81
+ 0,
82
+ 0,
83
+ f"Failed to decode file with supported encodings: {file_path}",
84
+ )
79
85
 
80
86
  # Use the detected encoding to read the file
81
87
  with open(file_path, "r", encoding=detected_encoding, errors="ignore") as f:
@@ -1,8 +1,15 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """FZF选择器工具。"""
3
+
3
4
  import shutil
4
5
  import subprocess
5
- from typing import List, Optional, Union, Dict, Any, cast
6
+ from typing import Any
7
+ from typing import Dict
8
+ from typing import List
9
+ from typing import Optional
10
+ from typing import Union
11
+ from typing import cast
12
+
6
13
 
7
14
  def fzf_select(
8
15
  options: Union[List[str], List[Dict[str, Any]]],
@@ -29,8 +36,7 @@ def fzf_select(
29
36
  if isinstance(options[0], dict):
30
37
  if key is None:
31
38
  raise ValueError("A key must be provided for a list of dicts.")
32
- options_dict = cast(List[Dict[str, Any]], options)
33
- input_lines = [str(item.get(key, "")) for item in options_dict]
39
+ input_lines = [str(cast(Dict[str, Any], item).get(key, "")) for item in options]
34
40
  else:
35
41
  input_lines = [str(item) for item in options]
36
42
 
@@ -1,4 +1,3 @@
1
- # -*- coding: utf-8 -*-
2
1
  """
3
2
  Git工具模块
4
3
  该模块提供了与Git仓库交互的工具。
@@ -9,14 +8,25 @@ Git工具模块
9
8
  - 获取最新提交的哈希值
10
9
  - 从Git差异中提取修改的行范围
11
10
  """
11
+
12
12
  import datetime
13
13
  import os
14
+
15
+ from jarvis.jarvis_utils.output import PrettyOutput
16
+
17
+ # -*- coding: utf-8 -*-
14
18
  import re
15
19
  import subprocess
16
20
  import sys
17
- from typing import Any, Dict, List, Optional, Set, Tuple
18
-
19
- from jarvis.jarvis_utils.config import get_data_dir, is_confirm_before_apply_patch
21
+ from typing import Any
22
+ from typing import Dict
23
+ from typing import List
24
+ from typing import Optional
25
+ from typing import Set
26
+ from typing import Tuple
27
+
28
+ from jarvis.jarvis_utils.config import get_data_dir
29
+ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
20
30
  from jarvis.jarvis_utils.input import user_confirm
21
31
  from jarvis.jarvis_utils.utils import is_rag_installed
22
32
 
@@ -110,7 +120,7 @@ def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]
110
120
  )
111
121
  if result.returncode != 0:
112
122
  error_msg = result.stderr.decode("utf-8", errors="replace")
113
- print(f"❌ 获取commit历史失败: {error_msg}")
123
+ PrettyOutput.auto_print(f"❌ 获取commit历史失败: {error_msg}")
114
124
  return []
115
125
 
116
126
  output = result.stdout.decode("utf-8", errors="replace")
@@ -122,7 +132,7 @@ def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]
122
132
  return commits
123
133
 
124
134
  except Exception as e:
125
- print(f"❌ 获取commit历史异常: {str(e)}")
135
+ PrettyOutput.auto_print(f"❌ 获取commit历史异常: {str(e)}")
126
136
  return []
127
137
 
128
138
 
@@ -185,7 +195,7 @@ def get_diff_between_commits(start_hash: str, end_hash: Optional[str] = None) ->
185
195
  if end_hash is None:
186
196
  # 如果end_hash为None,使用HEAD
187
197
  end_hash = "HEAD"
188
-
198
+
189
199
  # 检查start_hash是否存在
190
200
  start_check = subprocess.run(
191
201
  ["git", "rev-parse", "--verify", start_hash],
@@ -194,7 +204,7 @@ def get_diff_between_commits(start_hash: str, end_hash: Optional[str] = None) ->
194
204
  )
195
205
  if start_check.returncode != 0:
196
206
  return f"起始commit不存在: {start_hash}"
197
-
207
+
198
208
  # 检查end_hash是否存在
199
209
  end_check = subprocess.run(
200
210
  ["git", "rev-parse", "--verify", end_hash],
@@ -203,7 +213,7 @@ def get_diff_between_commits(start_hash: str, end_hash: Optional[str] = None) ->
203
213
  )
204
214
  if end_check.returncode != 0:
205
215
  return f"结束commit不存在: {end_hash}"
206
-
216
+
207
217
  # 获取两个commit之间的差异
208
218
  result = subprocess.run(
209
219
  ["git", "diff", f"{start_hash}..{end_hash}"],
@@ -211,7 +221,7 @@ def get_diff_between_commits(start_hash: str, end_hash: Optional[str] = None) ->
211
221
  text=False,
212
222
  check=True,
213
223
  )
214
-
224
+
215
225
  try:
216
226
  return result.stdout.decode("utf-8")
217
227
  except UnicodeDecodeError:
@@ -243,7 +253,7 @@ def revert_file(filepath: str) -> None:
243
253
  subprocess.run(["git", "clean", "-f", "--", filepath], check=True)
244
254
  except subprocess.CalledProcessError as e:
245
255
  error_msg = e.stderr.decode("utf-8", errors="replace") if e.stderr else str(e)
246
- print(f"❌ 恢复文件失败: {error_msg}")
256
+ PrettyOutput.auto_print(f"❌ 恢复文件失败: {error_msg}")
247
257
 
248
258
 
249
259
  # 修改后的恢复函数
@@ -264,15 +274,15 @@ def revert_change() -> None:
264
274
  subprocess.run(["git", "reset", "--hard", "HEAD"], check=True)
265
275
  subprocess.run(["git", "clean", "-fd"], check=True)
266
276
  except subprocess.CalledProcessError as e:
267
- print(f"❌ 恢复更改失败: {str(e)}")
277
+ PrettyOutput.auto_print(f"❌ 恢复更改失败: {str(e)}")
268
278
 
269
279
 
270
280
  def detect_large_code_deletion(threshold: int = 30) -> Optional[Dict[str, int]]:
271
281
  """检测是否有大量代码删除
272
-
282
+
273
283
  参数:
274
284
  threshold: 净删除行数阈值,默认200行
275
-
285
+
276
286
  返回:
277
287
  Optional[Dict[str, int]]: 如果检测到大量删除,返回包含统计信息的字典:
278
288
  {
@@ -284,15 +294,20 @@ def detect_large_code_deletion(threshold: int = 30) -> Optional[Dict[str, int]]:
284
294
  """
285
295
  try:
286
296
  # 临时暂存所有文件以便获取完整的diff统计
287
- subprocess.run(["git", "add", "-N", "."], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
288
-
297
+ subprocess.run(
298
+ ["git", "add", "-N", "."],
299
+ check=False,
300
+ stdout=subprocess.DEVNULL,
301
+ stderr=subprocess.DEVNULL,
302
+ )
303
+
289
304
  # 检查是否有HEAD
290
305
  head_check = subprocess.run(
291
306
  ["git", "rev-parse", "--verify", "HEAD"],
292
307
  stderr=subprocess.DEVNULL,
293
308
  stdout=subprocess.DEVNULL,
294
309
  )
295
-
310
+
296
311
  if head_check.returncode == 0:
297
312
  # 有HEAD,获取相对于HEAD的diff统计
298
313
  diff_result = subprocess.run(
@@ -313,28 +328,35 @@ def detect_large_code_deletion(threshold: int = 30) -> Optional[Dict[str, int]]:
313
328
  errors="replace",
314
329
  check=False,
315
330
  )
316
-
331
+
317
332
  # 重置暂存区
318
- subprocess.run(["git", "reset"], check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
319
-
333
+ subprocess.run(
334
+ ["git", "reset"],
335
+ check=False,
336
+ stdout=subprocess.DEVNULL,
337
+ stderr=subprocess.DEVNULL,
338
+ )
339
+
320
340
  # 解析插入和删除行数
321
341
  if diff_result.returncode == 0 and diff_result.stdout:
322
342
  insertions = 0
323
343
  deletions = 0
324
- insertions_match = re.search(r"(\d+)\s+insertions?\(\+\)", diff_result.stdout)
344
+ insertions_match = re.search(
345
+ r"(\d+)\s+insertions?\(\+\)", diff_result.stdout
346
+ )
325
347
  deletions_match = re.search(r"(\d+)\s+deletions?\(\-\)", diff_result.stdout)
326
348
  if insertions_match:
327
349
  insertions = int(insertions_match.group(1))
328
350
  if deletions_match:
329
351
  deletions = int(deletions_match.group(1))
330
-
352
+
331
353
  # 检查是否有大量代码删除(净删除超过阈值)
332
354
  net_deletions = deletions - insertions
333
355
  if net_deletions > threshold:
334
356
  return {
335
- 'insertions': insertions,
336
- 'deletions': deletions,
337
- 'net_deletions': net_deletions
357
+ "insertions": insertions,
358
+ "deletions": deletions,
359
+ "net_deletions": net_deletions,
338
360
  }
339
361
  return None
340
362
  except Exception:
@@ -347,16 +369,16 @@ def detect_large_code_deletion(threshold: int = 30) -> Optional[Dict[str, int]]:
347
369
 
348
370
  def check_large_code_deletion(threshold: int = 30) -> bool:
349
371
  """检查是否有大量代码删除
350
-
372
+
351
373
  参数:
352
374
  threshold: 净删除行数阈值,默认200行
353
-
375
+
354
376
  返回:
355
377
  bool: 始终返回True,由调用方统一处理大模型询问
356
378
  """
357
379
  # 检测功能现在由调用方统一处理
358
380
  return True
359
-
381
+
360
382
  # 直接返回True,让调用方统一处理大模型询问
361
383
  return True
362
384
 
@@ -556,17 +578,21 @@ def check_and_update_git_repo(repo_path: str) -> bool:
556
578
  and remote_tag_result.returncode == 0
557
579
  and local_tag_result.stdout.strip() != remote_tag_result.stdout.strip()
558
580
  ):
559
- print(f"ℹ️ 检测到新版本tag {remote_tag_result.stdout.strip()},正在更新Jarvis...")
581
+ PrettyOutput.auto_print(
582
+ f"ℹ️ 检测到新版本tag {remote_tag_result.stdout.strip()},正在更新Jarvis..."
583
+ )
560
584
  subprocess.run(
561
585
  ["git", "checkout", remote_tag_result.stdout.strip()],
562
586
  cwd=git_root,
563
587
  check=True,
564
588
  )
565
- print(f"✅ Jarvis已更新到tag {remote_tag_result.stdout.strip()}")
589
+ PrettyOutput.auto_print(
590
+ f"✅ Jarvis已更新到tag {remote_tag_result.stdout.strip()}"
591
+ )
566
592
 
567
593
  # 执行pip安装更新代码
568
594
  try:
569
- print("ℹ️ 正在安装更新后的代码...")
595
+ PrettyOutput.auto_print("ℹ️ 正在安装更新后的代码...")
570
596
 
571
597
  # 检查是否在虚拟环境中
572
598
  in_venv = hasattr(sys, "real_prefix") or (
@@ -575,6 +601,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
575
601
 
576
602
  # 检测 uv 可用性:优先虚拟环境内的 uv,其次 PATH 中的 uv
577
603
  from shutil import which as _which
604
+
578
605
  uv_executable = None
579
606
  if sys.platform == "win32":
580
607
  venv_uv = os.path.join(sys.prefix, "Scripts", "uv.exe")
@@ -623,7 +650,7 @@ def check_and_update_git_repo(repo_path: str) -> bool:
623
650
  )
624
651
 
625
652
  if result.returncode == 0:
626
- print("✅ 代码更新安装成功")
653
+ PrettyOutput.auto_print("✅ 代码更新安装成功")
627
654
  return True
628
655
 
629
656
  # 处理权限错误
@@ -641,21 +668,21 @@ def check_and_update_git_repo(repo_path: str) -> bool:
641
668
  text=True,
642
669
  )
643
670
  if user_result.returncode == 0:
644
- print("✅ 用户级代码安装成功")
671
+ PrettyOutput.auto_print("✅ 用户级代码安装成功")
645
672
  return True
646
673
  error_msg = user_result.stderr.strip()
647
674
 
648
- print(f"❌ 代码安装失败: {error_msg}")
675
+ PrettyOutput.auto_print(f"❌ 代码安装失败: {error_msg}")
649
676
  return False
650
677
  except Exception as e:
651
- print(f"❌ 安装过程中发生意外错误: {str(e)}")
678
+ PrettyOutput.auto_print(f"❌ 安装过程中发生意外错误: {str(e)}")
652
679
  return False
653
680
  # 更新检查日期文件
654
681
  with open(last_check_file, "w") as f:
655
682
  f.write(today_str)
656
683
  return False
657
684
  except Exception as e:
658
- print(f"⚠️ Git仓库更新检查失败: {e}")
685
+ PrettyOutput.auto_print(f"⚠️ Git仓库更新检查失败: {e}")
659
686
  return False
660
687
  finally:
661
688
  os.chdir(curr_dir)
@@ -682,16 +709,16 @@ def get_diff_file_list() -> List[str]:
682
709
  subprocess.run(["git", "reset"], check=True)
683
710
 
684
711
  if result.returncode != 0:
685
- print(f"❌ 获取差异文件列表失败: {result.stderr}")
712
+ PrettyOutput.auto_print(f"❌ 获取差异文件列表失败: {result.stderr}")
686
713
  return []
687
714
 
688
715
  return [f for f in result.stdout.splitlines() if f]
689
716
 
690
717
  except subprocess.CalledProcessError as e:
691
- print(f"❌ 获取差异文件列表失败: {str(e)}")
718
+ PrettyOutput.auto_print(f"❌ 获取差异文件列表失败: {str(e)}")
692
719
  return []
693
720
  except Exception as e:
694
- print(f"❌ 获取差异文件列表异常: {str(e)}")
721
+ PrettyOutput.auto_print(f"❌ 获取差异文件列表异常: {str(e)}")
695
722
  return []
696
723
 
697
724
 
@@ -841,7 +868,7 @@ def confirm_add_new_files() -> None:
841
868
 
842
869
  if output_lines:
843
870
  emoji = "⚠️ " if need_confirm else "ℹ️ "
844
- print(emoji + ("\n" + emoji).join(output_lines))
871
+ PrettyOutput.auto_print(emoji + ("\n" + emoji).join(output_lines))
845
872
 
846
873
  return need_confirm
847
874
 
@@ -892,9 +919,11 @@ def confirm_add_new_files() -> None:
892
919
  entry = rel_path if not rel_path.startswith("..") else file
893
920
  if entry not in existing_lines:
894
921
  f.write(entry + "\n")
895
- print("ℹ️ 已将未跟踪文件添加到 .gitignore,正在重新检测...")
922
+ PrettyOutput.auto_print(
923
+ "ℹ️ 已将未跟踪文件添加到 .gitignore,正在重新检测..."
924
+ )
896
925
  except Exception as e:
897
- print(f"⚠️ 更新 .gitignore 失败: {str(e)}")
926
+ PrettyOutput.auto_print(f"⚠️ 更新 .gitignore 失败: {str(e)}")
898
927
 
899
928
  continue
900
929