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