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.
Files changed (279) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +567 -222
  3. jarvis/jarvis_agent/agent_manager.py +19 -12
  4. jarvis/jarvis_agent/builtin_input_handler.py +79 -11
  5. jarvis/jarvis_agent/config_editor.py +7 -2
  6. jarvis/jarvis_agent/event_bus.py +24 -13
  7. jarvis/jarvis_agent/events.py +19 -1
  8. jarvis/jarvis_agent/file_context_handler.py +67 -64
  9. jarvis/jarvis_agent/file_methodology_manager.py +38 -24
  10. jarvis/jarvis_agent/jarvis.py +186 -114
  11. jarvis/jarvis_agent/language_extractors/__init__.py +8 -1
  12. jarvis/jarvis_agent/language_extractors/c_extractor.py +7 -4
  13. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +9 -4
  14. jarvis/jarvis_agent/language_extractors/go_extractor.py +7 -4
  15. jarvis/jarvis_agent/language_extractors/java_extractor.py +27 -20
  16. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +22 -17
  17. jarvis/jarvis_agent/language_extractors/python_extractor.py +7 -4
  18. jarvis/jarvis_agent/language_extractors/rust_extractor.py +7 -4
  19. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +22 -17
  20. jarvis/jarvis_agent/language_support_info.py +250 -219
  21. jarvis/jarvis_agent/main.py +19 -23
  22. jarvis/jarvis_agent/memory_manager.py +9 -6
  23. jarvis/jarvis_agent/methodology_share_manager.py +21 -15
  24. jarvis/jarvis_agent/output_handler.py +4 -2
  25. jarvis/jarvis_agent/prompt_builder.py +7 -6
  26. jarvis/jarvis_agent/prompt_manager.py +113 -8
  27. jarvis/jarvis_agent/prompts.py +317 -85
  28. jarvis/jarvis_agent/protocols.py +5 -2
  29. jarvis/jarvis_agent/run_loop.py +192 -32
  30. jarvis/jarvis_agent/session_manager.py +7 -3
  31. jarvis/jarvis_agent/share_manager.py +23 -13
  32. jarvis/jarvis_agent/shell_input_handler.py +12 -8
  33. jarvis/jarvis_agent/stdio_redirect.py +25 -26
  34. jarvis/jarvis_agent/task_analyzer.py +29 -23
  35. jarvis/jarvis_agent/task_list.py +869 -0
  36. jarvis/jarvis_agent/task_manager.py +26 -23
  37. jarvis/jarvis_agent/tool_executor.py +6 -5
  38. jarvis/jarvis_agent/tool_share_manager.py +24 -14
  39. jarvis/jarvis_agent/user_interaction.py +3 -3
  40. jarvis/jarvis_agent/utils.py +9 -1
  41. jarvis/jarvis_agent/web_bridge.py +37 -17
  42. jarvis/jarvis_agent/web_output_sink.py +5 -2
  43. jarvis/jarvis_agent/web_server.py +165 -36
  44. jarvis/jarvis_c2rust/__init__.py +1 -1
  45. jarvis/jarvis_c2rust/cli.py +260 -141
  46. jarvis/jarvis_c2rust/collector.py +37 -18
  47. jarvis/jarvis_c2rust/constants.py +60 -0
  48. jarvis/jarvis_c2rust/library_replacer.py +242 -1010
  49. jarvis/jarvis_c2rust/library_replacer_checkpoint.py +133 -0
  50. jarvis/jarvis_c2rust/library_replacer_llm.py +287 -0
  51. jarvis/jarvis_c2rust/library_replacer_loader.py +191 -0
  52. jarvis/jarvis_c2rust/library_replacer_output.py +134 -0
  53. jarvis/jarvis_c2rust/library_replacer_prompts.py +124 -0
  54. jarvis/jarvis_c2rust/library_replacer_utils.py +188 -0
  55. jarvis/jarvis_c2rust/llm_module_agent.py +98 -1044
  56. jarvis/jarvis_c2rust/llm_module_agent_apply.py +170 -0
  57. jarvis/jarvis_c2rust/llm_module_agent_executor.py +288 -0
  58. jarvis/jarvis_c2rust/llm_module_agent_loader.py +170 -0
  59. jarvis/jarvis_c2rust/llm_module_agent_prompts.py +268 -0
  60. jarvis/jarvis_c2rust/llm_module_agent_types.py +57 -0
  61. jarvis/jarvis_c2rust/llm_module_agent_utils.py +150 -0
  62. jarvis/jarvis_c2rust/llm_module_agent_validator.py +119 -0
  63. jarvis/jarvis_c2rust/loaders.py +28 -10
  64. jarvis/jarvis_c2rust/models.py +5 -2
  65. jarvis/jarvis_c2rust/optimizer.py +192 -1974
  66. jarvis/jarvis_c2rust/optimizer_build_fix.py +286 -0
  67. jarvis/jarvis_c2rust/optimizer_clippy.py +766 -0
  68. jarvis/jarvis_c2rust/optimizer_config.py +49 -0
  69. jarvis/jarvis_c2rust/optimizer_docs.py +183 -0
  70. jarvis/jarvis_c2rust/optimizer_options.py +48 -0
  71. jarvis/jarvis_c2rust/optimizer_progress.py +469 -0
  72. jarvis/jarvis_c2rust/optimizer_report.py +52 -0
  73. jarvis/jarvis_c2rust/optimizer_unsafe.py +309 -0
  74. jarvis/jarvis_c2rust/optimizer_utils.py +469 -0
  75. jarvis/jarvis_c2rust/optimizer_visibility.py +185 -0
  76. jarvis/jarvis_c2rust/scanner.py +229 -166
  77. jarvis/jarvis_c2rust/transpiler.py +531 -2732
  78. jarvis/jarvis_c2rust/transpiler_agents.py +503 -0
  79. jarvis/jarvis_c2rust/transpiler_build.py +1294 -0
  80. jarvis/jarvis_c2rust/transpiler_codegen.py +204 -0
  81. jarvis/jarvis_c2rust/transpiler_compile.py +146 -0
  82. jarvis/jarvis_c2rust/transpiler_config.py +178 -0
  83. jarvis/jarvis_c2rust/transpiler_context.py +122 -0
  84. jarvis/jarvis_c2rust/transpiler_executor.py +516 -0
  85. jarvis/jarvis_c2rust/transpiler_generation.py +278 -0
  86. jarvis/jarvis_c2rust/transpiler_git.py +163 -0
  87. jarvis/jarvis_c2rust/transpiler_mod_utils.py +225 -0
  88. jarvis/jarvis_c2rust/transpiler_modules.py +336 -0
  89. jarvis/jarvis_c2rust/transpiler_planning.py +394 -0
  90. jarvis/jarvis_c2rust/transpiler_review.py +1196 -0
  91. jarvis/jarvis_c2rust/transpiler_symbols.py +176 -0
  92. jarvis/jarvis_c2rust/utils.py +269 -79
  93. jarvis/jarvis_code_agent/after_change.py +233 -0
  94. jarvis/jarvis_code_agent/build_validation_config.py +37 -30
  95. jarvis/jarvis_code_agent/builtin_rules.py +68 -0
  96. jarvis/jarvis_code_agent/code_agent.py +976 -1517
  97. jarvis/jarvis_code_agent/code_agent_build.py +227 -0
  98. jarvis/jarvis_code_agent/code_agent_diff.py +246 -0
  99. jarvis/jarvis_code_agent/code_agent_git.py +525 -0
  100. jarvis/jarvis_code_agent/code_agent_impact.py +177 -0
  101. jarvis/jarvis_code_agent/code_agent_lint.py +283 -0
  102. jarvis/jarvis_code_agent/code_agent_llm.py +159 -0
  103. jarvis/jarvis_code_agent/code_agent_postprocess.py +105 -0
  104. jarvis/jarvis_code_agent/code_agent_prompts.py +46 -0
  105. jarvis/jarvis_code_agent/code_agent_rules.py +305 -0
  106. jarvis/jarvis_code_agent/code_analyzer/__init__.py +52 -48
  107. jarvis/jarvis_code_agent/code_analyzer/base_language.py +12 -10
  108. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +12 -11
  109. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +16 -12
  110. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +26 -17
  111. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +558 -104
  112. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +27 -16
  113. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +22 -18
  114. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +21 -16
  115. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +20 -16
  116. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +27 -16
  117. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +47 -23
  118. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +71 -37
  119. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +162 -35
  120. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +111 -57
  121. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +18 -12
  122. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +185 -183
  123. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +2 -1
  124. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +24 -15
  125. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +227 -141
  126. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +321 -247
  127. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +37 -29
  128. jarvis/jarvis_code_agent/code_analyzer/language_support.py +21 -13
  129. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +15 -9
  130. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +75 -45
  131. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +87 -52
  132. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +84 -51
  133. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +94 -64
  134. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +109 -71
  135. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +97 -63
  136. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +103 -69
  137. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +271 -268
  138. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +76 -64
  139. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +92 -19
  140. jarvis/jarvis_code_agent/diff_visualizer.py +998 -0
  141. jarvis/jarvis_code_agent/lint.py +223 -524
  142. jarvis/jarvis_code_agent/rule_share_manager.py +158 -0
  143. jarvis/jarvis_code_agent/rules/clean_code.md +144 -0
  144. jarvis/jarvis_code_agent/rules/code_review.md +115 -0
  145. jarvis/jarvis_code_agent/rules/documentation.md +165 -0
  146. jarvis/jarvis_code_agent/rules/generate_rules.md +52 -0
  147. jarvis/jarvis_code_agent/rules/performance.md +158 -0
  148. jarvis/jarvis_code_agent/rules/refactoring.md +139 -0
  149. jarvis/jarvis_code_agent/rules/security.md +160 -0
  150. jarvis/jarvis_code_agent/rules/tdd.md +78 -0
  151. jarvis/jarvis_code_agent/test_rules/cpp_test.md +118 -0
  152. jarvis/jarvis_code_agent/test_rules/go_test.md +98 -0
  153. jarvis/jarvis_code_agent/test_rules/java_test.md +99 -0
  154. jarvis/jarvis_code_agent/test_rules/javascript_test.md +113 -0
  155. jarvis/jarvis_code_agent/test_rules/php_test.md +117 -0
  156. jarvis/jarvis_code_agent/test_rules/python_test.md +91 -0
  157. jarvis/jarvis_code_agent/test_rules/ruby_test.md +102 -0
  158. jarvis/jarvis_code_agent/test_rules/rust_test.md +86 -0
  159. jarvis/jarvis_code_agent/utils.py +36 -26
  160. jarvis/jarvis_code_analysis/checklists/loader.py +21 -21
  161. jarvis/jarvis_code_analysis/code_review.py +64 -33
  162. jarvis/jarvis_data/config_schema.json +285 -192
  163. jarvis/jarvis_git_squash/main.py +8 -6
  164. jarvis/jarvis_git_utils/git_commiter.py +53 -76
  165. jarvis/jarvis_mcp/__init__.py +5 -2
  166. jarvis/jarvis_mcp/sse_mcp_client.py +40 -30
  167. jarvis/jarvis_mcp/stdio_mcp_client.py +27 -19
  168. jarvis/jarvis_mcp/streamable_mcp_client.py +35 -26
  169. jarvis/jarvis_memory_organizer/memory_organizer.py +78 -55
  170. jarvis/jarvis_methodology/main.py +48 -39
  171. jarvis/jarvis_multi_agent/__init__.py +56 -23
  172. jarvis/jarvis_multi_agent/main.py +15 -18
  173. jarvis/jarvis_platform/base.py +179 -111
  174. jarvis/jarvis_platform/human.py +27 -16
  175. jarvis/jarvis_platform/kimi.py +52 -45
  176. jarvis/jarvis_platform/openai.py +101 -40
  177. jarvis/jarvis_platform/registry.py +51 -33
  178. jarvis/jarvis_platform/tongyi.py +68 -38
  179. jarvis/jarvis_platform/yuanbao.py +59 -43
  180. jarvis/jarvis_platform_manager/main.py +68 -76
  181. jarvis/jarvis_platform_manager/service.py +24 -14
  182. jarvis/jarvis_rag/README_CONFIG.md +314 -0
  183. jarvis/jarvis_rag/README_DYNAMIC_LOADING.md +311 -0
  184. jarvis/jarvis_rag/README_ONLINE_MODELS.md +230 -0
  185. jarvis/jarvis_rag/__init__.py +57 -4
  186. jarvis/jarvis_rag/cache.py +3 -1
  187. jarvis/jarvis_rag/cli.py +48 -68
  188. jarvis/jarvis_rag/embedding_interface.py +39 -0
  189. jarvis/jarvis_rag/embedding_manager.py +7 -230
  190. jarvis/jarvis_rag/embeddings/__init__.py +41 -0
  191. jarvis/jarvis_rag/embeddings/base.py +114 -0
  192. jarvis/jarvis_rag/embeddings/cohere.py +66 -0
  193. jarvis/jarvis_rag/embeddings/edgefn.py +117 -0
  194. jarvis/jarvis_rag/embeddings/local.py +260 -0
  195. jarvis/jarvis_rag/embeddings/openai.py +62 -0
  196. jarvis/jarvis_rag/embeddings/registry.py +293 -0
  197. jarvis/jarvis_rag/llm_interface.py +8 -6
  198. jarvis/jarvis_rag/query_rewriter.py +8 -9
  199. jarvis/jarvis_rag/rag_pipeline.py +61 -52
  200. jarvis/jarvis_rag/reranker.py +7 -75
  201. jarvis/jarvis_rag/reranker_interface.py +32 -0
  202. jarvis/jarvis_rag/rerankers/__init__.py +41 -0
  203. jarvis/jarvis_rag/rerankers/base.py +109 -0
  204. jarvis/jarvis_rag/rerankers/cohere.py +67 -0
  205. jarvis/jarvis_rag/rerankers/edgefn.py +140 -0
  206. jarvis/jarvis_rag/rerankers/jina.py +79 -0
  207. jarvis/jarvis_rag/rerankers/local.py +89 -0
  208. jarvis/jarvis_rag/rerankers/registry.py +293 -0
  209. jarvis/jarvis_rag/retriever.py +58 -43
  210. jarvis/jarvis_sec/__init__.py +66 -141
  211. jarvis/jarvis_sec/agents.py +21 -17
  212. jarvis/jarvis_sec/analysis.py +80 -33
  213. jarvis/jarvis_sec/checkers/__init__.py +7 -13
  214. jarvis/jarvis_sec/checkers/c_checker.py +356 -164
  215. jarvis/jarvis_sec/checkers/rust_checker.py +47 -29
  216. jarvis/jarvis_sec/cli.py +43 -21
  217. jarvis/jarvis_sec/clustering.py +430 -272
  218. jarvis/jarvis_sec/file_manager.py +99 -55
  219. jarvis/jarvis_sec/parsers.py +9 -6
  220. jarvis/jarvis_sec/prompts.py +4 -3
  221. jarvis/jarvis_sec/report.py +44 -22
  222. jarvis/jarvis_sec/review.py +180 -107
  223. jarvis/jarvis_sec/status.py +50 -41
  224. jarvis/jarvis_sec/types.py +3 -0
  225. jarvis/jarvis_sec/utils.py +160 -83
  226. jarvis/jarvis_sec/verification.py +411 -181
  227. jarvis/jarvis_sec/workflow.py +132 -21
  228. jarvis/jarvis_smart_shell/main.py +28 -41
  229. jarvis/jarvis_stats/cli.py +14 -12
  230. jarvis/jarvis_stats/stats.py +28 -19
  231. jarvis/jarvis_stats/storage.py +14 -8
  232. jarvis/jarvis_stats/visualizer.py +12 -7
  233. jarvis/jarvis_tools/base.py +5 -2
  234. jarvis/jarvis_tools/clear_memory.py +13 -9
  235. jarvis/jarvis_tools/cli/main.py +23 -18
  236. jarvis/jarvis_tools/edit_file.py +572 -873
  237. jarvis/jarvis_tools/execute_script.py +10 -7
  238. jarvis/jarvis_tools/file_analyzer.py +7 -8
  239. jarvis/jarvis_tools/meta_agent.py +287 -0
  240. jarvis/jarvis_tools/methodology.py +5 -3
  241. jarvis/jarvis_tools/read_code.py +305 -1438
  242. jarvis/jarvis_tools/read_symbols.py +50 -17
  243. jarvis/jarvis_tools/read_webpage.py +19 -18
  244. jarvis/jarvis_tools/registry.py +435 -156
  245. jarvis/jarvis_tools/retrieve_memory.py +16 -11
  246. jarvis/jarvis_tools/save_memory.py +8 -6
  247. jarvis/jarvis_tools/search_web.py +31 -31
  248. jarvis/jarvis_tools/sub_agent.py +32 -28
  249. jarvis/jarvis_tools/sub_code_agent.py +44 -60
  250. jarvis/jarvis_tools/task_list_manager.py +1811 -0
  251. jarvis/jarvis_tools/virtual_tty.py +29 -19
  252. jarvis/jarvis_utils/__init__.py +4 -0
  253. jarvis/jarvis_utils/builtin_replace_map.py +2 -1
  254. jarvis/jarvis_utils/clipboard.py +9 -8
  255. jarvis/jarvis_utils/collections.py +331 -0
  256. jarvis/jarvis_utils/config.py +699 -194
  257. jarvis/jarvis_utils/dialogue_recorder.py +294 -0
  258. jarvis/jarvis_utils/embedding.py +6 -3
  259. jarvis/jarvis_utils/file_processors.py +7 -1
  260. jarvis/jarvis_utils/fzf.py +9 -3
  261. jarvis/jarvis_utils/git_utils.py +71 -42
  262. jarvis/jarvis_utils/globals.py +116 -32
  263. jarvis/jarvis_utils/http.py +6 -2
  264. jarvis/jarvis_utils/input.py +318 -83
  265. jarvis/jarvis_utils/jsonnet_compat.py +119 -104
  266. jarvis/jarvis_utils/methodology.py +37 -28
  267. jarvis/jarvis_utils/output.py +201 -44
  268. jarvis/jarvis_utils/utils.py +986 -628
  269. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/METADATA +49 -33
  270. jarvis_ai_assistant-1.0.2.dist-info/RECORD +304 -0
  271. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +0 -556
  272. jarvis/jarvis_tools/generate_new_tool.py +0 -205
  273. jarvis/jarvis_tools/lsp_client.py +0 -1552
  274. jarvis/jarvis_tools/rewrite_file.py +0 -105
  275. jarvis_ai_assistant-0.7.16.dist-info/RECORD +0 -218
  276. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/WHEEL +0 -0
  277. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/entry_points.txt +0 -0
  278. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/licenses/LICENSE +0 -0
  279. {jarvis_ai_assistant-0.7.16.dist-info → jarvis_ai_assistant-1.0.2.dist-info}/top_level.txt +0 -0
@@ -2,18 +2,28 @@
2
2
  """
3
3
  C2Rust 转译器工具函数
4
4
  """
5
+
5
6
  from __future__ import annotations
6
7
 
7
8
  import json
8
9
  import re
10
+ import subprocess
9
11
  from pathlib import Path
10
- from typing import Any, Callable, Dict, List, Optional, Tuple
12
+ from typing import Any
13
+ from typing import Callable
14
+ from typing import Dict
15
+ from typing import List
16
+ from typing import Optional
17
+ from typing import Tuple
11
18
 
12
- import typer
19
+ from jarvis.jarvis_utils.output import PrettyOutput
13
20
 
14
- from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME, ORDER_JSONL
21
+ from jarvis.jarvis_c2rust.constants import C2RUST_DIRNAME
22
+ from jarvis.jarvis_c2rust.constants import ORDER_JSONL
15
23
  from jarvis.jarvis_c2rust.scanner import compute_translation_order_jsonl
16
- from jarvis.jarvis_utils.git_utils import get_diff, get_diff_file_list
24
+ from jarvis.jarvis_utils.config import get_max_input_token_count
25
+ from jarvis.jarvis_utils.git_utils import get_diff
26
+ from jarvis.jarvis_utils.git_utils import get_diff_file_list
17
27
  from jarvis.jarvis_utils.jsonnet_compat import loads as json5_loads
18
28
 
19
29
 
@@ -21,7 +31,7 @@ def ensure_order_file(project_root: Path) -> Path:
21
31
  """确保 translation_order.jsonl 存在且包含有效步骤;仅基于 symbols.jsonl 生成,不使用任何回退。"""
22
32
  data_dir = project_root / C2RUST_DIRNAME
23
33
  order_path = data_dir / ORDER_JSONL
24
- typer.secho(f"[c2rust-transpiler][order] 目标顺序文件: {order_path}", fg=typer.colors.BLUE)
34
+ PrettyOutput.auto_print(f"📋 [c2rust-transpiler][order] 目标顺序文件: {order_path}")
25
35
 
26
36
  def _has_steps(p: Path) -> bool:
27
37
  try:
@@ -31,13 +41,19 @@ def ensure_order_file(project_root: Path) -> Path:
31
41
  return False
32
42
 
33
43
  # 已存在则校验是否有步骤
34
- typer.secho(f"[c2rust-transpiler][order] 检查现有顺序文件有效性: {order_path}", fg=typer.colors.BLUE)
44
+ PrettyOutput.auto_print(
45
+ f"🔍 [c2rust-transpiler][order] 检查现有顺序文件有效性: {order_path}"
46
+ )
35
47
  if order_path.exists():
36
48
  if _has_steps(order_path):
37
- typer.secho(f"[c2rust-transpiler][order] 现有顺序文件有效,将使用 {order_path}", fg=typer.colors.GREEN)
49
+ PrettyOutput.auto_print(
50
+ f"✅ [c2rust-transpiler][order] 现有顺序文件有效,将使用 {order_path}"
51
+ )
38
52
  return order_path
39
53
  # 为空或不可读:基于标准路径重新计算(仅 symbols.jsonl)
40
- typer.secho("[c2rust-transpiler][order] 现有顺序文件为空/无效,正基于 symbols.jsonl 重新计算", fg=typer.colors.YELLOW)
54
+ PrettyOutput.auto_print(
55
+ "⚠️ [c2rust-transpiler][order] 现有顺序文件为空/无效,正基于 symbols.jsonl 重新计算"
56
+ )
41
57
  try:
42
58
  compute_translation_order_jsonl(data_dir, out_path=order_path)
43
59
  except Exception as e:
@@ -50,13 +66,17 @@ def ensure_order_file(project_root: Path) -> Path:
50
66
  except Exception as e:
51
67
  raise RuntimeError(f"计算翻译顺序失败: {e}")
52
68
 
53
- typer.secho(f"[c2rust-transpiler][order] 已生成顺序文件: {order_path} (exists={order_path.exists()})", fg=typer.colors.BLUE)
69
+ PrettyOutput.auto_print(
70
+ f"📋 [c2rust-transpiler][order] 已生成顺序文件: {order_path} (exists={order_path.exists()})"
71
+ )
54
72
  if not order_path.exists():
55
73
  raise FileNotFoundError(f"计算后未找到 translation_order.jsonl: {order_path}")
56
74
 
57
75
  # 最终校验:若仍无有效步骤,直接报错并提示先执行 scan 或检查 symbols.jsonl
58
76
  if not _has_steps(order_path):
59
- raise RuntimeError("translation_order.jsonl 无有效步骤。请先执行 'jarvis-c2rust scan' 生成 symbols.jsonl 并重试。")
77
+ raise RuntimeError(
78
+ "translation_order.jsonl 无有效步骤。请先执行 'jarvis-c2rust scan' 生成 symbols.jsonl 并重试。"
79
+ )
60
80
 
61
81
  return order_path
62
82
 
@@ -84,7 +104,11 @@ def iter_order_steps(order_jsonl: Path) -> List[List[int]]:
84
104
  if isinstance(ids, list) and ids:
85
105
  # 新格式:仅支持 ids
86
106
  try:
87
- ids_int = [int(x) for x in ids if isinstance(x, (int, str)) and str(x).strip()]
107
+ ids_int = [
108
+ int(x)
109
+ for x in ids
110
+ if isinstance(x, (int, str)) and str(x).strip()
111
+ ]
88
112
  except Exception:
89
113
  ids_int = []
90
114
  if ids_int:
@@ -138,13 +162,17 @@ def write_json(path: Path, obj: Any) -> None:
138
162
  path.parent.mkdir(parents=True, exist_ok=True)
139
163
  # 使用临时文件确保原子性
140
164
  temp_path = path.with_suffix(path.suffix + ".tmp")
141
- temp_path.write_text(json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8")
165
+ temp_path.write_text(
166
+ json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8"
167
+ )
142
168
  # 原子性重命名
143
169
  temp_path.replace(path)
144
170
  except Exception:
145
171
  # 如果原子写入失败,回退到直接写入
146
172
  try:
147
- path.write_text(json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8")
173
+ path.write_text(
174
+ json.dumps(obj, ensure_ascii=False, indent=2), encoding="utf-8"
175
+ )
148
176
  except Exception:
149
177
  pass
150
178
 
@@ -185,10 +213,10 @@ def extract_json_from_summary(text: str) -> Tuple[Dict[str, Any], Optional[str]]
185
213
  def detect_test_deletion(log_prefix: str = "[c2rust]") -> Optional[Dict[str, Any]]:
186
214
  """
187
215
  检测是否错误删除了 #[test] 或 #[cfg(test)]。
188
-
216
+
189
217
  参数:
190
218
  log_prefix: 日志前缀(如 "[c2rust-transpiler]" 或 "[c2rust-optimizer]")
191
-
219
+
192
220
  返回:
193
221
  如果检测到删除,返回包含 'diff', 'files', 'deleted_tests' 的字典;否则返回 None
194
222
  """
@@ -196,90 +224,98 @@ def detect_test_deletion(log_prefix: str = "[c2rust]") -> Optional[Dict[str, Any
196
224
  diff = get_diff()
197
225
  if not diff:
198
226
  return None
199
-
227
+
200
228
  # 检查 diff 中是否包含删除的 #[test] 或 #[cfg(test)]
201
229
  test_patterns = [
202
- r'^-\s*#\[test\]',
203
- r'^-\s*#\[cfg\(test\)\]',
204
- r'^-\s*#\[cfg\(test\)',
230
+ r"^-\s*#\[test\]",
231
+ r"^-\s*#\[cfg\(test\)\]",
232
+ r"^-\s*#\[cfg\(test\)",
205
233
  ]
206
-
234
+
207
235
  deleted_tests = []
208
- lines = diff.split('\n')
236
+ lines = diff.split("\n")
209
237
  current_file = None
210
-
238
+
211
239
  for i, line in enumerate(lines):
212
240
  # 检测文件路径
213
- if line.startswith('diff --git') or line.startswith('---') or line.startswith('+++'):
241
+ if (
242
+ line.startswith("diff --git")
243
+ or line.startswith("---")
244
+ or line.startswith("+++")
245
+ ):
214
246
  # 尝试从 diff 行中提取文件名
215
- if line.startswith('---'):
247
+ if line.startswith("---"):
216
248
  parts = line.split()
217
249
  if len(parts) > 1:
218
- current_file = parts[1].lstrip('a/').lstrip('b/')
219
- elif line.startswith('+++'):
250
+ current_file = parts[1].lstrip("a/").lstrip("b/")
251
+ elif line.startswith("+++"):
220
252
  parts = line.split()
221
253
  if len(parts) > 1:
222
- current_file = parts[1].lstrip('a/').lstrip('b/')
254
+ current_file = parts[1].lstrip("a/").lstrip("b/")
223
255
  continue
224
-
256
+
225
257
  # 检查是否匹配删除的测试标记
226
258
  for pattern in test_patterns:
227
259
  if re.search(pattern, line, re.IGNORECASE):
228
260
  # 检查上下文,确认是删除而不是修改
229
- if i > 0 and lines[i-1].startswith('-'):
261
+ if i > 0 and lines[i - 1].startswith("-"):
230
262
  # 可能是删除的一部分
231
- deleted_tests.append({
232
- 'file': current_file or 'unknown',
233
- 'line': line,
234
- 'line_number': i + 1,
235
- })
236
- elif not (i < len(lines) - 1 and lines[i+1].startswith('+')):
263
+ deleted_tests.append(
264
+ {
265
+ "file": current_file or "unknown",
266
+ "line": line,
267
+ "line_number": i + 1,
268
+ }
269
+ )
270
+ elif not (i < len(lines) - 1 and lines[i + 1].startswith("+")):
237
271
  # 下一行不是添加,说明是删除
238
- deleted_tests.append({
239
- 'file': current_file or 'unknown',
240
- 'line': line,
241
- 'line_number': i + 1,
242
- })
272
+ deleted_tests.append(
273
+ {
274
+ "file": current_file or "unknown",
275
+ "line": line,
276
+ "line_number": i + 1,
277
+ }
278
+ )
243
279
  break
244
-
280
+
245
281
  if deleted_tests:
246
282
  modified_files = get_diff_file_list()
247
283
  return {
248
- 'diff': diff,
249
- 'files': modified_files,
250
- 'deleted_tests': deleted_tests,
284
+ "diff": diff,
285
+ "files": modified_files,
286
+ "deleted_tests": deleted_tests,
251
287
  }
252
288
  return None
253
289
  except Exception as e:
254
- typer.secho(f"{log_prefix}[test-detection] 检测测试删除时发生异常: {e}", fg=typer.colors.YELLOW)
290
+ PrettyOutput.auto_print(
291
+ f"⚠️ {log_prefix}[test-detection] 检测测试删除时发生异常: {e}"
292
+ )
255
293
  return None
256
294
 
257
295
 
258
296
  def ask_llm_about_test_deletion(
259
- detection_result: Dict[str, Any],
260
- agent: Any,
261
- log_prefix: str = "[c2rust]"
297
+ detection_result: Dict[str, Any], agent: Any, log_prefix: str = "[c2rust]"
262
298
  ) -> bool:
263
299
  """
264
300
  询问 LLM 是否错误删除了测试代码。
265
-
301
+
266
302
  参数:
267
303
  detection_result: 检测结果字典,包含 'diff', 'files', 'deleted_tests'
268
304
  agent: 代码生成或修复的 agent 实例,使用其 model 进行询问
269
305
  log_prefix: 日志前缀(如 "[c2rust-transpiler]" 或 "[c2rust-optimizer]")
270
-
306
+
271
307
  返回:
272
308
  bool: 如果 LLM 认为删除不合理返回 True(需要回退),否则返回 False
273
309
  """
274
- if not agent or not hasattr(agent, 'model'):
310
+ if not agent or not hasattr(agent, "model"):
275
311
  # 如果没有 agent 或 agent 没有 model,默认认为有问题(保守策略)
276
312
  return True
277
-
313
+
278
314
  try:
279
- deleted_tests = detection_result.get('deleted_tests', [])
280
- diff = detection_result.get('diff', '')
281
- files = detection_result.get('files', [])
282
-
315
+ deleted_tests = detection_result.get("deleted_tests", [])
316
+ diff = detection_result.get("diff", "")
317
+ files = detection_result.get("files", [])
318
+
283
319
  # 构建预览(限制长度)
284
320
  preview_lines = []
285
321
  preview_lines.append("检测到可能错误删除了测试代码标记:")
@@ -289,17 +325,17 @@ def ask_llm_about_test_deletion(
289
325
  preview_lines.append(f" 行: {item.get('line', '')}")
290
326
  if len(deleted_tests) > 10:
291
327
  preview_lines.append(f"... 还有 {len(deleted_tests) - 10} 个删除的测试标记")
292
-
328
+
293
329
  # 限制 diff 长度
294
330
  diff_preview = diff[:5000] if len(diff) > 5000 else diff
295
331
  if len(diff) > 5000:
296
332
  diff_preview += "\n... (diff 内容过长,已截断)"
297
-
333
+
298
334
  prompt = f"""检测到代码变更中可能错误删除了测试代码标记(#[test] 或 #[cfg(test)]),请判断是否合理:
299
335
 
300
336
  删除的测试标记统计:
301
337
  - 删除的测试标记数量: {len(deleted_tests)}
302
- - 涉及的文件: {', '.join(files[:5])}{' ...' if len(files) > 5 else ''}
338
+ - 涉及的文件: {", ".join(files[:5])}{" ..." if len(files) > 5 else ""}
303
339
 
304
340
  删除的测试标记详情:
305
341
  {chr(10).join(preview_lines)}
@@ -318,25 +354,29 @@ def ask_llm_about_test_deletion(
318
354
 
319
355
  请严格按照协议格式回答,不要添加其他内容。
320
356
  """
321
-
322
- typer.secho(f"{log_prefix}[test-detection] 正在询问 LLM 判断测试代码删除是否合理...", fg=typer.colors.YELLOW)
323
- response = agent.model.chat_until_success(prompt) # type: ignore
357
+
358
+ PrettyOutput.auto_print(
359
+ f"🤔 {log_prefix}[test-detection] 正在询问 LLM 判断测试代码删除是否合理..."
360
+ )
361
+ response = agent.model.chat_until_success(prompt)
324
362
  response_str = str(response or "")
325
-
363
+
326
364
  # 使用确定的协议标记解析回答
327
365
  if "<!!!NO!!!>" in response_str:
328
- typer.secho("⚠️ LLM 确认:测试代码删除不合理,需要回退", fg=typer.colors.RED)
366
+ PrettyOutput.auto_print(" LLM 确认:测试代码删除不合理,需要回退")
329
367
  return True # 需要回退
330
368
  elif "<!!!YES!!!>" in response_str:
331
- typer.secho("✅ LLM 确认:测试代码删除合理", fg=typer.colors.GREEN)
369
+ PrettyOutput.auto_print("✅ LLM 确认:测试代码删除合理")
332
370
  return False # 不需要回退
333
371
  else:
334
372
  # 如果无法找到协议标记,默认认为有问题(保守策略)
335
- typer.secho(f"⚠️ 无法找到协议标记,默认认为有问题。回答内容: {response_str[:200]}", fg=typer.colors.YELLOW)
373
+ PrettyOutput.auto_print(
374
+ f"⚠️ 无法找到协议标记,默认认为有问题。回答内容: {response_str[:200]}"
375
+ )
336
376
  return True # 保守策略:默认回退
337
377
  except Exception as e:
338
378
  # 如果询问失败,默认认为有问题(保守策略)
339
- typer.secho(f"⚠️ 询问 LLM 失败: {str(e)},默认认为有问题", fg=typer.colors.YELLOW)
379
+ PrettyOutput.auto_print(f"⚠️ 询问 LLM 失败: {str(e)},默认认为有问题")
340
380
  return True # 保守策略:默认回退
341
381
 
342
382
 
@@ -344,42 +384,192 @@ def check_and_handle_test_deletion(
344
384
  before_commit: Optional[str],
345
385
  agent: Any,
346
386
  reset_to_commit_fn: Callable[[str], bool],
347
- log_prefix: str = "[c2rust]"
387
+ log_prefix: str = "[c2rust]",
348
388
  ) -> bool:
349
389
  """
350
390
  检测并处理测试代码删除。
351
-
391
+
352
392
  参数:
353
393
  before_commit: agent 运行前的 commit hash
354
394
  agent: 代码生成或修复的 agent 实例,使用其 model 进行询问
355
395
  reset_to_commit_fn: 回退到指定 commit 的函数,接受 commit hash 作为参数,返回是否成功
356
396
  log_prefix: 日志前缀(如 "[c2rust-transpiler]" 或 "[c2rust-optimizer]")
357
-
397
+
358
398
  返回:
359
399
  bool: 如果检测到问题且已回退,返回 True;否则返回 False
360
400
  """
361
401
  if not before_commit:
362
402
  # 没有记录 commit,无法回退
363
403
  return False
364
-
404
+
365
405
  detection_result = detect_test_deletion(log_prefix)
366
406
  if not detection_result:
367
407
  # 没有检测到删除
368
408
  return False
369
-
370
- typer.secho(f"{log_prefix}[test-detection] 检测到可能错误删除了测试代码标记", fg=typer.colors.YELLOW)
371
-
409
+
410
+ PrettyOutput.auto_print(
411
+ f"⚠️ {log_prefix}[test-detection] 检测到可能错误删除了测试代码标记"
412
+ )
413
+
372
414
  # 询问 LLM(使用传入的 agent 的 model)
373
415
  need_reset = ask_llm_about_test_deletion(detection_result, agent, log_prefix)
374
-
416
+
375
417
  if need_reset:
376
- typer.secho(f"{log_prefix}[test-detection] LLM 确认删除不合理,正在回退到 commit: {before_commit}", fg=typer.colors.RED)
418
+ PrettyOutput.auto_print(
419
+ f"❌ {log_prefix}[test-detection] LLM 确认删除不合理,正在回退到 commit: {before_commit}"
420
+ )
377
421
  if reset_to_commit_fn(before_commit):
378
- typer.secho(f"{log_prefix}[test-detection] 已回退到之前的 commit", fg=typer.colors.GREEN)
422
+ PrettyOutput.auto_print(
423
+ f"✅ {log_prefix}[test-detection] 已回退到之前的 commit"
424
+ )
379
425
  return True
380
426
  else:
381
- typer.secho(f"{log_prefix}[test-detection] 回退失败", fg=typer.colors.RED)
427
+ PrettyOutput.auto_print(f"{log_prefix}[test-detection] 回退失败")
382
428
  return False
383
-
429
+
384
430
  return False
385
431
 
432
+
433
+ def extract_files_from_git_diff(git_diff: str) -> List[str]:
434
+ """
435
+ 从 git diff 字符串中提取所有修改的文件列表。
436
+
437
+ 参数:
438
+ git_diff: git diff 内容
439
+
440
+ 返回:
441
+ List[str]: 修改的文件路径列表(去重)
442
+ """
443
+ if not git_diff or not git_diff.strip():
444
+ return []
445
+
446
+ files = set()
447
+ # 匹配 "diff --git a/path b/path" 格式
448
+ # git diff 标准格式:diff --git a/path b/path
449
+ pattern = r"^diff --git a/([^\s]+) b/([^\s]+)$"
450
+ for line in git_diff.split("\n"):
451
+ match = re.match(pattern, line)
452
+ if match:
453
+ # 通常 a/path 和 b/path 相同,但处理重命名时可能不同
454
+ file_a = match.group(1)
455
+ file_b = match.group(2)
456
+ # 优先使用 b 路径(新路径),如果不同则都添加
457
+ files.add(file_b)
458
+ if file_a != file_b:
459
+ files.add(file_a)
460
+
461
+ return sorted(list(files))
462
+
463
+
464
+ def get_modified_files_from_git(
465
+ base_commit: Optional[str], crate_dir: Optional[Path]
466
+ ) -> List[str]:
467
+ """
468
+ 使用 git 命令获取修改的文件列表。
469
+
470
+ 参数:
471
+ base_commit: 基准 commit(如果为 None,则与 HEAD 比较)
472
+ crate_dir: crate 根目录(如果为 None,则使用当前目录)
473
+
474
+ 返回:
475
+ List[str]: 修改的文件路径列表
476
+ """
477
+ if not crate_dir:
478
+ return []
479
+
480
+ try:
481
+ # 构建 git diff 命令
482
+ if base_commit:
483
+ cmd = ["git", "diff", "--name-only", base_commit]
484
+ else:
485
+ cmd = ["git", "diff", "--name-only", "HEAD"]
486
+
487
+ result = subprocess.run(
488
+ cmd,
489
+ cwd=crate_dir,
490
+ capture_output=True,
491
+ text=True,
492
+ check=False,
493
+ )
494
+
495
+ if result.returncode == 0:
496
+ files = [f.strip() for f in result.stdout.splitlines() if f.strip()]
497
+ return sorted(files)
498
+ else:
499
+ # 如果命令失败,返回空列表
500
+ return []
501
+ except Exception:
502
+ # 如果出现任何异常,返回空列表
503
+ return []
504
+
505
+
506
+ def truncate_git_diff_with_context_limit(
507
+ git_diff: str,
508
+ agent: Optional[Any] = None,
509
+ llm_group: Optional[str] = None,
510
+ token_ratio: float = 0.3,
511
+ base_commit: Optional[str] = None,
512
+ crate_dir: Optional[Path] = None,
513
+ ) -> str:
514
+ """
515
+ 限制 git diff 的长度,避免上下文过大。
516
+
517
+ 参数:
518
+ git_diff: 原始的 git diff 内容
519
+ agent: 可选的 agent 实例,用于获取剩余 token 数量(更准确,考虑对话历史)
520
+ llm_group: 可选的 LLM 组名称,用于获取输入窗口限制(当 agent 不可用时使用)
521
+ token_ratio: token 使用比例(默认 0.3,即 30%)
522
+ base_commit: 可选的基准 commit,如果提供则使用 git 命令获取文件列表
523
+ crate_dir: 可选的 crate 根目录,如果提供则使用 git 命令获取文件列表
524
+
525
+ 返回:
526
+ str: 限制长度后的 git diff(如果超出限制则截断并添加提示和文件列表)
527
+ """
528
+ if not git_diff or not git_diff.strip():
529
+ return git_diff
530
+
531
+ max_diff_chars = None
532
+
533
+ # 优先尝试使用 agent 获取剩余 token(更准确,包含对话历史)
534
+ if agent:
535
+ try:
536
+ remaining_tokens = agent.get_remaining_token_count()
537
+ if remaining_tokens > 0:
538
+ # 使用剩余 token 的指定比例作为字符限制(1 token ≈ 4字符)
539
+ max_diff_chars = int(remaining_tokens * token_ratio * 4)
540
+ if max_diff_chars <= 0:
541
+ max_diff_chars = None
542
+ except Exception:
543
+ pass
544
+
545
+ # 回退方案:使用输入窗口的指定比例转换为字符数
546
+ if max_diff_chars is None:
547
+ try:
548
+ max_input_tokens = get_max_input_token_count(llm_group)
549
+ max_diff_chars = int(max_input_tokens * token_ratio * 4)
550
+ except Exception:
551
+ # 如果获取失败,使用默认值(约 10000 字符)
552
+ max_diff_chars = 10000
553
+
554
+ # 应用长度限制
555
+ if len(git_diff) > max_diff_chars:
556
+ # 优先使用 git 命令获取文件列表(更可靠)
557
+ if base_commit is not None and crate_dir:
558
+ modified_files = get_modified_files_from_git(base_commit, crate_dir)
559
+ else:
560
+ # 回退到从 diff 内容中提取
561
+ modified_files = extract_files_from_git_diff(git_diff)
562
+
563
+ truncated_diff = git_diff[:max_diff_chars] + "\n... (差异内容过长,已截断)"
564
+
565
+ # 如果有修改的文件,添加文件列表
566
+ if modified_files:
567
+ truncated_diff += "\n\n**修改的文件列表(共 {} 个文件):**\n".format(
568
+ len(modified_files)
569
+ )
570
+ for file_path in modified_files:
571
+ truncated_diff += f" - {file_path}\n"
572
+
573
+ return truncated_diff
574
+
575
+ return git_diff