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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +458 -152
  3. jarvis/jarvis_agent/agent_manager.py +17 -13
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +329 -0
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +628 -55
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +34 -10
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +105 -9
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +20 -22
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  31. jarvis/jarvis_agent/task_analyzer.py +31 -6
  32. jarvis/jarvis_agent/task_manager.py +11 -27
  33. jarvis/jarvis_agent/tool_executor.py +2 -3
  34. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  35. jarvis/jarvis_agent/utils.py +5 -1
  36. jarvis/jarvis_agent/web_bridge.py +189 -0
  37. jarvis/jarvis_agent/web_output_sink.py +53 -0
  38. jarvis/jarvis_agent/web_server.py +786 -0
  39. jarvis/jarvis_c2rust/__init__.py +26 -0
  40. jarvis/jarvis_c2rust/cli.py +575 -0
  41. jarvis/jarvis_c2rust/collector.py +250 -0
  42. jarvis/jarvis_c2rust/constants.py +26 -0
  43. jarvis/jarvis_c2rust/library_replacer.py +1254 -0
  44. jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
  45. jarvis/jarvis_c2rust/loaders.py +207 -0
  46. jarvis/jarvis_c2rust/models.py +28 -0
  47. jarvis/jarvis_c2rust/optimizer.py +2157 -0
  48. jarvis/jarvis_c2rust/scanner.py +1681 -0
  49. jarvis/jarvis_c2rust/transpiler.py +2983 -0
  50. jarvis/jarvis_c2rust/utils.py +385 -0
  51. jarvis/jarvis_code_agent/build_validation_config.py +132 -0
  52. jarvis/jarvis_code_agent/code_agent.py +1371 -220
  53. jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
  54. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
  60. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
  61. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
  62. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
  63. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
  64. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
  65. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
  66. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
  67. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
  68. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  69. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
  70. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  71. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  72. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  73. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  74. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  75. jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
  76. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
  77. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
  78. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
  79. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  80. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  81. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
  82. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
  83. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  84. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
  85. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  86. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
  87. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
  88. jarvis/jarvis_code_agent/lint.py +501 -8
  89. jarvis/jarvis_code_agent/utils.py +141 -0
  90. jarvis/jarvis_code_analysis/code_review.py +493 -584
  91. jarvis/jarvis_data/config_schema.json +128 -12
  92. jarvis/jarvis_git_squash/main.py +4 -5
  93. jarvis/jarvis_git_utils/git_commiter.py +82 -75
  94. jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
  95. jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
  96. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  97. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  98. jarvis/jarvis_methodology/main.py +32 -48
  99. jarvis/jarvis_multi_agent/__init__.py +287 -55
  100. jarvis/jarvis_multi_agent/main.py +36 -4
  101. jarvis/jarvis_platform/base.py +524 -202
  102. jarvis/jarvis_platform/human.py +7 -8
  103. jarvis/jarvis_platform/kimi.py +30 -36
  104. jarvis/jarvis_platform/openai.py +88 -25
  105. jarvis/jarvis_platform/registry.py +26 -10
  106. jarvis/jarvis_platform/tongyi.py +24 -25
  107. jarvis/jarvis_platform/yuanbao.py +32 -43
  108. jarvis/jarvis_platform_manager/main.py +66 -77
  109. jarvis/jarvis_platform_manager/service.py +8 -13
  110. jarvis/jarvis_rag/cli.py +53 -55
  111. jarvis/jarvis_rag/embedding_manager.py +13 -18
  112. jarvis/jarvis_rag/llm_interface.py +8 -9
  113. jarvis/jarvis_rag/query_rewriter.py +10 -21
  114. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  115. jarvis/jarvis_rag/reranker.py +4 -5
  116. jarvis/jarvis_rag/retriever.py +28 -30
  117. jarvis/jarvis_sec/__init__.py +305 -0
  118. jarvis/jarvis_sec/agents.py +143 -0
  119. jarvis/jarvis_sec/analysis.py +276 -0
  120. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  121. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  122. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  123. jarvis/jarvis_sec/cli.py +139 -0
  124. jarvis/jarvis_sec/clustering.py +1439 -0
  125. jarvis/jarvis_sec/file_manager.py +427 -0
  126. jarvis/jarvis_sec/parsers.py +73 -0
  127. jarvis/jarvis_sec/prompts.py +268 -0
  128. jarvis/jarvis_sec/report.py +336 -0
  129. jarvis/jarvis_sec/review.py +453 -0
  130. jarvis/jarvis_sec/status.py +264 -0
  131. jarvis/jarvis_sec/types.py +20 -0
  132. jarvis/jarvis_sec/utils.py +499 -0
  133. jarvis/jarvis_sec/verification.py +848 -0
  134. jarvis/jarvis_sec/workflow.py +226 -0
  135. jarvis/jarvis_smart_shell/main.py +38 -87
  136. jarvis/jarvis_stats/cli.py +2 -2
  137. jarvis/jarvis_stats/stats.py +8 -8
  138. jarvis/jarvis_stats/storage.py +15 -21
  139. jarvis/jarvis_stats/visualizer.py +1 -1
  140. jarvis/jarvis_tools/clear_memory.py +3 -20
  141. jarvis/jarvis_tools/cli/main.py +21 -23
  142. jarvis/jarvis_tools/edit_file.py +1019 -132
  143. jarvis/jarvis_tools/execute_script.py +83 -25
  144. jarvis/jarvis_tools/file_analyzer.py +6 -9
  145. jarvis/jarvis_tools/generate_new_tool.py +14 -21
  146. jarvis/jarvis_tools/lsp_client.py +1552 -0
  147. jarvis/jarvis_tools/methodology.py +2 -3
  148. jarvis/jarvis_tools/read_code.py +1736 -35
  149. jarvis/jarvis_tools/read_symbols.py +140 -0
  150. jarvis/jarvis_tools/read_webpage.py +12 -13
  151. jarvis/jarvis_tools/registry.py +427 -200
  152. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  153. jarvis/jarvis_tools/rewrite_file.py +72 -158
  154. jarvis/jarvis_tools/save_memory.py +3 -15
  155. jarvis/jarvis_tools/search_web.py +18 -18
  156. jarvis/jarvis_tools/sub_agent.py +36 -43
  157. jarvis/jarvis_tools/sub_code_agent.py +25 -26
  158. jarvis/jarvis_tools/virtual_tty.py +55 -33
  159. jarvis/jarvis_utils/clipboard.py +7 -10
  160. jarvis/jarvis_utils/config.py +232 -45
  161. jarvis/jarvis_utils/embedding.py +8 -5
  162. jarvis/jarvis_utils/fzf.py +8 -8
  163. jarvis/jarvis_utils/git_utils.py +225 -36
  164. jarvis/jarvis_utils/globals.py +3 -3
  165. jarvis/jarvis_utils/http.py +1 -1
  166. jarvis/jarvis_utils/input.py +99 -48
  167. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  168. jarvis/jarvis_utils/methodology.py +52 -48
  169. jarvis/jarvis_utils/utils.py +819 -491
  170. jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
  171. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  172. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
  173. jarvis/jarvis_agent/config.py +0 -92
  174. jarvis/jarvis_agent/edit_file_handler.py +0 -296
  175. jarvis/jarvis_platform/ai8.py +0 -332
  176. jarvis/jarvis_tools/ask_user.py +0 -54
  177. jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
  178. jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
  179. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  180. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  181. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Jarvis C2Rust 工具集。
4
+
5
+ 核心数据:
6
+ - 统一符号表(JSONL):<project_root>/.jarvis/c2rust/symbols.jsonl(后续流程的主输入)
7
+ - 原始符号表(JSONL):<project_root>/.jarvis/c2rust/symbols_raw.jsonl
8
+ - 其他产物:translation_order.jsonl、library_replacements.jsonl、progress.json、config.json、symbol_map.jsonl 等
9
+
10
+ 推荐用法(CLI):
11
+ - 配置管理: jarvis-c2rust config --files <hdrs...> [--root-list-syms ...] [--disabled-libs ...]
12
+ - 扫描: jarvis-c2rust scan
13
+ - 库替代评估: jarvis-c2rust lib-replace [-g <llm-group>]
14
+ - 规划/落盘: jarvis-c2rust prepare [-g <llm-group>]
15
+ - 转译: jarvis-c2rust transpile [-g <llm-group>] [-m <max-retries>](断点续跑默认始终启用)
16
+ - 代码优化: jarvis-c2rust optimize [--crate-dir ...] [--unsafe/--no-unsafe] [--structure/--no-structure] [--visibility/--no-visibility] [--doc/--no-doc] [-m N] [--dry-run]
17
+ - 一键流水线: jarvis-c2rust run [-g <llm-group>] [-m <max-retries>]
18
+
19
+ 或(模块方式):
20
+ python -m jarvis.jarvis_c2rust.cli <subcommand>
21
+
22
+ 说明:
23
+ - 所有路径均推荐使用 <project_root>/.jarvis/c2rust 下的标准文件名,便于断点续跑与复用。
24
+ """
25
+
26
+ __all__ = ["scanner", "optimizer"]
@@ -0,0 +1,575 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ C2Rust 独立命令行入口。
4
+
5
+ 提供分组式 CLI:
6
+ - jarvis-c2rust run: 执行完整的转译流水线(scan -> lib-replace -> prepare -> transpile -> optimize),支持断点续跑
7
+ - jarvis-c2rust config: 管理转译配置文件(根符号列表、禁用库列表、附加说明等)
8
+
9
+ 实现策略:
10
+ - 使用 Typer 分组式结构,便于后续扩展更多子命令(如 analyze/export 等)。
11
+ - run 命令支持断点续跑,根据状态文件自动跳过已完成的阶段。
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ from pathlib import Path
18
+ from typing import Optional, List
19
+
20
+ import typer
21
+ from jarvis.jarvis_c2rust.scanner import run_scan as _run_scan
22
+ from jarvis.jarvis_c2rust.library_replacer import (
23
+ apply_library_replacement as _apply_library_replacement,
24
+ )
25
+ from jarvis.jarvis_utils.utils import init_env
26
+ from jarvis.jarvis_c2rust.llm_module_agent import (
27
+ execute_llm_plan as _execute_llm_plan,
28
+ )
29
+
30
+
31
+ def _check_optimize_completed(crate_dir: Path) -> bool:
32
+ """
33
+ 检查优化是否真正完成。
34
+ 需要检查 optimize_progress.json 中所有必要的步骤是否都完成了。
35
+ 特别是 clippy_elimination:如果有告警,必须完成;如果没有告警,可以跳过。
36
+ """
37
+ import json
38
+ from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
39
+
40
+ progress_path = crate_dir / C2RUST_DIRNAME / "optimize_progress.json"
41
+ if not progress_path.exists():
42
+ # 如果没有进度文件,说明还没开始,不算完成
43
+ return False
44
+
45
+ try:
46
+ with progress_path.open("r", encoding="utf-8") as f:
47
+ progress = json.load(f)
48
+
49
+ steps_completed = set(progress.get("steps_completed", []))
50
+
51
+ # 检查是否有 clippy 告警
52
+ # 直接调用 optimizer 模块中的函数(虽然是私有函数,但我们需要它来检查)
53
+ import subprocess
54
+ try:
55
+ res = subprocess.run(
56
+ ["cargo", "clippy", "--", "-W", "clippy::all"],
57
+ capture_output=True,
58
+ text=True,
59
+ check=False,
60
+ cwd=str(crate_dir),
61
+ )
62
+ stderr_output = (res.stderr or "").strip()
63
+ stdout_output = (res.stdout or "").strip()
64
+ output = (stderr_output + "\n" + stdout_output).strip() if (stderr_output and stdout_output) else (stderr_output or stdout_output or "").strip()
65
+ output_lower = output.lower()
66
+ has_warnings = "warning:" in output_lower or "warn(" in output_lower or ("clippy::" in output_lower and res.returncode != 0)
67
+ except Exception:
68
+ # 如果检查失败,保守地认为有告警(需要完成)
69
+ has_warnings = True
70
+
71
+ # 如果有告警,clippy_elimination 必须在 steps_completed 中
72
+ if has_warnings:
73
+ if "clippy_elimination" not in steps_completed:
74
+ return False
75
+
76
+ # 检查其他必要的步骤(根据 enable_* 选项,但这里我们假设都启用了)
77
+ # 注意:这里我们只检查 clippy_elimination,其他步骤(unsafe_cleanup, visibility_opt, doc_opt)
78
+ # 可能因为 enable_* 选项而未执行,所以不强制要求
79
+
80
+ return True
81
+ except Exception:
82
+ # 如果读取失败,保守地认为未完成
83
+ return False
84
+
85
+
86
+ app = typer.Typer(help="C2Rust 命令行工具")
87
+
88
+ # 显式定义根回调,确保为命令组而非单函数入口
89
+ @app.callback()
90
+ def _root():
91
+ """
92
+ C2Rust 命令行工具
93
+ """
94
+ # 设置环境变量,标识当前运行在 c2rust 环境中
95
+ os.environ["JARVIS_C2RUST_ENABLED"] = "1"
96
+ # 不做任何处理,仅作为命令组的占位,使 'scan' 作为子命令出现
97
+ init_env("")
98
+ pass
99
+
100
+
101
+ def _load_config() -> dict:
102
+ """
103
+ 从配置文件加载配置。
104
+ 返回包含 root_symbols、disabled_libraries 和 additional_notes 的字典。
105
+ """
106
+ import json
107
+ from jarvis.jarvis_c2rust.constants import CONFIG_JSON, C2RUST_DIRNAME
108
+
109
+ data_dir = Path(".") / C2RUST_DIRNAME
110
+ config_path = data_dir / CONFIG_JSON
111
+ default_config = {"root_symbols": [], "disabled_libraries": [], "additional_notes": ""}
112
+
113
+ if not config_path.exists():
114
+ return default_config
115
+
116
+ try:
117
+ with config_path.open("r", encoding="utf-8") as f:
118
+ config = json.load(f)
119
+ if not isinstance(config, dict):
120
+ return default_config
121
+ # 确保包含所有必需的键
122
+ return {
123
+ "root_symbols": config.get("root_symbols", []),
124
+ "disabled_libraries": config.get("disabled_libraries", []),
125
+ "additional_notes": config.get("additional_notes", ""),
126
+ }
127
+ except Exception:
128
+ return default_config
129
+
130
+
131
+ RUN_STATE_JSON = "run_state.json"
132
+
133
+
134
+ def _get_run_state_path() -> Path:
135
+ """获取 run 状态文件路径"""
136
+ from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
137
+ data_dir = Path(".") / C2RUST_DIRNAME
138
+ return data_dir / RUN_STATE_JSON
139
+
140
+
141
+ def _load_run_state() -> dict:
142
+ """加载 run 状态文件"""
143
+ import json
144
+
145
+ state_path = _get_run_state_path()
146
+ default_state = {
147
+ "scan": {"completed": False, "timestamp": None},
148
+ "lib_replace": {"completed": False, "timestamp": None},
149
+ "prepare": {"completed": False, "timestamp": None},
150
+ "transpile": {"completed": False, "timestamp": None},
151
+ "optimize": {"completed": False, "timestamp": None},
152
+ }
153
+
154
+ if not state_path.exists():
155
+ return default_state
156
+
157
+ try:
158
+ with state_path.open("r", encoding="utf-8") as f:
159
+ state = json.load(f)
160
+ if not isinstance(state, dict):
161
+ return default_state
162
+ # 确保包含所有必需的阶段
163
+ for stage in ["scan", "lib_replace", "prepare", "transpile", "optimize"]:
164
+ if stage not in state:
165
+ state[stage] = {"completed": False, "timestamp": None}
166
+ return state
167
+ except Exception:
168
+ return default_state
169
+
170
+
171
+ def _save_run_state(stage: str, completed: bool = True) -> None:
172
+ """保存 run 状态文件"""
173
+ import json
174
+ import time
175
+
176
+ state_path = _get_run_state_path()
177
+ state = _load_run_state()
178
+
179
+ state[stage] = {
180
+ "completed": completed,
181
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime()) if completed else None,
182
+ }
183
+
184
+ try:
185
+ state_path.parent.mkdir(parents=True, exist_ok=True)
186
+ with state_path.open("w", encoding="utf-8") as f:
187
+ json.dump(state, f, ensure_ascii=False, indent=2)
188
+ except Exception as e:
189
+ typer.secho(f"[c2rust-run] 保存状态文件失败: {e}", fg=typer.colors.YELLOW, err=True)
190
+
191
+
192
+ @app.command("config")
193
+ def config(
194
+ files: Optional[List[Path]] = typer.Option(
195
+ None, "--files", help="头文件(.h/.hh/.hpp/.hxx)或函数名列表文件(每行一个函数名,忽略空行与以#开头的注释)"
196
+ ),
197
+ root_list_syms: Optional[str] = typer.Option(
198
+ None, "--root-list-syms", help="根符号列表内联(逗号分隔)"
199
+ ),
200
+ disabled_libs: Optional[str] = typer.Option(
201
+ None, "--disabled-libs", help="禁用库列表(逗号分隔)"
202
+ ),
203
+ additional_notes: Optional[str] = typer.Option(
204
+ None, "--additional-notes", help="附加说明(将在所有 agent 的提示词中追加)"
205
+ ),
206
+ show: bool = typer.Option(
207
+ False, "--show", help="显示当前配置内容"
208
+ ),
209
+ clear: bool = typer.Option(
210
+ False, "--clear", help="清空配置(重置为默认值)"
211
+ ),
212
+ ) -> None:
213
+ """
214
+ 管理转译配置文件(.jarvis/c2rust/config.json)。
215
+
216
+ 可以设置根符号列表(root_symbols)、禁用库列表(disabled_libraries)和附加说明(additional_notes)。
217
+ 这些配置会被 transpile 命令自动读取和使用。
218
+
219
+ 示例:
220
+ # 从头文件自动提取函数名并设置根符号列表
221
+ jarvis-c2rust config --files bzlib.h
222
+
223
+ # 从多个头文件提取函数名
224
+ jarvis-c2rust config --files a.h b.hpp c.hxx
225
+
226
+ # 从函数名列表文件设置根符号列表
227
+ jarvis-c2rust config --files roots.txt
228
+
229
+ # 从命令行设置根符号列表
230
+ jarvis-c2rust config --root-list-syms "func1,func2,func3"
231
+
232
+ # 设置禁用库列表
233
+ jarvis-c2rust config --disabled-libs "libc,libm"
234
+
235
+ # 设置附加说明(将在所有 agent 的提示词中追加)
236
+ jarvis-c2rust config --additional-notes "注意:所有函数必须处理错误情况,避免 panic"
237
+
238
+ # 同时设置多个参数
239
+ jarvis-c2rust config --files bzlib.h --disabled-libs "libc" --additional-notes "特殊要求说明"
240
+
241
+ # 查看当前配置
242
+ jarvis-c2rust config --show
243
+
244
+ # 清空配置
245
+ jarvis-c2rust config --clear
246
+ """
247
+ import json
248
+ from jarvis.jarvis_c2rust.constants import CONFIG_JSON, C2RUST_DIRNAME
249
+
250
+ data_dir = Path(".") / C2RUST_DIRNAME
251
+ config_path = data_dir / CONFIG_JSON
252
+ data_dir.mkdir(parents=True, exist_ok=True)
253
+
254
+ # 读取现有配置
255
+ default_config = {"root_symbols": [], "disabled_libraries": [], "additional_notes": ""}
256
+ current_config = default_config.copy()
257
+
258
+ if config_path.exists():
259
+ try:
260
+ with config_path.open("r", encoding="utf-8") as f:
261
+ current_config = json.load(f)
262
+ if not isinstance(current_config, dict):
263
+ current_config = default_config.copy()
264
+ except Exception as e:
265
+ typer.secho(f"[c2rust-config] 读取现有配置失败: {e},将使用默认值", fg=typer.colors.YELLOW)
266
+ current_config = default_config.copy()
267
+
268
+ # 如果只是查看配置
269
+ if show:
270
+ typer.secho(f"[c2rust-config] 当前配置文件: {config_path}", fg=typer.colors.BLUE)
271
+ typer.secho(json.dumps(current_config, ensure_ascii=False, indent=2), fg=typer.colors.CYAN)
272
+ return
273
+
274
+ # 如果清空配置
275
+ if clear:
276
+ current_config = default_config.copy()
277
+ with config_path.open("w", encoding="utf-8") as f:
278
+ json.dump(current_config, f, ensure_ascii=False, indent=2)
279
+ typer.secho(f"[c2rust-config] 配置已清空: {config_path}", fg=typer.colors.GREEN)
280
+ return
281
+
282
+ # 读取根符号列表(从现有配置开始,以便追加而不是替换)
283
+ root_symbols: List[str] = list(current_config.get("root_symbols", []))
284
+ header_exts = {".h", ".hh", ".hpp", ".hxx"}
285
+
286
+ if files:
287
+ for file_path in files:
288
+ try:
289
+ file_path = Path(file_path).resolve()
290
+ if not file_path.exists():
291
+ typer.secho(f"[c2rust-config] 警告: 文件不存在,跳过: {file_path}", fg=typer.colors.YELLOW)
292
+ continue
293
+
294
+ # 检查是否是头文件
295
+ if file_path.suffix.lower() in header_exts:
296
+ # 从头文件提取函数名
297
+ typer.secho(f"[c2rust-config] 从头文件提取函数名: {file_path}", fg=typer.colors.BLUE)
298
+ try:
299
+ from jarvis.jarvis_c2rust.collector import collect_function_names as _collect_fn_names
300
+ # 使用临时文件存储提取的函数名
301
+ import tempfile
302
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as tmp:
303
+ tmp_path = Path(tmp.name)
304
+ _collect_fn_names(files=[file_path], out_path=tmp_path, compile_commands_root=None)
305
+ # 读取提取的函数名
306
+ txt = tmp_path.read_text(encoding="utf-8")
307
+ collected = [ln.strip() for ln in txt.splitlines() if ln.strip()]
308
+ root_symbols.extend(collected)
309
+ typer.secho(f"[c2rust-config] 从头文件 {file_path.name} 提取了 {len(collected)} 个函数名", fg=typer.colors.GREEN)
310
+ # 清理临时文件
311
+ try:
312
+ tmp_path.unlink()
313
+ except Exception:
314
+ pass
315
+ except Exception as e:
316
+ typer.secho(f"[c2rust-config] 从头文件提取函数名失败: {file_path}: {e}", fg=typer.colors.RED, err=True)
317
+ raise typer.Exit(code=1)
318
+ else:
319
+ # 读取函数名列表文件(每行一个函数名)
320
+ txt = file_path.read_text(encoding="utf-8")
321
+ collected = [ln.strip() for ln in txt.splitlines() if ln.strip() and not ln.strip().startswith("#")]
322
+ root_symbols.extend(collected)
323
+ typer.secho(f"[c2rust-config] 从文件 {file_path.name} 读取了 {len(collected)} 个根符号", fg=typer.colors.BLUE)
324
+ except typer.Exit:
325
+ raise
326
+ except Exception as e:
327
+ typer.secho(f"[c2rust-config] 处理文件失败: {file_path}: {e}", fg=typer.colors.RED, err=True)
328
+ raise typer.Exit(code=1)
329
+
330
+ # 标记是否处理了 root_list_syms,以便即使结果为空也更新配置
331
+ processed_root_list_syms = False
332
+ if isinstance(root_list_syms, str) and root_list_syms.strip():
333
+ parts = [s.strip() for s in root_list_syms.replace("\n", ",").split(",") if s.strip()]
334
+ root_symbols.extend(parts)
335
+ processed_root_list_syms = True
336
+ typer.secho(f"[c2rust-config] 从命令行读取根符号: {len(parts)} 个", fg=typer.colors.BLUE)
337
+
338
+ # 去重根符号列表(如果处理了 files 或 root_list_syms,或者 root_symbols 非空,则更新配置)
339
+ if files or processed_root_list_syms or root_symbols:
340
+ try:
341
+ root_symbols = list(dict.fromkeys(root_symbols))
342
+ except Exception:
343
+ root_symbols = sorted(list(set(root_symbols)))
344
+ current_config["root_symbols"] = root_symbols
345
+ typer.secho(f"[c2rust-config] 已设置根符号列表: {len(root_symbols)} 个", fg=typer.colors.GREEN)
346
+
347
+ # 读取禁用库列表
348
+ if isinstance(disabled_libs, str) and disabled_libs.strip():
349
+ disabled_list = [s.strip() for s in disabled_libs.replace("\n", ",").split(",") if s.strip()]
350
+ if disabled_list:
351
+ current_config["disabled_libraries"] = disabled_list
352
+ typer.secho(f"[c2rust-config] 已设置禁用库列表: {', '.join(disabled_list)}", fg=typer.colors.GREEN)
353
+
354
+ # 读取附加说明
355
+ if isinstance(additional_notes, str):
356
+ current_config["additional_notes"] = additional_notes.strip()
357
+ if additional_notes.strip():
358
+ typer.secho(f"[c2rust-config] 已设置附加说明: {len(additional_notes.strip())} 字符", fg=typer.colors.GREEN)
359
+ else:
360
+ typer.secho("[c2rust-config] 已清空附加说明", fg=typer.colors.GREEN)
361
+
362
+ # 如果没有提供任何参数,提示用户
363
+ if not files and not root_list_syms and not disabled_libs and additional_notes is None:
364
+ typer.secho("[c2rust-config] 未提供任何参数,使用 --show 查看当前配置,或使用 --help 查看帮助", fg=typer.colors.YELLOW)
365
+ return
366
+
367
+ # 保存配置
368
+ try:
369
+ with config_path.open("w", encoding="utf-8") as f:
370
+ json.dump(current_config, f, ensure_ascii=False, indent=2)
371
+ typer.secho(f"[c2rust-config] 配置已保存: {config_path}", fg=typer.colors.GREEN)
372
+ typer.secho(json.dumps(current_config, ensure_ascii=False, indent=2), fg=typer.colors.CYAN)
373
+ except Exception as e:
374
+ typer.secho(f"[c2rust-config] 保存配置失败: {e}", fg=typer.colors.RED, err=True)
375
+ raise typer.Exit(code=1)
376
+
377
+
378
+ @app.command("run")
379
+ def run(
380
+ llm_group: Optional[str] = typer.Option(
381
+ None,
382
+ "-g",
383
+ "--llm-group",
384
+ help="用于 LLM 相关阶段(lib-replace/prepare/transpile/optimize)的模型组",
385
+ ),
386
+ max_retries: int = typer.Option(
387
+ 0, "-m", "--max-retries", help="transpile 构建/修复与审查的最大重试次数(0 表示不限制)"
388
+ ),
389
+ interactive: bool = typer.Option(
390
+ False,
391
+ "--interactive",
392
+ help="启用交互模式(默认非交互模式)",
393
+ ),
394
+ reset: bool = typer.Option(
395
+ False,
396
+ "--reset",
397
+ help="重置状态,从头开始执行所有阶段",
398
+ ),
399
+ ) -> None:
400
+ """
401
+ 依次执行流水线:scan -> lib-replace -> prepare -> transpile -> optimize
402
+
403
+ 支持断点续跑:根据状态文件(.jarvis/c2rust/run_state.json)自动跳过已完成的阶段。
404
+
405
+ 约束:
406
+
407
+ - 根符号列表和禁用库列表从配置文件(.jarvis/c2rust/config.json)读取
408
+ 使用 jarvis-c2rust config 命令设置这些配置(例如:jarvis-c2rust config --files bzlib.h)
409
+
410
+ - 使用 --reset 可以重置状态,从头开始执行所有阶段
411
+
412
+ - prepare/transpile 会使用 --llm-group 指定的模型组
413
+
414
+ - optimize 阶段采用默认优化配置,自动检测 crate 根目录并进行保守优化(unsafe 清理、结构优化、可见性优化、文档补充)
415
+ """
416
+
417
+ try:
418
+ # 加载状态文件
419
+ if reset:
420
+ # 重置状态
421
+ state_path = _get_run_state_path()
422
+ if state_path.exists():
423
+ state_path.unlink()
424
+ typer.secho("[c2rust-run] 已重置状态,将从头开始执行", fg=typer.colors.YELLOW)
425
+ state = _load_run_state()
426
+ else:
427
+ state = _load_run_state()
428
+ # 显示当前状态
429
+ completed_stages = [s for s, info in state.items() if info.get("completed", False)]
430
+ if completed_stages:
431
+ typer.secho(f"[c2rust-run] 检测到已完成阶段: {', '.join(completed_stages)},将从断点继续", fg=typer.colors.CYAN)
432
+
433
+ # Step 1: scan
434
+ if not state.get("scan", {}).get("completed", False):
435
+ typer.secho("[c2rust-run] scan: 开始", fg=typer.colors.BLUE)
436
+ _run_scan(dot=None, only_dot=False, subgraphs_dir=None, only_subgraphs=False, png=False, non_interactive=True)
437
+ typer.secho("[c2rust-run] scan: 完成", fg=typer.colors.GREEN)
438
+ # 保存状态(因为直接调用 _run_scan 函数,需要手动保存状态)
439
+ _save_run_state("scan", completed=True)
440
+ else:
441
+ typer.secho("[c2rust-run] scan: 已完成,跳过", fg=typer.colors.CYAN)
442
+
443
+ # Step 2: lib-replace(从配置文件读取根列表和禁用库列表)
444
+ if not state.get("lib_replace", {}).get("completed", False):
445
+ # 从配置文件读取基础配置
446
+ config = _load_config()
447
+ root_names: List[str] = list(config.get("root_symbols", []))
448
+ disabled_list: Optional[List[str]] = config.get("disabled_libraries", []) or None
449
+
450
+ # 去重并校验(允许为空时回退为自动根集)
451
+ if root_names:
452
+ try:
453
+ root_names = list(dict.fromkeys(root_names))
454
+ except Exception:
455
+ root_names = sorted(list(set(root_names)))
456
+
457
+ candidates_list: Optional[List[str]] = root_names if root_names else None
458
+ if not candidates_list:
459
+ typer.secho("[c2rust-run] lib-replace: 根列表为空,将回退为自动检测的根集合(基于扫描结果)", fg=typer.colors.YELLOW)
460
+
461
+ if disabled_list:
462
+ typer.secho(f"[c2rust-run] lib-replace: 从配置文件读取禁用库: {', '.join(disabled_list)}", fg=typer.colors.BLUE)
463
+
464
+ # 执行 lib-replace(默认库 std)
465
+ library = "std"
466
+ root_count_str = str(len(candidates_list)) if candidates_list is not None else "auto"
467
+ typer.secho(f"[c2rust-run] lib-replace: 开始(库: {library},根数: {root_count_str})", fg=typer.colors.BLUE)
468
+ ret = _apply_library_replacement(
469
+ db_path=Path("."),
470
+ library_name=library,
471
+ llm_group=llm_group,
472
+ candidates=candidates_list, # None 表示自动检测全部根
473
+ out_symbols_path=None,
474
+ out_mapping_path=None,
475
+ max_funcs=None,
476
+ disabled_libraries=disabled_list,
477
+ non_interactive=not interactive,
478
+ )
479
+ try:
480
+ order_msg = f"\n[c2rust-run] lib-replace: 转译顺序: {ret['order']}" if 'order' in ret else ""
481
+ typer.secho(
482
+ f"[c2rust-run] lib-replace: 替代映射: {ret['mapping']}\n"
483
+ f"[c2rust-run] lib-replace: 新符号表: {ret['symbols']}"
484
+ + order_msg,
485
+ fg=typer.colors.GREEN,
486
+ )
487
+ except Exception as _e:
488
+ typer.secho(f"[c2rust-run] lib-replace: 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
489
+ # 保存状态(因为直接调用 _apply_library_replacement 函数,需要手动保存状态)
490
+ _save_run_state("lib_replace", completed=True)
491
+ else:
492
+ typer.secho("[c2rust-run] lib-replace: 已完成,跳过", fg=typer.colors.CYAN)
493
+
494
+ # Step 3: prepare
495
+ if not state.get("prepare", {}).get("completed", False):
496
+ typer.secho("[c2rust-run] prepare: 开始", fg=typer.colors.BLUE)
497
+ _execute_llm_plan(apply=True, llm_group=llm_group, non_interactive=not interactive)
498
+ typer.secho("[c2rust-run] prepare: 完成", fg=typer.colors.GREEN)
499
+ # 保存状态(因为直接调用 _execute_llm_plan 函数,需要手动保存状态)
500
+ _save_run_state("prepare", completed=True)
501
+ else:
502
+ typer.secho("[c2rust-run] prepare: 已完成,跳过", fg=typer.colors.CYAN)
503
+
504
+ # Step 4: transpile
505
+ if not state.get("transpile", {}).get("completed", False):
506
+ typer.secho("[c2rust-run] transpile: 开始", fg=typer.colors.BLUE)
507
+ from jarvis.jarvis_c2rust.transpiler import run_transpile as _run_transpile
508
+ # 从配置文件读取配置(transpile 内部会自动读取)
509
+ _run_transpile(
510
+ project_root=Path("."),
511
+ crate_dir=None,
512
+ llm_group=llm_group,
513
+ max_retries=max_retries,
514
+ disabled_libraries=None, # 从配置文件恢复
515
+ root_symbols=None, # 从配置文件恢复
516
+ non_interactive=not interactive,
517
+ )
518
+ typer.secho("[c2rust-run] transpile: 完成", fg=typer.colors.GREEN)
519
+ # 保存状态(因为直接调用 _run_transpile 函数,需要手动保存状态)
520
+ _save_run_state("transpile", completed=True)
521
+ else:
522
+ typer.secho("[c2rust-run] transpile: 已完成,跳过", fg=typer.colors.CYAN)
523
+
524
+ # Step 5: optimize
525
+ if not state.get("optimize", {}).get("completed", False):
526
+ try:
527
+ typer.secho("[c2rust-run] optimize: 开始", fg=typer.colors.BLUE)
528
+ from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
529
+ from jarvis.jarvis_c2rust.utils import default_crate_dir
530
+ # 使用与 transpile 相同的逻辑确定项目根目录和 crate 目录
531
+ project_root = Path(".")
532
+ crate_dir = default_crate_dir(project_root)
533
+ typer.secho(f"[c2rust-run] optimize: 使用项目根目录: {project_root}, crate 目录: {crate_dir}", fg=typer.colors.CYAN)
534
+ res = _optimize_project(project_root=project_root, crate_dir=crate_dir, llm_group=llm_group, non_interactive=not interactive)
535
+ summary = (
536
+ f"[c2rust-run] optimize: 结果摘要:\n"
537
+ f" files_scanned: {res.get('files_scanned')}\n"
538
+ f" unsafe_removed: {res.get('unsafe_removed')}\n"
539
+ f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
540
+ f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
541
+ f" docs_added: {res.get('docs_added')}\n"
542
+ f" cargo_checks: {res.get('cargo_checks')}\n"
543
+ )
544
+ typer.secho(summary, fg=typer.colors.GREEN)
545
+
546
+ # 检查优化是否真正完成(所有步骤都完成,包括 clippy 告警修复)
547
+ optimize_truly_completed = _check_optimize_completed(crate_dir)
548
+ if optimize_truly_completed:
549
+ typer.secho("[c2rust-run] optimize: 完成", fg=typer.colors.GREEN)
550
+ # 保存状态(因为直接调用 _optimize_project 函数,需要手动保存状态)
551
+ _save_run_state("optimize", completed=True)
552
+ else:
553
+ typer.secho("[c2rust-run] optimize: 部分步骤未完成(如 clippy 告警未完全修复),下次将继续", fg=typer.colors.YELLOW)
554
+ # 不保存状态,下次恢复时会继续执行优化
555
+ except Exception as _e:
556
+ typer.secho(f"[c2rust-run] optimize: 错误: {_e}", fg=typer.colors.RED, err=True)
557
+ raise
558
+ else:
559
+ typer.secho("[c2rust-run] optimize: 已完成,跳过", fg=typer.colors.CYAN)
560
+
561
+ # 所有阶段完成
562
+ typer.secho("[c2rust-run] 所有阶段已完成!", fg=typer.colors.GREEN)
563
+ except Exception as e:
564
+ typer.secho(f"[c2rust-run] 错误: {e}", fg=typer.colors.RED, err=True)
565
+ raise typer.Exit(code=1)
566
+
567
+
568
+
569
+ def main() -> None:
570
+ """主入口"""
571
+ app()
572
+
573
+
574
+ if __name__ == "__main__":
575
+ main()