jarvis-ai-assistant 0.7.16__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.16.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.16.dist-info/RECORD +0 -218
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""优化器工具函数。"""
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict
|
|
10
|
+
from typing import Iterable
|
|
11
|
+
from typing import List
|
|
12
|
+
from typing import Optional
|
|
13
|
+
from typing import Set
|
|
14
|
+
from typing import Tuple
|
|
15
|
+
|
|
16
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
17
|
+
from jarvis.jarvis_c2rust.optimizer_options import OptimizeOptions
|
|
18
|
+
from jarvis.jarvis_c2rust.optimizer_options import OptimizeStats
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def run_cmd(
|
|
22
|
+
cmd: List[str],
|
|
23
|
+
cwd: Path,
|
|
24
|
+
env: Optional[Dict[str, str]] = None,
|
|
25
|
+
timeout: Optional[int] = None,
|
|
26
|
+
) -> Tuple[int, str, str]:
|
|
27
|
+
"""执行命令并返回结果。"""
|
|
28
|
+
p = subprocess.Popen(
|
|
29
|
+
cmd,
|
|
30
|
+
cwd=str(cwd),
|
|
31
|
+
stdout=subprocess.PIPE,
|
|
32
|
+
stderr=subprocess.PIPE,
|
|
33
|
+
text=True,
|
|
34
|
+
env=dict(os.environ, **(env or {})),
|
|
35
|
+
)
|
|
36
|
+
try:
|
|
37
|
+
out, err = p.communicate(timeout=timeout if timeout and timeout > 0 else None)
|
|
38
|
+
return p.returncode, out, err
|
|
39
|
+
except subprocess.TimeoutExpired:
|
|
40
|
+
p.kill()
|
|
41
|
+
out, err = p.communicate()
|
|
42
|
+
err_msg = f"Command '{' '.join(cmd)}' timed out after {timeout} seconds."
|
|
43
|
+
if err:
|
|
44
|
+
err_msg += f"\n{err}"
|
|
45
|
+
return -1, out, err_msg
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def cargo_check(
|
|
49
|
+
crate_dir: Path,
|
|
50
|
+
stats: OptimizeStats,
|
|
51
|
+
max_checks: int,
|
|
52
|
+
timeout: Optional[int] = None,
|
|
53
|
+
) -> Tuple[bool, str]:
|
|
54
|
+
"""执行 cargo test 并返回结果摘要。"""
|
|
55
|
+
# 统一使用 cargo test 作为验证手段
|
|
56
|
+
if max_checks and stats.cargo_checks >= max_checks:
|
|
57
|
+
return False, "cargo test budget exhausted"
|
|
58
|
+
code, out, err = run_cmd(["cargo", "test", "-q"], crate_dir, timeout=timeout)
|
|
59
|
+
stats.cargo_checks += 1
|
|
60
|
+
ok = code == 0
|
|
61
|
+
diag = err.strip() or out.strip()
|
|
62
|
+
# 取首行作为摘要
|
|
63
|
+
first_line = next((ln for ln in diag.splitlines() if ln.strip()), "")
|
|
64
|
+
return ok, first_line
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_cargo_fmt(crate_dir: Path) -> None:
|
|
68
|
+
"""执行 cargo fmt 格式化代码。fmt 失败不影响主流程,只记录警告。"""
|
|
69
|
+
try:
|
|
70
|
+
res = subprocess.run(
|
|
71
|
+
["cargo", "fmt"],
|
|
72
|
+
capture_output=True,
|
|
73
|
+
text=True,
|
|
74
|
+
check=False,
|
|
75
|
+
cwd=str(crate_dir),
|
|
76
|
+
)
|
|
77
|
+
if res.returncode == 0:
|
|
78
|
+
PrettyOutput.auto_print("✅ [c2rust-optimizer][fmt] 代码格式化完成")
|
|
79
|
+
else:
|
|
80
|
+
# fmt 失败不影响主流程,只记录警告
|
|
81
|
+
PrettyOutput.auto_print(
|
|
82
|
+
f"⚠️ [c2rust-optimizer][fmt] 代码格式化失败(非致命): {res.stderr or res.stdout}"
|
|
83
|
+
)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
# fmt 失败不影响主流程,只记录警告
|
|
86
|
+
PrettyOutput.auto_print(
|
|
87
|
+
f"⚠️ [c2rust-optimizer][fmt] 代码格式化异常(非致命): {e}"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def check_clippy_warnings(crate_dir: Path) -> Tuple[bool, str]:
|
|
92
|
+
"""
|
|
93
|
+
检查是否有 clippy 告警。
|
|
94
|
+
使用 JSON 格式输出,便于精确解析和指定警告。
|
|
95
|
+
返回 (has_warnings, json_output),has_warnings 为 True 表示有告警,json_output 为 JSON 格式的输出。
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
res = subprocess.run(
|
|
99
|
+
["cargo", "clippy", "--message-format=json", "--", "-W", "clippy::all"],
|
|
100
|
+
capture_output=True,
|
|
101
|
+
text=True,
|
|
102
|
+
check=False,
|
|
103
|
+
cwd=str(crate_dir),
|
|
104
|
+
)
|
|
105
|
+
# clippy 的 JSON 输出通常在 stdout
|
|
106
|
+
stdout_output = (res.stdout or "").strip()
|
|
107
|
+
stderr_output = (res.stderr or "").strip()
|
|
108
|
+
|
|
109
|
+
# 解析 JSON 输出,提取警告信息
|
|
110
|
+
warnings = []
|
|
111
|
+
if stdout_output:
|
|
112
|
+
for line in stdout_output.splitlines():
|
|
113
|
+
line = line.strip()
|
|
114
|
+
if not line:
|
|
115
|
+
continue
|
|
116
|
+
try:
|
|
117
|
+
msg = json.loads(line)
|
|
118
|
+
# 只处理 warning 类型的消息
|
|
119
|
+
if (
|
|
120
|
+
msg.get("reason") == "compiler-message"
|
|
121
|
+
and msg.get("message", {}).get("level") == "warning"
|
|
122
|
+
):
|
|
123
|
+
warnings.append(msg)
|
|
124
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
125
|
+
# 忽略无法解析的行(可能是其他输出)
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
has_warnings = len(warnings) > 0
|
|
129
|
+
|
|
130
|
+
# 调试输出
|
|
131
|
+
if has_warnings:
|
|
132
|
+
PrettyOutput.auto_print(
|
|
133
|
+
f"⚠️ [c2rust-optimizer][clippy-check] 检测到 {len(warnings)} 个 Clippy 告警"
|
|
134
|
+
)
|
|
135
|
+
elif res.returncode != 0:
|
|
136
|
+
# 如果返回码非零但没有警告,可能是编译错误
|
|
137
|
+
PrettyOutput.auto_print(
|
|
138
|
+
f"📊 [c2rust-optimizer][clippy-check] Clippy 返回非零退出码({res.returncode}),但未检测到告警,可能是编译错误"
|
|
139
|
+
)
|
|
140
|
+
if stderr_output:
|
|
141
|
+
PrettyOutput.auto_print(
|
|
142
|
+
f"📊 [c2rust-optimizer][clippy-check] 错误输出预览(前200字符): {stderr_output[:200]}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# 返回 JSON 格式的输出(用于后续解析)
|
|
146
|
+
return has_warnings, stdout_output
|
|
147
|
+
except Exception as e:
|
|
148
|
+
# 检查失败时假设没有告警,避免阻塞流程
|
|
149
|
+
PrettyOutput.auto_print(
|
|
150
|
+
f"⚠️ [c2rust-optimizer][clippy-check] 检查 Clippy 告警异常(非致命): {e}"
|
|
151
|
+
)
|
|
152
|
+
return False, ""
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def check_missing_safety_doc_warnings(crate_dir: Path) -> Tuple[bool, str]:
|
|
156
|
+
"""
|
|
157
|
+
检查是否有 missing_safety_doc 告警。
|
|
158
|
+
使用 JSON 格式输出,便于精确解析和指定警告。
|
|
159
|
+
返回 (has_warnings, json_output),has_warnings 为 True 表示有告警,json_output 为 JSON 格式的输出。
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
res = subprocess.run(
|
|
163
|
+
[
|
|
164
|
+
"cargo",
|
|
165
|
+
"clippy",
|
|
166
|
+
"--message-format=json",
|
|
167
|
+
"--",
|
|
168
|
+
"-W",
|
|
169
|
+
"clippy::missing_safety_doc",
|
|
170
|
+
],
|
|
171
|
+
capture_output=True,
|
|
172
|
+
text=True,
|
|
173
|
+
check=False,
|
|
174
|
+
cwd=str(crate_dir),
|
|
175
|
+
)
|
|
176
|
+
# clippy 的 JSON 输出通常在 stdout
|
|
177
|
+
stdout_output = (res.stdout or "").strip()
|
|
178
|
+
stderr_output = (res.stderr or "").strip()
|
|
179
|
+
|
|
180
|
+
# 解析 JSON 输出,提取警告信息
|
|
181
|
+
warnings = []
|
|
182
|
+
if stdout_output:
|
|
183
|
+
for line in stdout_output.splitlines():
|
|
184
|
+
line = line.strip()
|
|
185
|
+
if not line:
|
|
186
|
+
continue
|
|
187
|
+
try:
|
|
188
|
+
msg = json.loads(line)
|
|
189
|
+
# 只处理 warning 类型的消息,且是 missing_safety_doc
|
|
190
|
+
if msg.get("reason") == "compiler-message":
|
|
191
|
+
message = msg.get("message", {})
|
|
192
|
+
if message.get("level") == "warning":
|
|
193
|
+
code = message.get("code", {})
|
|
194
|
+
code_str = code.get("code", "") if code else ""
|
|
195
|
+
if "missing_safety_doc" in code_str:
|
|
196
|
+
warnings.append(msg)
|
|
197
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
198
|
+
# 忽略无法解析的行(可能是其他输出)
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
has_warnings = len(warnings) > 0
|
|
202
|
+
|
|
203
|
+
# 调试输出
|
|
204
|
+
if has_warnings:
|
|
205
|
+
PrettyOutput.auto_print(
|
|
206
|
+
f"⚠️ [c2rust-optimizer][missing-safety-doc-check] 检测到 {len(warnings)} 个 missing_safety_doc 告警"
|
|
207
|
+
)
|
|
208
|
+
elif res.returncode != 0:
|
|
209
|
+
# 如果返回码非零但没有警告,可能是编译错误
|
|
210
|
+
PrettyOutput.auto_print(
|
|
211
|
+
f"📊 [c2rust-optimizer][missing-safety-doc-check] Clippy 返回非零退出码({res.returncode}),但未检测到告警,可能是编译错误"
|
|
212
|
+
)
|
|
213
|
+
if stderr_output:
|
|
214
|
+
PrettyOutput.auto_print(
|
|
215
|
+
f"📊 [c2rust-optimizer][missing-safety-doc-check] 错误输出预览(前200字符): {stderr_output[:200]}"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# 返回 JSON 格式的输出(用于后续解析)
|
|
219
|
+
return has_warnings, stdout_output
|
|
220
|
+
except Exception as e:
|
|
221
|
+
# 检查失败时假设没有告警,避免阻塞流程
|
|
222
|
+
PrettyOutput.auto_print(
|
|
223
|
+
f"⚠️ [c2rust-optimizer][missing-safety-doc-check] 检查 missing_safety_doc 告警异常(非致命): {e}"
|
|
224
|
+
)
|
|
225
|
+
return False, ""
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def cargo_check_full(
|
|
229
|
+
crate_dir: Path,
|
|
230
|
+
stats: OptimizeStats,
|
|
231
|
+
max_checks: int,
|
|
232
|
+
timeout: Optional[int] = None,
|
|
233
|
+
) -> Tuple[bool, str]:
|
|
234
|
+
"""
|
|
235
|
+
执行 cargo test,返回是否成功与完整输出(stdout+stderr)。
|
|
236
|
+
会计入 stats.cargo_checks,并受 max_checks 预算限制。
|
|
237
|
+
"""
|
|
238
|
+
if max_checks and stats.cargo_checks >= max_checks:
|
|
239
|
+
return False, "cargo test budget exhausted"
|
|
240
|
+
try:
|
|
241
|
+
res = subprocess.run(
|
|
242
|
+
["cargo", "test", "-q"],
|
|
243
|
+
capture_output=True,
|
|
244
|
+
text=True,
|
|
245
|
+
check=False,
|
|
246
|
+
cwd=str(crate_dir),
|
|
247
|
+
timeout=timeout if timeout and timeout > 0 else None,
|
|
248
|
+
)
|
|
249
|
+
stats.cargo_checks += 1
|
|
250
|
+
ok = res.returncode == 0
|
|
251
|
+
out = res.stdout or ""
|
|
252
|
+
err = res.stderr or ""
|
|
253
|
+
msg = (out + ("\n" + err if err else "")).strip()
|
|
254
|
+
return ok, msg
|
|
255
|
+
except subprocess.TimeoutExpired as e:
|
|
256
|
+
stats.cargo_checks += 1
|
|
257
|
+
out_s = e.stdout.decode("utf-8", errors="ignore") if e.stdout else ""
|
|
258
|
+
err_s = e.stderr.decode("utf-8", errors="ignore") if e.stderr else ""
|
|
259
|
+
msg = f"cargo test timed out after {timeout} seconds"
|
|
260
|
+
full_output = (out_s + ("\n" + err_s if err_s else "")).strip()
|
|
261
|
+
if full_output:
|
|
262
|
+
msg += f"\nOutput:\n{full_output}"
|
|
263
|
+
return False, msg
|
|
264
|
+
except Exception as e:
|
|
265
|
+
stats.cargo_checks += 1
|
|
266
|
+
return False, f"cargo test exception: {e}"
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def git_toplevel(start: Path) -> Optional[Path]:
|
|
270
|
+
"""返回包含 start 的 Git 仓库根目录(--show-toplevel)。若不在仓库中则返回 None。"""
|
|
271
|
+
try:
|
|
272
|
+
code, out, err = run_cmd(["git", "rev-parse", "--show-toplevel"], start)
|
|
273
|
+
if code == 0:
|
|
274
|
+
p = (out or "").strip()
|
|
275
|
+
if p:
|
|
276
|
+
return Path(p)
|
|
277
|
+
return None
|
|
278
|
+
except Exception:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def git_head_commit(root: Path) -> Optional[str]:
|
|
283
|
+
"""获取 Git HEAD commit hash。"""
|
|
284
|
+
try:
|
|
285
|
+
code, out, err = run_cmd(["git", "rev-parse", "--verify", "HEAD"], root)
|
|
286
|
+
if code == 0:
|
|
287
|
+
return out.strip()
|
|
288
|
+
return None
|
|
289
|
+
except Exception:
|
|
290
|
+
return None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def git_reset_hard(root: Path, commit: str) -> bool:
|
|
294
|
+
"""执行 git reset --hard 到指定 commit。"""
|
|
295
|
+
try:
|
|
296
|
+
code, _, _ = run_cmd(["git", "reset", "--hard", commit], root)
|
|
297
|
+
if code != 0:
|
|
298
|
+
return False
|
|
299
|
+
return True
|
|
300
|
+
except Exception:
|
|
301
|
+
return False
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def iter_rust_files(crate_dir: Path) -> Iterable[Path]:
|
|
305
|
+
"""遍历 crate 目录下的所有 Rust 文件。"""
|
|
306
|
+
src = crate_dir / "src"
|
|
307
|
+
if not src.exists():
|
|
308
|
+
# 仍尝试遍历整个 crate 目录,但优先 src
|
|
309
|
+
yield from crate_dir.rglob("*.rs")
|
|
310
|
+
return
|
|
311
|
+
# 遍历 src 优先
|
|
312
|
+
yield from src.rglob("*.rs")
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def read_file(path: Path) -> str:
|
|
316
|
+
"""读取文件内容。"""
|
|
317
|
+
return path.read_text(encoding="utf-8")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def write_file(path: Path, content: str) -> None:
|
|
321
|
+
"""写入文件内容。"""
|
|
322
|
+
path.write_text(content, encoding="utf-8")
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def backup_file(path: Path) -> Path:
|
|
326
|
+
"""备份文件。"""
|
|
327
|
+
bak = path.with_suffix(path.suffix + ".bak_opt")
|
|
328
|
+
shutil.copy2(path, bak)
|
|
329
|
+
return bak
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def restore_file_from_backup(path: Path, backup: Path) -> None:
|
|
333
|
+
"""从备份恢复文件。"""
|
|
334
|
+
shutil.move(str(backup), str(path))
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def remove_backup(backup: Path) -> None:
|
|
338
|
+
"""删除备份文件。"""
|
|
339
|
+
if backup.exists():
|
|
340
|
+
backup.unlink(missing_ok=True)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def ensure_report_dir(crate_dir: Path) -> Path:
|
|
344
|
+
"""确保报告目录存在。"""
|
|
345
|
+
report_dir = crate_dir / ".jarvis" / "c2rust"
|
|
346
|
+
report_dir.mkdir(parents=True, exist_ok=True)
|
|
347
|
+
return report_dir
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def find_project_root() -> Optional[Path]:
|
|
351
|
+
"""
|
|
352
|
+
查找项目根目录(包含 .jarvis/c2rust 的目录)。
|
|
353
|
+
从当前目录向上查找,最多向上查找 5 层。
|
|
354
|
+
"""
|
|
355
|
+
from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
|
|
356
|
+
|
|
357
|
+
cwd = Path(".").resolve()
|
|
358
|
+
current = cwd
|
|
359
|
+
for _ in range(5):
|
|
360
|
+
if current and current.exists():
|
|
361
|
+
jarvis_dir = current / C2RUST_DIRNAME
|
|
362
|
+
if jarvis_dir.exists() and jarvis_dir.is_dir():
|
|
363
|
+
return current
|
|
364
|
+
parent = current.parent
|
|
365
|
+
if parent == current: # 已到达根目录
|
|
366
|
+
break
|
|
367
|
+
current = parent
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def parse_patterns(s: Optional[str]) -> List[str]:
|
|
372
|
+
"""解析逗号分隔的模式字符串。"""
|
|
373
|
+
if not s or not isinstance(s, str):
|
|
374
|
+
return []
|
|
375
|
+
parts = [x.strip() for x in s.replace("\n", ",").split(",")]
|
|
376
|
+
return [x for x in parts if x]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def match_any_pattern(rel: str, patterns: List[str]) -> bool:
|
|
380
|
+
"""检查相对路径是否匹配任一模式。"""
|
|
381
|
+
if not patterns:
|
|
382
|
+
return False
|
|
383
|
+
import fnmatch
|
|
384
|
+
|
|
385
|
+
return any(fnmatch.fnmatch(rel, pat) for pat in patterns)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def compute_target_files(
|
|
389
|
+
crate_dir: Path,
|
|
390
|
+
options: OptimizeOptions,
|
|
391
|
+
processed: Set[str],
|
|
392
|
+
) -> List[Path]:
|
|
393
|
+
"""
|
|
394
|
+
计算目标文件列表(按 include/exclude/resume/max_files 过滤)。
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
crate_dir: crate 根目录
|
|
398
|
+
options: 优化选项
|
|
399
|
+
processed: 已处理的文件集合
|
|
400
|
+
|
|
401
|
+
Returns:
|
|
402
|
+
目标文件列表
|
|
403
|
+
"""
|
|
404
|
+
include = parse_patterns(options.include_patterns)
|
|
405
|
+
exclude = parse_patterns(options.exclude_patterns)
|
|
406
|
+
maxn = int(options.max_files or 0)
|
|
407
|
+
take: List[Path] = []
|
|
408
|
+
for p in sorted(iter_rust_files(crate_dir), key=lambda x: x.as_posix()):
|
|
409
|
+
try:
|
|
410
|
+
rel = p.resolve().relative_to(crate_dir.resolve()).as_posix()
|
|
411
|
+
except Exception:
|
|
412
|
+
rel = p.as_posix()
|
|
413
|
+
# include 过滤(若提供,则必须命中其一)
|
|
414
|
+
if include and not match_any_pattern(rel, include):
|
|
415
|
+
continue
|
|
416
|
+
# exclude 过滤
|
|
417
|
+
if exclude and match_any_pattern(rel, exclude):
|
|
418
|
+
continue
|
|
419
|
+
# resume:跳过已处理文件
|
|
420
|
+
if options.resume and rel in processed:
|
|
421
|
+
continue
|
|
422
|
+
take.append(p)
|
|
423
|
+
if maxn > 0 and len(take) >= maxn:
|
|
424
|
+
break
|
|
425
|
+
return take
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def detect_crate_dir(preferred: Optional[Path]) -> Path:
|
|
429
|
+
"""
|
|
430
|
+
选择 crate 目录策略:
|
|
431
|
+
- 若提供 preferred 且包含 Cargo.toml,则使用
|
|
432
|
+
- 否则:尝试从项目根目录推断(查找包含 .jarvis/c2rust 的目录)
|
|
433
|
+
- 否则:优先 <cwd>/<cwd.name>_rs;若存在 Cargo.toml 则用之
|
|
434
|
+
- 否则:在当前目录下递归寻找第一个包含 Cargo.toml 的目录
|
|
435
|
+
- 若失败:若当前目录有 Cargo.toml 则返回当前目录,否则抛错
|
|
436
|
+
"""
|
|
437
|
+
if preferred:
|
|
438
|
+
preferred = preferred.resolve()
|
|
439
|
+
if (preferred / "Cargo.toml").exists():
|
|
440
|
+
return preferred
|
|
441
|
+
|
|
442
|
+
# 尝试从项目根目录推断 crate 目录
|
|
443
|
+
project_root = find_project_root()
|
|
444
|
+
if project_root:
|
|
445
|
+
# 策略1: project_root 的父目录下的 <project_root.name>_rs
|
|
446
|
+
candidate1 = project_root.parent / f"{project_root.name}_rs"
|
|
447
|
+
if (candidate1 / "Cargo.toml").exists():
|
|
448
|
+
return candidate1
|
|
449
|
+
# 策略2: project_root 本身(如果包含 Cargo.toml)
|
|
450
|
+
if (project_root / "Cargo.toml").exists():
|
|
451
|
+
return project_root
|
|
452
|
+
# 策略3: project_root 下的子目录中包含 Cargo.toml 的
|
|
453
|
+
for d in project_root.iterdir():
|
|
454
|
+
if d.is_dir() and (d / "Cargo.toml").exists():
|
|
455
|
+
return d
|
|
456
|
+
|
|
457
|
+
cwd = Path(".").resolve()
|
|
458
|
+
candidate = cwd / f"{cwd.name}_rs"
|
|
459
|
+
if (candidate / "Cargo.toml").exists():
|
|
460
|
+
return candidate
|
|
461
|
+
|
|
462
|
+
# 搜索第一个包含 Cargo.toml 的目录(限制深度2以避免过慢)
|
|
463
|
+
for p in [cwd] + [d for d in cwd.iterdir() if d.is_dir()]:
|
|
464
|
+
if (p / "Cargo.toml").exists():
|
|
465
|
+
return p
|
|
466
|
+
|
|
467
|
+
if (cwd / "Cargo.toml").exists():
|
|
468
|
+
return cwd
|
|
469
|
+
raise FileNotFoundError("未找到 Cargo.toml,对应 crate 目录无法确定。")
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""可见性优化模块。"""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Callable
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
from jarvis.jarvis_utils.output import PrettyOutput
|
|
10
|
+
|
|
11
|
+
from jarvis.jarvis_agent.events import AFTER_TOOL_CALL
|
|
12
|
+
from jarvis.jarvis_agent.events import BEFORE_TOOL_CALL
|
|
13
|
+
from jarvis.jarvis_c2rust.optimizer_options import OptimizeOptions
|
|
14
|
+
from jarvis.jarvis_c2rust.optimizer_options import OptimizeStats
|
|
15
|
+
from jarvis.jarvis_c2rust.optimizer_progress import ProgressManager
|
|
16
|
+
from jarvis.jarvis_c2rust.optimizer_utils import cargo_check_full
|
|
17
|
+
from jarvis.jarvis_c2rust.optimizer_utils import run_cargo_fmt
|
|
18
|
+
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class VisibilityOptimizer:
|
|
22
|
+
"""可见性优化器。"""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
crate_dir: Path,
|
|
27
|
+
options: OptimizeOptions,
|
|
28
|
+
stats: OptimizeStats,
|
|
29
|
+
progress_manager: ProgressManager,
|
|
30
|
+
append_additional_notes_func: Callable[[str], str],
|
|
31
|
+
):
|
|
32
|
+
self.crate_dir = crate_dir
|
|
33
|
+
self.options = options
|
|
34
|
+
self.stats = stats
|
|
35
|
+
self.progress_manager = progress_manager
|
|
36
|
+
self.append_additional_notes = append_additional_notes_func
|
|
37
|
+
|
|
38
|
+
def codeagent_opt_visibility(self, target_files: List[Path]) -> None:
|
|
39
|
+
"""
|
|
40
|
+
使用 CodeAgent 进行可见性优化。
|
|
41
|
+
|
|
42
|
+
注意:CodeAgent 必须在 crate 目录下创建和执行,以确保所有文件操作和命令执行都在正确的上下文中进行。
|
|
43
|
+
"""
|
|
44
|
+
crate = self.crate_dir.resolve()
|
|
45
|
+
file_list: List[str] = []
|
|
46
|
+
for p in target_files:
|
|
47
|
+
try:
|
|
48
|
+
rel = p.resolve().relative_to(crate).as_posix()
|
|
49
|
+
except Exception:
|
|
50
|
+
rel = p.as_posix()
|
|
51
|
+
file_list.append(rel)
|
|
52
|
+
self.stats.files_scanned += 1
|
|
53
|
+
|
|
54
|
+
prompt_lines: List[str] = [
|
|
55
|
+
"你是资深 Rust 代码工程师。请在当前 crate 下执行可见性优化,并以补丁形式输出修改:",
|
|
56
|
+
f"- crate 根目录:{crate}",
|
|
57
|
+
"",
|
|
58
|
+
"本次优化仅允许修改以下文件范围(严格限制):",
|
|
59
|
+
*[f"- {rel}" for rel in file_list],
|
|
60
|
+
"",
|
|
61
|
+
"优化目标:",
|
|
62
|
+
"1) 可见性最小化:",
|
|
63
|
+
" - 优先将 `pub fn` 降为 `pub(crate) fn`(如果函数仅在 crate 内部使用);",
|
|
64
|
+
" - 保持对外接口(跨 crate 使用的接口,如 lib.rs 中的顶层导出)为 `pub`;",
|
|
65
|
+
" - 在 lib.rs 中的顶层导出保持现状,不要修改。",
|
|
66
|
+
"",
|
|
67
|
+
"2) 修复已有实现的问题:",
|
|
68
|
+
" - 如果在进行可见性优化的过程中,发现代码已有的实现有问题(如逻辑错误、潜在 bug、性能问题、内存安全问题等),也需要一并修复;",
|
|
69
|
+
" - 这些问题可能包括但不限于:不正确的可见性设计、未检查的边界条件、资源泄漏、竞态条件等;",
|
|
70
|
+
" - 修复时应该保持最小改动原则,优先修复最严重的问题。",
|
|
71
|
+
"",
|
|
72
|
+
"约束与范围:",
|
|
73
|
+
"- 仅修改上述列出的文件;除非必须(如修复引用路径),否则不要修改其他文件。",
|
|
74
|
+
"- 保持最小改动,不要进行与可见性优化无关的重构或格式化。",
|
|
75
|
+
"- 修改后需保证 `cargo test` 可以通过;如需引入少量配套改动,请一并包含在补丁中以确保通过。",
|
|
76
|
+
"- 输出仅为补丁,不要输出解释或多余文本。",
|
|
77
|
+
"",
|
|
78
|
+
"优先级说明:",
|
|
79
|
+
"- **如果优化过程中出现了测试不通过或编译错误,必须优先解决这些问题**;",
|
|
80
|
+
"- 在进行可见性优化之前,先确保代码能够正常编译和通过测试;",
|
|
81
|
+
"- 如果可见性优化导致了编译错误或测试失败,必须立即修复这些错误,然后再继续优化。",
|
|
82
|
+
"",
|
|
83
|
+
"自检要求:在每次输出补丁后,请使用 execute_script 工具在 crate 根目录执行 `cargo test -q` 进行验证;",
|
|
84
|
+
"若出现编译错误或测试失败,请优先修复这些问题,然后再继续可见性优化;",
|
|
85
|
+
"若未通过,请继续输出新的补丁进行最小修复并再次自检,直至 `cargo test` 通过为止。",
|
|
86
|
+
]
|
|
87
|
+
prompt = "\n".join(prompt_lines)
|
|
88
|
+
prompt = self.append_additional_notes(prompt)
|
|
89
|
+
# 切换到 crate 目录,确保 CodeAgent 在正确的上下文中创建和执行
|
|
90
|
+
prev_cwd = os.getcwd()
|
|
91
|
+
PrettyOutput.auto_print(
|
|
92
|
+
"[c2rust-optimizer][codeagent][visibility] 正在调用 CodeAgent 进行可见性优化..."
|
|
93
|
+
)
|
|
94
|
+
try:
|
|
95
|
+
os.chdir(str(crate))
|
|
96
|
+
# 修复前执行 cargo fmt
|
|
97
|
+
run_cargo_fmt(crate)
|
|
98
|
+
|
|
99
|
+
# 记录运行前的 commit id
|
|
100
|
+
commit_before = self.progress_manager.get_crate_commit_hash()
|
|
101
|
+
|
|
102
|
+
# CodeAgent 在 crate 目录下创建和执行
|
|
103
|
+
agent = CodeAgent(
|
|
104
|
+
name="VisibilityOptimizer",
|
|
105
|
+
need_summary=False,
|
|
106
|
+
non_interactive=self.options.non_interactive,
|
|
107
|
+
model_group=self.options.llm_group,
|
|
108
|
+
enable_task_list_manager=False,
|
|
109
|
+
disable_review=True,
|
|
110
|
+
)
|
|
111
|
+
# 订阅 BEFORE_TOOL_CALL 和 AFTER_TOOL_CALL 事件,用于细粒度检测测试代码删除
|
|
112
|
+
agent.event_bus.subscribe(
|
|
113
|
+
BEFORE_TOOL_CALL, self.progress_manager.on_before_tool_call
|
|
114
|
+
)
|
|
115
|
+
agent.event_bus.subscribe(
|
|
116
|
+
AFTER_TOOL_CALL, self.progress_manager.on_after_tool_call
|
|
117
|
+
)
|
|
118
|
+
# 记录 Agent 创建时的 commit id(作为初始值)
|
|
119
|
+
agent_id = id(agent)
|
|
120
|
+
agent_key = f"agent_{agent_id}"
|
|
121
|
+
initial_commit = self.progress_manager.get_crate_commit_hash()
|
|
122
|
+
if initial_commit:
|
|
123
|
+
self.progress_manager._agent_before_commits[agent_key] = initial_commit
|
|
124
|
+
agent.run(
|
|
125
|
+
prompt, prefix="[c2rust-optimizer][codeagent][visibility]", suffix=""
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# 检测并处理测试代码删除
|
|
129
|
+
if self.progress_manager.check_and_handle_test_deletion(
|
|
130
|
+
commit_before, agent
|
|
131
|
+
):
|
|
132
|
+
# 如果回退了,需要重新运行 agent
|
|
133
|
+
PrettyOutput.auto_print(
|
|
134
|
+
"[c2rust-optimizer][codeagent][visibility] 检测到测试代码删除问题,已回退,重新运行 agent"
|
|
135
|
+
)
|
|
136
|
+
commit_before = self.progress_manager.get_crate_commit_hash()
|
|
137
|
+
agent.run(
|
|
138
|
+
prompt,
|
|
139
|
+
prefix="[c2rust-optimizer][codeagent][visibility][retry]",
|
|
140
|
+
suffix="",
|
|
141
|
+
)
|
|
142
|
+
# 再次检测
|
|
143
|
+
if self.progress_manager.check_and_handle_test_deletion(
|
|
144
|
+
commit_before, agent
|
|
145
|
+
):
|
|
146
|
+
PrettyOutput.auto_print(
|
|
147
|
+
"[c2rust-optimizer][codeagent][visibility] 再次检测到测试代码删除问题,已回退"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# 验证修复是否成功(通过 cargo test)
|
|
151
|
+
ok, _ = cargo_check_full(
|
|
152
|
+
crate,
|
|
153
|
+
self.stats,
|
|
154
|
+
self.options.max_checks,
|
|
155
|
+
timeout=self.options.cargo_test_timeout,
|
|
156
|
+
)
|
|
157
|
+
if ok:
|
|
158
|
+
# 修复成功,保存进度和 commit id
|
|
159
|
+
file_paths = [crate / f for f in file_list if (crate / f).exists()]
|
|
160
|
+
self.progress_manager.save_fix_progress(
|
|
161
|
+
"visibility_opt", "batch", file_paths if file_paths else None
|
|
162
|
+
)
|
|
163
|
+
PrettyOutput.auto_print(
|
|
164
|
+
"[c2rust-optimizer][codeagent][visibility] 可见性优化成功,已保存进度"
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
# 测试失败,回退到运行前的 commit
|
|
168
|
+
if commit_before:
|
|
169
|
+
PrettyOutput.auto_print(
|
|
170
|
+
f"[c2rust-optimizer][codeagent][visibility] 可见性优化后测试失败,回退到运行前的 commit: {commit_before[:8]}"
|
|
171
|
+
)
|
|
172
|
+
if self.progress_manager.reset_to_commit(commit_before):
|
|
173
|
+
PrettyOutput.auto_print(
|
|
174
|
+
f"[c2rust-optimizer][codeagent][visibility] 已成功回退到 commit: {commit_before[:8]}"
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
PrettyOutput.auto_print(
|
|
178
|
+
"[c2rust-optimizer][codeagent][visibility] 回退失败,请手动检查代码状态"
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
PrettyOutput.auto_print(
|
|
182
|
+
"[c2rust-optimizer][codeagent][visibility] 可见性优化后测试失败,但无法获取运行前的 commit"
|
|
183
|
+
)
|
|
184
|
+
finally:
|
|
185
|
+
os.chdir(prev_cwd)
|