jarvis-ai-assistant 0.3.30__py3-none-any.whl → 0.7.0__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 (115) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +289 -87
  3. jarvis/jarvis_agent/agent_manager.py +17 -8
  4. jarvis/jarvis_agent/edit_file_handler.py +374 -86
  5. jarvis/jarvis_agent/event_bus.py +1 -1
  6. jarvis/jarvis_agent/file_context_handler.py +79 -0
  7. jarvis/jarvis_agent/jarvis.py +601 -43
  8. jarvis/jarvis_agent/main.py +32 -2
  9. jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
  10. jarvis/jarvis_agent/run_loop.py +38 -5
  11. jarvis/jarvis_agent/share_manager.py +8 -1
  12. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  13. jarvis/jarvis_agent/task_analyzer.py +5 -2
  14. jarvis/jarvis_agent/task_planner.py +496 -0
  15. jarvis/jarvis_agent/utils.py +5 -1
  16. jarvis/jarvis_agent/web_bridge.py +189 -0
  17. jarvis/jarvis_agent/web_output_sink.py +53 -0
  18. jarvis/jarvis_agent/web_server.py +751 -0
  19. jarvis/jarvis_c2rust/__init__.py +26 -0
  20. jarvis/jarvis_c2rust/cli.py +613 -0
  21. jarvis/jarvis_c2rust/collector.py +258 -0
  22. jarvis/jarvis_c2rust/library_replacer.py +1122 -0
  23. jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
  24. jarvis/jarvis_c2rust/optimizer.py +960 -0
  25. jarvis/jarvis_c2rust/scanner.py +1681 -0
  26. jarvis/jarvis_c2rust/transpiler.py +2325 -0
  27. jarvis/jarvis_code_agent/build_validation_config.py +133 -0
  28. jarvis/jarvis_code_agent/code_agent.py +1171 -94
  29. jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
  30. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  31. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  32. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
  33. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
  34. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  35. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
  36. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
  37. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
  38. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
  39. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
  40. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
  41. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
  42. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
  43. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
  44. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  45. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
  46. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  47. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  48. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  49. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  50. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  51. jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
  52. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
  53. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
  54. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
  55. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
  56. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
  57. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
  58. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
  59. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
  60. jarvis/jarvis_code_agent/lint.py +270 -8
  61. jarvis/jarvis_code_agent/utils.py +142 -0
  62. jarvis/jarvis_code_analysis/code_review.py +483 -569
  63. jarvis/jarvis_data/config_schema.json +97 -8
  64. jarvis/jarvis_git_utils/git_commiter.py +38 -26
  65. jarvis/jarvis_mcp/sse_mcp_client.py +2 -2
  66. jarvis/jarvis_mcp/stdio_mcp_client.py +1 -1
  67. jarvis/jarvis_memory_organizer/memory_organizer.py +1 -1
  68. jarvis/jarvis_multi_agent/__init__.py +239 -25
  69. jarvis/jarvis_multi_agent/main.py +37 -1
  70. jarvis/jarvis_platform/base.py +103 -51
  71. jarvis/jarvis_platform/openai.py +26 -1
  72. jarvis/jarvis_platform/yuanbao.py +1 -1
  73. jarvis/jarvis_platform_manager/service.py +2 -2
  74. jarvis/jarvis_rag/cli.py +4 -4
  75. jarvis/jarvis_sec/__init__.py +3605 -0
  76. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  77. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  78. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  79. jarvis/jarvis_sec/cli.py +116 -0
  80. jarvis/jarvis_sec/report.py +257 -0
  81. jarvis/jarvis_sec/status.py +264 -0
  82. jarvis/jarvis_sec/types.py +20 -0
  83. jarvis/jarvis_sec/workflow.py +219 -0
  84. jarvis/jarvis_stats/cli.py +1 -1
  85. jarvis/jarvis_stats/stats.py +1 -1
  86. jarvis/jarvis_stats/visualizer.py +1 -1
  87. jarvis/jarvis_tools/cli/main.py +1 -0
  88. jarvis/jarvis_tools/execute_script.py +46 -9
  89. jarvis/jarvis_tools/generate_new_tool.py +3 -1
  90. jarvis/jarvis_tools/read_code.py +275 -12
  91. jarvis/jarvis_tools/read_symbols.py +141 -0
  92. jarvis/jarvis_tools/read_webpage.py +5 -3
  93. jarvis/jarvis_tools/registry.py +73 -35
  94. jarvis/jarvis_tools/search_web.py +15 -11
  95. jarvis/jarvis_tools/sub_agent.py +24 -42
  96. jarvis/jarvis_tools/sub_code_agent.py +14 -13
  97. jarvis/jarvis_tools/virtual_tty.py +1 -1
  98. jarvis/jarvis_utils/config.py +187 -35
  99. jarvis/jarvis_utils/embedding.py +3 -0
  100. jarvis/jarvis_utils/git_utils.py +181 -6
  101. jarvis/jarvis_utils/globals.py +3 -3
  102. jarvis/jarvis_utils/http.py +1 -1
  103. jarvis/jarvis_utils/input.py +78 -2
  104. jarvis/jarvis_utils/methodology.py +25 -19
  105. jarvis/jarvis_utils/utils.py +644 -359
  106. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/METADATA +85 -1
  107. jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
  108. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +4 -0
  109. jarvis/jarvis_agent/config.py +0 -92
  110. jarvis/jarvis_tools/edit_file.py +0 -179
  111. jarvis/jarvis_tools/rewrite_file.py +0 -191
  112. jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
  113. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
  114. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
  115. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.0.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、symbol_map.jsonl 等
9
+
10
+ 推荐用法(CLI):
11
+ - 扫描: jarvis-c2rust scan
12
+ - 库替代评估: jarvis-c2rust lib-replace --root-list-file roots.txt [--disabled-libs ...]
13
+ - 规划/落盘: jarvis-c2rust prepare [-g <llm-group>]
14
+ - 转译: jarvis-c2rust transpile [-g <llm-group>] [--only ...] [--max-retries N] [--resume/--no-resume]
15
+ - 代码优化: jarvis-c2rust optimize [--crate-dir ...] [--unsafe/--no-unsafe] [--structure/--no-structure] [--visibility/--no-visibility] [--doc/--no-doc] [-m N] [--dry-run]
16
+ - 头文件收集: jarvis-c2rust collect <hdrs...> -o roots.txt
17
+ - 一键流水线: jarvis-c2rust run [--files <hdrs...> -o roots.txt | --root-list-syms ...] [-g <llm-group>] [--disabled-libs ...]
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,613 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ C2Rust 独立命令行入口。
4
+
5
+ 提供分组式 CLI,将扫描能力作为子命令 scan 暴露:
6
+ - jarvis-c2rust scan --root <path> [--dot ...] [--only-dot] [--subgraphs-dir ...] [--only-subgraphs] [--png]
7
+
8
+ 实现策略:
9
+ - 复用 scanner.cli 的核心逻辑,避免重复代码。
10
+ - 使用 Typer 分组式结构,便于后续扩展更多子命令(如 analyze/export 等)。
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ from pathlib import Path
17
+ from typing import Optional, List
18
+
19
+ import typer
20
+ from jarvis.jarvis_c2rust.scanner import run_scan as _run_scan
21
+ from jarvis.jarvis_c2rust.library_replacer import (
22
+ apply_library_replacement as _apply_library_replacement,
23
+ )
24
+ from jarvis.jarvis_utils.utils import init_env
25
+ from jarvis.jarvis_c2rust.llm_module_agent import (
26
+ execute_llm_plan as _execute_llm_plan,
27
+ )
28
+
29
+ app = typer.Typer(help="C2Rust 命令行工具")
30
+
31
+ # 显式定义根回调,确保为命令组而非单函数入口
32
+ @app.callback()
33
+ def _root():
34
+ """
35
+ C2Rust 命令行工具
36
+ """
37
+ # 设置环境变量,标识当前运行在 c2rust 环境中
38
+ os.environ["JARVIS_C2RUST_ENABLED"] = "1"
39
+ # 不做任何处理,仅作为命令组的占位,使 'scan' 作为子命令出现
40
+ init_env("欢迎使用 Jarvis C2Rust 工具")
41
+ pass
42
+
43
+
44
+ @app.command("scan")
45
+ def scan(
46
+ dot: Optional[Path] = typer.Option(
47
+ None,
48
+ "--dot",
49
+ help="扫描后将引用依赖图写入 DOT 文件(或与 --only-dot 一起使用)",
50
+ ),
51
+ only_dot: bool = typer.Option(
52
+ False,
53
+ "--only-dot",
54
+ help="不重新扫描。读取现有数据 (JSONL) 并仅生成 DOT(需要 --dot)",
55
+ ),
56
+ subgraphs_dir: Optional[Path] = typer.Option(
57
+ None,
58
+ "--subgraphs-dir",
59
+ help="用于写入每个根函数引用子图 DOT 文件的目录(每个根函数一个文件)",
60
+ ),
61
+ only_subgraphs: bool = typer.Option(
62
+ False,
63
+ "--only-subgraphs",
64
+ help="不重新扫描。仅生成每个根函数的引用子图 DOT 文件(需要 --subgraphs-dir)",
65
+ ),
66
+ interactive: bool = typer.Option(
67
+ False,
68
+ "--interactive",
69
+ help="启用交互模式(默认非交互模式)",
70
+ ),
71
+ ) -> None:
72
+ """
73
+ 进行 C/C++ 函数扫描并生成引用关系 DOT 图;PNG 渲染默认启用(无需参数)。
74
+ """
75
+ _run_scan(
76
+ dot=dot,
77
+ only_dot=only_dot,
78
+ subgraphs_dir=subgraphs_dir,
79
+ only_subgraphs=only_subgraphs,
80
+ png=True,
81
+ non_interactive=not interactive,
82
+ )
83
+
84
+ @app.command("prepare")
85
+ def prepare(
86
+ llm_group: Optional[str] = typer.Option(
87
+ None, "-g", "--llm-group", help="指定用于规划的 LLM 模型组(仅影响本次运行)"
88
+ ),
89
+ interactive: bool = typer.Option(
90
+ False,
91
+ "--interactive",
92
+ help="启用交互模式(默认非交互模式)",
93
+ ),
94
+ ) -> None:
95
+ """
96
+ 使用 LLM Agent 基于根函数子图规划 Rust crate 模块结构并直接应用到磁盘。
97
+ 需先执行: jarvis-c2rust scan 以生成数据文件(symbols.jsonl)
98
+ 默认使用当前目录作为项目根,并从 <root>/.jarvis/c2rust/symbols.jsonl 读取数据
99
+ """
100
+ try:
101
+ _execute_llm_plan(apply=True, llm_group=llm_group, non_interactive=not interactive)
102
+ except Exception as e:
103
+ typer.secho(f"[c2rust-llm-planner] 错误: {e}", fg=typer.colors.RED, err=True)
104
+ raise typer.Exit(code=1)
105
+
106
+
107
+ @app.command("transpile")
108
+ def transpile(
109
+ llm_group: Optional[str] = typer.Option(
110
+ None, "-g", "--llm-group", help="指定用于翻译的 LLM 模型组"
111
+ ),
112
+ only: Optional[str] = typer.Option(
113
+ None, "--only", help="仅翻译指定的函数(名称或限定名称),以逗号分隔"
114
+ ),
115
+ max_retries: int = typer.Option(
116
+ 0, "-m", "--max-retries", help="构建/修复与审查的最大重试次数(0 表示不限制)"
117
+ ),
118
+ resume: bool = typer.Option(
119
+ True, "--resume/--no-resume", help="是否启用断点续跑(默认启用)"
120
+ ),
121
+ interactive: bool = typer.Option(
122
+ False,
123
+ "--interactive",
124
+ help="启用交互模式(默认非交互模式)",
125
+ ),
126
+ ) -> None:
127
+ """
128
+ 使用转译器按扫描顺序逐个函数转译并构建修复。
129
+ 需先执行: jarvis-c2rust scan 以生成数据文件(symbols.jsonl 与 translation_order.jsonl)
130
+ 默认使用当前目录作为项目根,并从 <root>/.jarvis/c2rust/symbols.jsonl 读取数据。
131
+ 未指定目标 crate 时,使用默认 <cwd>/<cwd.name>_rs。
132
+
133
+ 选项:
134
+ - --only: 仅翻译指定的函数(名称或限定名称),以逗号分隔
135
+ - --max-retries/-m: 构建/修复与审查的最大重试次数(0 表示不限制)
136
+ - --resume/--no-resume: 是否启用断点续跑(默认启用)
137
+ - --llm-group/-g: 指定用于翻译的 LLM 模型组
138
+ """
139
+ try:
140
+ # Lazy import to avoid hard dependency if not used
141
+ from jarvis.jarvis_c2rust.transpiler import run_transpile as _run_transpile
142
+ only_list = [s.strip() for s in str(only).split(",") if s.strip()] if only else None
143
+ _run_transpile(
144
+ project_root=Path("."),
145
+ crate_dir=None,
146
+ llm_group=llm_group,
147
+ max_retries=max_retries,
148
+ resume=resume,
149
+ only=only_list,
150
+ non_interactive=not interactive,
151
+ )
152
+ except Exception as e:
153
+ typer.secho(f"[c2rust-transpiler] 错误: {e}", fg=typer.colors.RED, err=True)
154
+ raise typer.Exit(code=1)
155
+
156
+
157
+ @app.command("lib-replace")
158
+ def lib_replace(
159
+ llm_group: Optional[str] = typer.Option(
160
+ None, "-g", "--llm-group", help="用于评估的 LLM 模型组"
161
+ ),
162
+ root_list_file: Optional[Path] = typer.Option(
163
+ None, "--root-list-file", help="根列表文件:按行列出要参与评估的根符号名称或限定名(忽略空行与以#开头的注释)"
164
+ ),
165
+ root_list_syms: Optional[str] = typer.Option(
166
+ None, "--root-list-syms", help="根列表内联:以逗号分隔的符号名称或限定名(仅评估这些根)"
167
+ ),
168
+ disabled_libs: Optional[str] = typer.Option(
169
+ None, "--disabled-libs", help="禁用库列表:逗号分隔的库名(评估时禁止使用这些库)"
170
+ ),
171
+ interactive: bool = typer.Option(
172
+ False,
173
+ "--interactive",
174
+ help="启用交互模式(默认非交互模式)",
175
+ ),
176
+ ) -> None:
177
+ """
178
+ Root-list 评估模式(必须走 LLM 评估):
179
+ - 必须提供根列表(--root-list-file 或 --root-list-syms,至少一种)
180
+ - 仅对根列表中的符号作为评估根执行 LLM 子树评估
181
+ - 若可替代:替换该根的 ref 为库占位,并剪除其所有子孙函数(根本身保留)
182
+ - 需先执行: jarvis-c2rust scan 以生成数据文件(symbols.jsonl)
183
+ - 默认库: std(仅用于对后续流程保持一致的默认上下文)
184
+ - 可选:--disabled-libs 指定评估时禁止使用的库列表(逗号分隔)
185
+ """
186
+ try:
187
+ data_dir = Path(".") / ".jarvis" / "c2rust"
188
+ curated_symbols = data_dir / "symbols.jsonl"
189
+ raw_symbols = data_dir / "symbols_raw.jsonl"
190
+ if not curated_symbols.exists() and not raw_symbols.exists():
191
+ typer.secho("[c2rust-lib-replace] 未找到符号数据(symbols.jsonl 或 symbols_raw.jsonl),正在执行扫描以生成数据...", fg=typer.colors.YELLOW)
192
+ _run_scan(dot=None, only_dot=False, subgraphs_dir=None, only_subgraphs=False, png=False)
193
+ if not curated_symbols.exists() and not raw_symbols.exists():
194
+ raise FileNotFoundError(f"未找到符号数据: {curated_symbols} 或 {raw_symbols}")
195
+
196
+ # 使用默认库: std
197
+ library = "std"
198
+
199
+ # 读取根列表(必填,至少提供一种来源)
200
+ root_names: List[str] = []
201
+ # 文件来源
202
+ if root_list_file is not None:
203
+ try:
204
+ txt = root_list_file.read_text(encoding="utf-8")
205
+ root_names.extend([ln.strip() for ln in txt.splitlines() if ln.strip() and not ln.strip().startswith("#")])
206
+ except Exception as _e:
207
+ typer.secho(f"[c2rust-lib-replace] 读取根列表失败: {root_list_file}: {_e}", fg=typer.colors.RED, err=True)
208
+ raise typer.Exit(code=1)
209
+ # 内联来源
210
+ if isinstance(root_list_syms, str) and root_list_syms.strip():
211
+ parts = [s.strip() for s in root_list_syms.replace("\n", ",").split(",") if s.strip()]
212
+ root_names.extend(parts)
213
+ # 去重
214
+ try:
215
+ root_names = list(dict.fromkeys(root_names))
216
+ except Exception:
217
+ root_names = sorted(list(set(root_names)))
218
+ if not root_names:
219
+ typer.secho("[c2rust-lib-replace] 错误:必须提供根列表(--root-list-file 或 --root-list-syms)。", fg=typer.colors.RED, err=True)
220
+ raise typer.Exit(code=2)
221
+
222
+ # 解析禁用库列表(可选)
223
+ disabled_list: Optional[List[str]] = None
224
+ if isinstance(disabled_libs, str) and disabled_libs.strip():
225
+ disabled_list = [s.strip() for s in disabled_libs.replace("\n", ",").split(",") if s.strip()]
226
+ if disabled_list:
227
+ typer.secho(f"[c2rust-lib-replace] 禁用库: {', '.join(disabled_list)}", fg=typer.colors.YELLOW)
228
+
229
+ # 必须走 LLM 评估:仅评估提供的根(candidates),不启用强制剪枝模式
230
+ ret = _apply_library_replacement(
231
+ db_path=Path("."),
232
+ library_name=library,
233
+ llm_group=llm_group,
234
+ candidates=root_names, # 仅评估这些根
235
+ out_symbols_path=None,
236
+ out_mapping_path=None,
237
+ max_funcs=None,
238
+ disabled_libraries=disabled_list,
239
+ non_interactive=not interactive,
240
+ )
241
+ # 输出简要结果摘要(底层已写出新的符号表与可选转译顺序)
242
+ try:
243
+ order_msg = f"\n[c2rust-lib-replace] 转译顺序: {ret['order']}" if 'order' in ret else ""
244
+ typer.secho(
245
+ f"[c2rust-lib-replace] 替代映射: {ret['mapping']}\n"
246
+ f"[c2rust-lib-replace] 新符号表: {ret['symbols']}"
247
+ + order_msg,
248
+ fg=typer.colors.GREEN,
249
+ )
250
+ except Exception as _e:
251
+ typer.secho(f"[c2rust-lib-replace] 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
252
+ except Exception as e:
253
+ typer.secho(f"[c2rust-lib-replace] 错误: {e}", fg=typer.colors.RED, err=True)
254
+ raise typer.Exit(code=1)
255
+
256
+
257
+
258
+ @app.command("collect")
259
+ def collect(
260
+ files: List[Path] = typer.Argument(..., help="一个或多个 C/C++ 头文件路径(.h/.hh/.hpp/.hxx)"),
261
+ out: Path = typer.Option(..., "-o", "--out", help="输出文件路径(写入唯一函数名,每行一个)"),
262
+ cc_root: Optional[Path] = typer.Option(
263
+ None, "--cc-root", help="compile_commands.json 根目录(可选,用于提升解析准确性)"
264
+ ),
265
+ interactive: bool = typer.Option(
266
+ False,
267
+ "--interactive",
268
+ help="启用交互模式(默认非交互模式)",
269
+ ),
270
+ ) -> None:
271
+ """
272
+ 收集指定头文件中的函数名(使用 libclang 解析),并写入指定输出文件(每行一个)。
273
+ 示例:
274
+ jarvis-c2rust collect a.h b.hpp -o funcs.txt
275
+ 说明:
276
+ 非头文件会被跳过(仅支持 .h/.hh/.hpp/.hxx)。
277
+ """
278
+ try:
279
+ from jarvis.jarvis_c2rust.collector import collect_function_names as _collect_fn_names
280
+ _collect_fn_names(files=files, out_path=out, compile_commands_root=cc_root)
281
+ typer.secho(f"[c2rust-collect] 函数名已写入: {out}", fg=typer.colors.GREEN)
282
+ except Exception as e:
283
+ typer.secho(f"[c2rust-collect] 错误: {e}", fg=typer.colors.RED, err=True)
284
+ raise typer.Exit(code=1)
285
+
286
+ @app.command("run")
287
+ def run(
288
+ files: Optional[List[Path]] = typer.Option(
289
+ None,
290
+ "--files",
291
+ help="用于 collect 阶段的头文件列表(.h/.hh/.hpp/.hxx);提供则先执行 collect",
292
+ ),
293
+ out: Optional[Path] = typer.Option(
294
+ None,
295
+ "-o",
296
+ "--out",
297
+ help="collect 输出函数名文件;若未提供且指定 --files 则默认为 <root>/.jarvis/c2rust/roots.txt",
298
+ ),
299
+ llm_group: Optional[str] = typer.Option(
300
+ None,
301
+ "-g",
302
+ "--llm-group",
303
+ help="用于 LLM 相关阶段(lib-replace/prepare/transpile/optimize)的模型组",
304
+ ),
305
+ root_list_file: Optional[Path] = typer.Option(
306
+ None,
307
+ "--root-list-file",
308
+ help="兼容占位:run 会使用 collect 的 --out 作为 lib-replace 的输入;当提供 --files 时本参数将被忽略;未提供 --files 时,本命令要求使用 --root-list-syms",
309
+ ),
310
+ root_list_syms: Optional[str] = typer.Option(
311
+ None,
312
+ "--root-list-syms",
313
+ help="lib-replace 的根列表内联(逗号分隔)。未提供 --files 时该参数为必填",
314
+ ),
315
+ disabled_libs: Optional[str] = typer.Option(
316
+ None,
317
+ "--disabled-libs",
318
+ help="lib-replace 禁用库列表(逗号分隔)",
319
+ ),
320
+ max_retries: int = typer.Option(
321
+ 0, "-m", "--max-retries", help="transpile 构建/修复与审查的最大重试次数(0 表示不限制)"
322
+ ),
323
+ resume: bool = typer.Option(
324
+ True, "--resume/--no-resume", help="transpile 是否启用断点续跑(默认启用)"
325
+ ),
326
+ interactive: bool = typer.Option(
327
+ False,
328
+ "--interactive",
329
+ help="启用交互模式(默认非交互模式)",
330
+ ),
331
+ ) -> None:
332
+ """
333
+ 依次执行流水线:collect -> scan -> lib-replace -> prepare -> transpile -> optimize
334
+
335
+ 约束:
336
+
337
+ - collect 的输出文件就是 lib-replace 的输入文件;
338
+ 当提供 --files 时,lib-replace 将固定读取 --out(或默认值)作为根列表文件,忽略 --root-list-file
339
+
340
+ - 未提供 --files 时,必须通过 --root-list-syms 提供根列表
341
+
342
+ - scan 始终执行以确保数据完整
343
+
344
+ - prepare/transpile 会使用 --llm-group 指定的模型组
345
+
346
+ - optimize 阶段采用默认优化配置,自动检测 crate 根目录并进行保守优化(unsafe 清理、结构优化、可见性优化、文档补充)
347
+
348
+ 补充:
349
+
350
+ - 如需精细化控制,可独立调用子命令:collect、scan、lib-replace、prepare、transpile、optimize
351
+ """
352
+ try:
353
+ data_dir = Path(".") / ".jarvis" / "c2rust"
354
+ default_roots = data_dir / "roots.txt"
355
+
356
+ # Step 1: collect(可选)
357
+ roots_path: Optional[Path] = None
358
+ if files:
359
+ try:
360
+ if out is None:
361
+ out = default_roots
362
+ out.parent.mkdir(parents=True, exist_ok=True)
363
+ from jarvis.jarvis_c2rust.collector import (
364
+ collect_function_names as _collect_fn_names,
365
+ )
366
+ _collect_fn_names(files=files, out_path=out)
367
+ typer.secho(f"[c2rust-run] collect: 函数名已写入: {out}", fg=typer.colors.GREEN)
368
+ roots_path = out
369
+ except Exception as _e:
370
+ typer.secho(f"[c2rust-run] collect: 错误: {_e}", fg=typer.colors.RED, err=True)
371
+ raise
372
+
373
+ # Step 2: scan(始终执行)
374
+ typer.secho("[c2rust-run] scan: 开始", fg=typer.colors.BLUE)
375
+ _run_scan(dot=None, only_dot=False, subgraphs_dir=None, only_subgraphs=False, png=False, non_interactive=not interactive)
376
+ typer.secho("[c2rust-run] scan: 完成", fg=typer.colors.GREEN)
377
+
378
+ # Step 3: lib-replace(强制执行,依据约束获取根列表)
379
+ root_names: List[str] = []
380
+
381
+ if files:
382
+ # 约束:collect 的输出文件作为唯一文件来源
383
+ if not roots_path or not roots_path.exists():
384
+ typer.secho("[c2rust-run] lib-replace: 未找到 collect 输出文件,无法继续", fg=typer.colors.RED, err=True)
385
+ raise typer.Exit(code=2)
386
+ try:
387
+ txt = roots_path.read_text(encoding="utf-8")
388
+ root_names.extend([ln.strip() for ln in txt.splitlines() if ln.strip() and not ln.strip().startswith("#")])
389
+ typer.secho(f"[c2rust-run] lib-replace: 使用根列表文件: {roots_path}", fg=typer.colors.BLUE)
390
+ except Exception as _e:
391
+ typer.secho(f"[c2rust-run] lib-replace: 读取根列表失败: {roots_path}: {_e}", fg=typer.colors.RED, err=True)
392
+ raise
393
+ # 兼容参数提示
394
+ if root_list_file is not None:
395
+ typer.secho("[c2rust-run] 提示: --root-list-file 已被忽略(run 会固定使用 collect 的 --out 作为输入)", fg=typer.colors.YELLOW)
396
+ else:
397
+ # 约束:未传递 files 必须提供 --root-list-syms
398
+ if not (isinstance(root_list_syms, str) and root_list_syms.strip()):
399
+ typer.secho("[c2rust-run] 错误:未提供 --files 时,必须通过 --root-list-syms 指定根列表(逗号分隔)", fg=typer.colors.RED, err=True)
400
+ raise typer.Exit(code=2)
401
+ parts = [s.strip() for s in root_list_syms.replace("\n", ",").split(",") if s.strip()]
402
+ root_names.extend(parts)
403
+
404
+ # 去重并校验(允许为空时回退为自动根集)
405
+ try:
406
+ root_names = list(dict.fromkeys(root_names))
407
+ except Exception:
408
+ root_names = sorted(list(set(root_names)))
409
+ candidates_list: Optional[List[str]] = None
410
+ if root_names:
411
+ candidates_list = root_names
412
+ else:
413
+ typer.secho("[c2rust-run] lib-replace: 根列表为空,将回退为自动检测的根集合(基于扫描结果)", fg=typer.colors.YELLOW)
414
+
415
+ # 可选禁用库列表
416
+ disabled_list: Optional[List[str]] = None
417
+ if isinstance(disabled_libs, str) and disabled_libs.strip():
418
+ disabled_list = [s.strip() for s in disabled_libs.replace("\n", ",").split(",") if s.strip()]
419
+ if disabled_list:
420
+ typer.secho(f"[c2rust-run] lib-replace: 禁用库: {', '.join(disabled_list)}", fg=typer.colors.YELLOW)
421
+
422
+ # 执行 lib-replace(默认库 std)
423
+ library = "std"
424
+ root_count_str = str(len(candidates_list)) if candidates_list is not None else "auto"
425
+ typer.secho(f"[c2rust-run] lib-replace: 开始(库: {library},根数: {root_count_str})", fg=typer.colors.BLUE)
426
+ ret = _apply_library_replacement(
427
+ db_path=Path("."),
428
+ library_name=library,
429
+ llm_group=llm_group,
430
+ candidates=candidates_list, # None 表示自动检测全部根
431
+ out_symbols_path=None,
432
+ out_mapping_path=None,
433
+ max_funcs=None,
434
+ disabled_libraries=disabled_list,
435
+ non_interactive=not interactive,
436
+ )
437
+ try:
438
+ order_msg = f"\n[c2rust-run] lib-replace: 转译顺序: {ret['order']}" if 'order' in ret else ""
439
+ typer.secho(
440
+ f"[c2rust-run] lib-replace: 替代映射: {ret['mapping']}\n"
441
+ f"[c2rust-run] lib-replace: 新符号表: {ret['symbols']}"
442
+ + order_msg,
443
+ fg=typer.colors.GREEN,
444
+ )
445
+ except Exception as _e:
446
+ typer.secho(f"[c2rust-run] lib-replace: 结果输出时发生非致命错误: {_e}", fg=typer.colors.YELLOW, err=True)
447
+
448
+ # Step 4: prepare
449
+ typer.secho("[c2rust-run] prepare: 开始", fg=typer.colors.BLUE)
450
+ _execute_llm_plan(apply=True, llm_group=llm_group, non_interactive=not interactive)
451
+ typer.secho("[c2rust-run] prepare: 完成", fg=typer.colors.GREEN)
452
+
453
+ # Step 5: transpile
454
+ typer.secho("[c2rust-run] transpile: 开始", fg=typer.colors.BLUE)
455
+ from jarvis.jarvis_c2rust.transpiler import run_transpile as _run_transpile
456
+ _run_transpile(
457
+ project_root=Path("."),
458
+ crate_dir=None,
459
+ llm_group=llm_group,
460
+ max_retries=max_retries,
461
+ resume=resume,
462
+ only=None,
463
+ non_interactive=not interactive,
464
+ )
465
+ typer.secho("[c2rust-run] transpile: 完成", fg=typer.colors.GREEN)
466
+
467
+ # Step 6: optimize
468
+ try:
469
+ typer.secho("[c2rust-run] optimize: 开始", fg=typer.colors.BLUE)
470
+ from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
471
+ res = _optimize_project(crate_dir=None, llm_group=llm_group, non_interactive=not interactive)
472
+ summary = (
473
+ f"[c2rust-run] optimize: 结果摘要:\n"
474
+ f" files_scanned: {res.get('files_scanned')}\n"
475
+ f" unsafe_removed: {res.get('unsafe_removed')}\n"
476
+ f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
477
+ f" duplicates_tagged: {res.get('duplicates_tagged')}\n"
478
+ f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
479
+ f" docs_added: {res.get('docs_added')}\n"
480
+ f" cargo_checks: {res.get('cargo_checks')}\n"
481
+ )
482
+ typer.secho(summary, fg=typer.colors.GREEN)
483
+ typer.secho("[c2rust-run] optimize: 完成", fg=typer.colors.GREEN)
484
+ except Exception as _e:
485
+ typer.secho(f"[c2rust-run] optimize: 错误: {_e}", fg=typer.colors.RED, err=True)
486
+ raise
487
+ except Exception as e:
488
+ typer.secho(f"[c2rust-run] 错误: {e}", fg=typer.colors.RED, err=True)
489
+ raise typer.Exit(code=1)
490
+
491
+
492
+ @app.command("optimize")
493
+ def optimize(
494
+ crate_dir: Optional[Path] = typer.Option(
495
+ None, "--crate-dir", help="Rust crate 根目录(包含 Cargo.toml);未提供时自动检测"
496
+ ),
497
+ llm_group: Optional[str] = typer.Option(
498
+ None,
499
+ "-g",
500
+ "--llm-group",
501
+ help="用于 CodeAgent 修复与整体优化的 LLM 模型组",
502
+ ),
503
+ enable_unsafe_cleanup: bool = typer.Option(
504
+ True, "--unsafe/--no-unsafe", help="启用 unsafe 清理"
505
+ ),
506
+ enable_structure_opt: bool = typer.Option(
507
+ True, "--structure/--no-structure", help="启用代码结构优化(重复代码标注)"
508
+ ),
509
+ enable_visibility_opt: bool = typer.Option(
510
+ True, "--visibility/--no-visibility", help="启用可见性优化(尽可能最小可见性)"
511
+ ),
512
+ enable_doc_opt: bool = typer.Option(
513
+ True, "--doc/--no-doc", help="启用文档补充(模块/函数占位文档)"
514
+ ),
515
+ max_checks: int = typer.Option(
516
+ 0, "-m", "--max-checks", help="cargo check 最大次数限制(0 表示不限)"
517
+ ),
518
+ dry_run: bool = typer.Option(
519
+ False, "--dry-run", help="仅统计潜在修改,不写回文件"
520
+ ),
521
+ include_patterns: Optional[str] = typer.Option(
522
+ None,
523
+ "--include",
524
+ help="仅处理匹配的文件(逗号分隔 glob,相对 crate 根,如: src/**/*.rs,src/foo/*.rs)",
525
+ ),
526
+ exclude_patterns: Optional[str] = typer.Option(
527
+ None,
528
+ "--exclude",
529
+ help="排除匹配的文件(逗号分隔 glob,相对 crate 根)",
530
+ ),
531
+ max_files: int = typer.Option(
532
+ 0,
533
+ "-n",
534
+ "--max-files",
535
+ help="本次最多处理的文件数(0 表示不限,用于大项目分批优化)",
536
+ ),
537
+ resume: bool = typer.Option(
538
+ True,
539
+ "--resume/--no-resume",
540
+ help="是否启用断点续跑,跳过已处理文件(默认启用)",
541
+ ),
542
+ reset_progress: bool = typer.Option(
543
+ False,
544
+ "--reset-progress",
545
+ help="重置优化进度(清空已处理文件记录)后再执行",
546
+ ),
547
+ build_fix_retries: int = typer.Option(
548
+ 3,
549
+ "-r",
550
+ "--build-fix-retries",
551
+ help="构建失败后的最小修复重试次数(使用 CodeAgent 迭代修复)",
552
+ ),
553
+ git_guard: bool = typer.Option(
554
+ True,
555
+ "--git-guard/--no-git-guard",
556
+ help="启用Git保护:每一步优化前记录当前HEAD,修复耗尽时自动git reset --hard回快照(默认启用)",
557
+ ),
558
+ interactive: bool = typer.Option(
559
+ False,
560
+ "--interactive",
561
+ help="启用交互模式(默认非交互模式)",
562
+ ),
563
+
564
+ ) -> None:
565
+ """
566
+ 对生成的 Rust 代码执行保守优化(unsafe 清理、结构优化、可见性优化、文档补充)。
567
+ 建议在转译完成后执行:jarvis-c2rust optimize
568
+ """
569
+ try:
570
+ from jarvis.jarvis_c2rust.optimizer import optimize_project as _optimize_project
571
+
572
+ typer.secho("[c2rust-optimize] 开始", fg=typer.colors.BLUE)
573
+ res = _optimize_project(
574
+ crate_dir=crate_dir,
575
+ enable_unsafe_cleanup=enable_unsafe_cleanup,
576
+ enable_structure_opt=enable_structure_opt,
577
+ enable_visibility_opt=enable_visibility_opt,
578
+ enable_doc_opt=enable_doc_opt,
579
+ max_checks=max_checks,
580
+ dry_run=dry_run,
581
+ include_patterns=include_patterns,
582
+ exclude_patterns=exclude_patterns,
583
+ max_files=max_files,
584
+ resume=resume,
585
+ reset_progress=reset_progress,
586
+ build_fix_retries=build_fix_retries,
587
+ git_guard=git_guard,
588
+ llm_group=llm_group,
589
+ non_interactive=not interactive,
590
+ )
591
+ # 摘要输出
592
+ summary = (
593
+ f"[c2rust-optimize] 结果摘要:\n"
594
+ f" files_scanned: {res.get('files_scanned')}\n"
595
+ f" unsafe_removed: {res.get('unsafe_removed')}\n"
596
+ f" unsafe_annotated: {res.get('unsafe_annotated')}\n"
597
+ f" duplicates_tagged: {res.get('duplicates_tagged')}\n"
598
+ f" visibility_downgraded: {res.get('visibility_downgraded')}\n"
599
+ f" docs_added: {res.get('docs_added')}\n"
600
+ f" cargo_checks: {res.get('cargo_checks')}\n"
601
+ )
602
+ typer.secho(summary, fg=typer.colors.GREEN)
603
+ except Exception as e:
604
+ typer.secho(f"[c2rust-optimize] 错误: {e}", fg=typer.colors.RED, err=True)
605
+ raise typer.Exit(code=1)
606
+
607
+ def main() -> None:
608
+ """主入口"""
609
+ app()
610
+
611
+
612
+ if __name__ == "__main__":
613
+ main()