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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +567 -222
- jarvis/jarvis_agent/agent_manager.py +19 -12
- jarvis/jarvis_agent/builtin_input_handler.py +79 -11
- jarvis/jarvis_agent/config_editor.py +7 -2
- jarvis/jarvis_agent/event_bus.py +24 -13
- jarvis/jarvis_agent/events.py +19 -1
- jarvis/jarvis_agent/file_context_handler.py +67 -64
- jarvis/jarvis_agent/file_methodology_manager.py +38 -24
- jarvis/jarvis_agent/jarvis.py +186 -114
- jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
- jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
- jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
- jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
- jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
- jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
- jarvis/jarvis_agent/language_support_info.py +250 -219
- jarvis/jarvis_agent/main.py +19 -23
- jarvis/jarvis_agent/memory_manager.py +9 -6
- jarvis/jarvis_agent/methodology_share_manager.py +21 -15
- jarvis/jarvis_agent/output_handler.py +4 -2
- jarvis/jarvis_agent/prompt_builder.py +7 -6
- jarvis/jarvis_agent/prompt_manager.py +113 -8
- jarvis/jarvis_agent/prompts.py +317 -85
- jarvis/jarvis_agent/protocols.py +5 -2
- jarvis/jarvis_agent/run_loop.py +192 -32
- jarvis/jarvis_agent/session_manager.py +7 -3
- jarvis/jarvis_agent/share_manager.py +23 -13
- jarvis/jarvis_agent/shell_input_handler.py +12 -8
- jarvis/jarvis_agent/stdio_redirect.py +25 -26
- jarvis/jarvis_agent/task_analyzer.py +29 -23
- jarvis/jarvis_agent/task_list.py +869 -0
- jarvis/jarvis_agent/task_manager.py +26 -23
- jarvis/jarvis_agent/tool_executor.py +6 -5
- jarvis/jarvis_agent/tool_share_manager.py +24 -14
- jarvis/jarvis_agent/user_interaction.py +3 -3
- jarvis/jarvis_agent/utils.py +9 -1
- jarvis/jarvis_agent/web_bridge.py +37 -17
- jarvis/jarvis_agent/web_output_sink.py +5 -2
- jarvis/jarvis_agent/web_server.py +165 -36
- jarvis/jarvis_c2rust/__init__.py +1 -1
- jarvis/jarvis_c2rust/cli.py +260 -141
- jarvis/jarvis_c2rust/collector.py +37 -18
- jarvis/jarvis_c2rust/constants.py +60 -0
- jarvis/jarvis_c2rust/library_replacer.py +242 -1010
- jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
- jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
- jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
- jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
- jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
- jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
- jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
- jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
- jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
- jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
- jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
- jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
- jarvis/jarvis_c2rust/loaders.py +28 -10
- jarvis/jarvis_c2rust/models.py +5 -2
- jarvis/jarvis_c2rust/optimizer.py +192 -1974
- jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
- jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
- jarvis/jarvis_c2rust/optimizer_config.py +49 -0
- jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
- jarvis/jarvis_c2rust/optimizer_options.py +48 -0
- jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
- jarvis/jarvis_c2rust/optimizer_report.py +52 -0
- jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
- jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
- jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
- jarvis/jarvis_c2rust/scanner.py +229 -166
- jarvis/jarvis_c2rust/transpiler.py +531 -2732
- jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
- jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
- jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
- jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
- jarvis/jarvis_c2rust/transpiler_config.py +178 -0
- jarvis/jarvis_c2rust/transpiler_context.py +122 -0
- jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
- jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
- jarvis/jarvis_c2rust/transpiler_git.py +163 -0
- jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
- jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
- jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
- jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
- jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
- jarvis/jarvis_c2rust/utils.py +269 -79
- jarvis/jarvis_code_agent/after_change.py +233 -0
- jarvis/jarvis_code_agent/build_validation_config.py +37 -30
- jarvis/jarvis_code_agent/builtin_rules.py +68 -0
- jarvis/jarvis_code_agent/code_agent.py +976 -1517
- jarvis/jarvis_code_agent/code_agent_build.py +227 -0
- jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
- jarvis/jarvis_code_agent/code_agent_git.py +525 -0
- jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
- jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
- jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
- jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
- jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
- jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
- jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
- jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
- jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
- jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
- jarvis/jarvis_code_agent/lint.py +223 -524
- jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
- jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
- jarvis/jarvis_code_agent/rules/code_review.md +115 -0
- jarvis/jarvis_code_agent/rules/documentation.md +165 -0
- jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
- jarvis/jarvis_code_agent/rules/performance.md +158 -0
- jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
- jarvis/jarvis_code_agent/rules/security.md +160 -0
- jarvis/jarvis_code_agent/rules/tdd.md +78 -0
- jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
- jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
- jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
- jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
- jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
- jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
- jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
- jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
- jarvis/jarvis_code_agent/utils.py +36 -26
- jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
- jarvis/jarvis_code_analysis/code_review.py +64 -33
- jarvis/jarvis_data/config_schema.json +285 -192
- jarvis/jarvis_git_squash/main.py +8 -6
- jarvis/jarvis_git_utils/git_commiter.py +53 -76
- jarvis/jarvis_mcp/__init__.py +5 -2
- jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
- jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
- jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
- jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
- jarvis/jarvis_methodology/main.py +48 -39
- jarvis/jarvis_multi_agent/__init__.py +56 -23
- jarvis/jarvis_multi_agent/main.py +15 -18
- jarvis/jarvis_platform/base.py +179 -111
- jarvis/jarvis_platform/human.py +27 -16
- jarvis/jarvis_platform/kimi.py +52 -45
- jarvis/jarvis_platform/openai.py +101 -40
- jarvis/jarvis_platform/registry.py +51 -33
- jarvis/jarvis_platform/tongyi.py +68 -38
- jarvis/jarvis_platform/yuanbao.py +59 -43
- jarvis/jarvis_platform_manager/main.py +68 -76
- jarvis/jarvis_platform_manager/service.py +24 -14
- jarvis/jarvis_rag/README_CONFIG.md +314 -0
- jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
- jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
- jarvis/jarvis_rag/__init__.py +57 -4
- jarvis/jarvis_rag/cache.py +3 -1
- jarvis/jarvis_rag/cli.py +48 -68
- jarvis/jarvis_rag/embedding_interface.py +39 -0
- jarvis/jarvis_rag/embedding_manager.py +7 -230
- jarvis/jarvis_rag/embeddings/__init__.py +41 -0
- jarvis/jarvis_rag/embeddings/base.py +114 -0
- jarvis/jarvis_rag/embeddings/cohere.py +66 -0
- jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
- jarvis/jarvis_rag/embeddings/local.py +260 -0
- jarvis/jarvis_rag/embeddings/openai.py +62 -0
- jarvis/jarvis_rag/embeddings/registry.py +293 -0
- jarvis/jarvis_rag/llm_interface.py +8 -6
- jarvis/jarvis_rag/query_rewriter.py +8 -9
- jarvis/jarvis_rag/rag_pipeline.py +61 -52
- jarvis/jarvis_rag/reranker.py +7 -75
- jarvis/jarvis_rag/reranker_interface.py +32 -0
- jarvis/jarvis_rag/rerankers/__init__.py +41 -0
- jarvis/jarvis_rag/rerankers/base.py +109 -0
- jarvis/jarvis_rag/rerankers/cohere.py +67 -0
- jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
- jarvis/jarvis_rag/rerankers/jina.py +79 -0
- jarvis/jarvis_rag/rerankers/local.py +89 -0
- jarvis/jarvis_rag/rerankers/registry.py +293 -0
- jarvis/jarvis_rag/retriever.py +58 -43
- jarvis/jarvis_sec/__init__.py +66 -141
- jarvis/jarvis_sec/agents.py +21 -17
- jarvis/jarvis_sec/analysis.py +80 -33
- jarvis/jarvis_sec/checkers/__init__.py +7 -13
- jarvis/jarvis_sec/checkers/c_checker.py +356 -164
- jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
- jarvis/jarvis_sec/cli.py +43 -21
- jarvis/jarvis_sec/clustering.py +430 -272
- jarvis/jarvis_sec/file_manager.py +99 -55
- jarvis/jarvis_sec/parsers.py +9 -6
- jarvis/jarvis_sec/prompts.py +4 -3
- jarvis/jarvis_sec/report.py +44 -22
- jarvis/jarvis_sec/review.py +180 -107
- jarvis/jarvis_sec/status.py +50 -41
- jarvis/jarvis_sec/types.py +3 -0
- jarvis/jarvis_sec/utils.py +160 -83
- jarvis/jarvis_sec/verification.py +411 -181
- jarvis/jarvis_sec/workflow.py +132 -21
- jarvis/jarvis_smart_shell/main.py +28 -41
- jarvis/jarvis_stats/cli.py +14 -12
- jarvis/jarvis_stats/stats.py +28 -19
- jarvis/jarvis_stats/storage.py +14 -8
- jarvis/jarvis_stats/visualizer.py +12 -7
- jarvis/jarvis_tools/base.py +5 -2
- jarvis/jarvis_tools/clear_memory.py +13 -9
- jarvis/jarvis_tools/cli/main.py +23 -18
- jarvis/jarvis_tools/edit_file.py +572 -873
- jarvis/jarvis_tools/execute_script.py +10 -7
- jarvis/jarvis_tools/file_analyzer.py +7 -8
- jarvis/jarvis_tools/meta_agent.py +287 -0
- jarvis/jarvis_tools/methodology.py +5 -3
- jarvis/jarvis_tools/read_code.py +305 -1438
- jarvis/jarvis_tools/read_symbols.py +50 -17
- jarvis/jarvis_tools/read_webpage.py +19 -18
- jarvis/jarvis_tools/registry.py +435 -156
- jarvis/jarvis_tools/retrieve_memory.py +16 -11
- jarvis/jarvis_tools/save_memory.py +8 -6
- jarvis/jarvis_tools/search_web.py +31 -31
- jarvis/jarvis_tools/sub_agent.py +32 -28
- jarvis/jarvis_tools/sub_code_agent.py +44 -60
- jarvis/jarvis_tools/task_list_manager.py +1811 -0
- jarvis/jarvis_tools/virtual_tty.py +29 -19
- jarvis/jarvis_utils/__init__.py +4 -0
- jarvis/jarvis_utils/builtin_replace_map.py +2 -1
- jarvis/jarvis_utils/clipboard.py +9 -8
- jarvis/jarvis_utils/collections.py +331 -0
- jarvis/jarvis_utils/config.py +699 -194
- jarvis/jarvis_utils/dialogue_recorder.py +294 -0
- jarvis/jarvis_utils/embedding.py +6 -3
- jarvis/jarvis_utils/file_processors.py +7 -1
- jarvis/jarvis_utils/fzf.py +9 -3
- jarvis/jarvis_utils/git_utils.py +71 -42
- jarvis/jarvis_utils/globals.py +116 -32
- jarvis/jarvis_utils/http.py +6 -2
- jarvis/jarvis_utils/input.py +318 -83
- jarvis/jarvis_utils/jsonnet_compat.py +119 -104
- jarvis/jarvis_utils/methodology.py +37 -28
- jarvis/jarvis_utils/output.py +201 -44
- jarvis/jarvis_utils/utils.py +986 -628
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
- jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
- jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
- jarvis/jarvis_tools/generate_new_tool.py +0 -205
- jarvis/jarvis_tools/lsp_client.py +0 -1552
- jarvis/jarvis_tools/rewrite_file.py +0 -105
- jarvis_ai_assistant-0.7.8.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.8.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {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()
|
jarvis/jarvis_utils/embedding.py
CHANGED
|
@@ -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
|
-
|
|
34
|
-
return int(
|
|
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
|
-
|
|
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(
|
|
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:
|
jarvis/jarvis_utils/fzf.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
|
jarvis/jarvis_utils/git_utils.py
CHANGED
|
@@ -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
|
|
18
|
-
|
|
19
|
-
from
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
589
|
+
PrettyOutput.auto_print(
|
|
590
|
+
f"✅ Jarvis已更新到tag {remote_tag_result.stdout.strip()}"
|
|
591
|
+
)
|
|
566
592
|
|
|
567
593
|
# 执行pip安装更新代码
|
|
568
594
|
try:
|
|
569
|
-
|
|
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
|
-
|
|
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
|
-
|
|
671
|
+
PrettyOutput.auto_print("✅ 用户级代码安装成功")
|
|
645
672
|
return True
|
|
646
673
|
error_msg = user_result.stderr.strip()
|
|
647
674
|
|
|
648
|
-
|
|
675
|
+
PrettyOutput.auto_print(f"❌ 代码安装失败: {error_msg}")
|
|
649
676
|
return False
|
|
650
677
|
except Exception as e:
|
|
651
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
718
|
+
PrettyOutput.auto_print(f"❌ 获取差异文件列表失败: {str(e)}")
|
|
692
719
|
return []
|
|
693
720
|
except Exception as e:
|
|
694
|
-
|
|
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
|
-
|
|
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
|
-
|
|
922
|
+
PrettyOutput.auto_print(
|
|
923
|
+
"ℹ️ 已将未跟踪文件添加到 .gitignore,正在重新检测..."
|
|
924
|
+
)
|
|
896
925
|
except Exception as e:
|
|
897
|
-
|
|
926
|
+
PrettyOutput.auto_print(f"⚠️ 更新 .gitignore 失败: {str(e)}")
|
|
898
927
|
|
|
899
928
|
continue
|
|
900
929
|
|