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
@@ -1,10 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """工具函数模块"""
3
3
 
4
- from typing import Dict, List, Optional
5
- from pathlib import Path
6
4
  import json
7
- import typer
5
+ from pathlib import Path
6
+ from typing import Dict
7
+ from typing import List
8
+ from typing import Optional
9
+
10
+ from jarvis.jarvis_utils.output import PrettyOutput
8
11
 
9
12
  from jarvis.jarvis_sec.workflow import direct_scan
10
13
 
@@ -16,15 +19,26 @@ def git_restore_if_dirty(repo_root: str) -> int:
16
19
  """
17
20
  try:
18
21
  import subprocess as _sub
22
+
19
23
  root = Path(repo_root)
20
24
  if not (root / ".git").exists():
21
25
  return 0
22
- proc = _sub.run(["git", "status", "--porcelain"], cwd=str(root), capture_output=True, text=True)
26
+ proc = _sub.run(
27
+ ["git", "status", "--porcelain"],
28
+ cwd=str(root),
29
+ capture_output=True,
30
+ text=True,
31
+ )
23
32
  if proc.returncode != 0:
24
33
  return 0
25
34
  lines = [line for line in proc.stdout.splitlines() if line.strip()]
26
35
  if lines:
27
- _sub.run(["git", "checkout", "--", "."], cwd=str(root), capture_output=True, text=True)
36
+ _sub.run(
37
+ ["git", "checkout", "--", "."],
38
+ cwd=str(root),
39
+ capture_output=True,
40
+ text=True,
41
+ )
28
42
  return len(lines)
29
43
  except Exception:
30
44
  pass
@@ -47,17 +61,17 @@ def initialize_analysis_context(
47
61
  ) -> tuple:
48
62
  """
49
63
  初始化分析上下文,包括状态管理、进度文件、目录等。
50
-
64
+
51
65
  返回: (sec_dir, progress_path, _progress_append)
52
66
  """
53
67
  # 获取 .jarvis/sec 目录
54
68
  sec_dir = get_sec_dir(entry_path)
55
69
  progress_path = None # 不再使用 progress.jsonl
56
-
70
+
57
71
  # 进度追加函数(空函数,不再记录)
58
72
  def _progress_append(rec: Dict) -> None:
59
73
  pass # 不再记录进度日志
60
-
74
+
61
75
  return sec_dir, progress_path, _progress_append
62
76
 
63
77
 
@@ -71,26 +85,33 @@ def load_or_run_heuristic_scan(
71
85
  ) -> tuple[List[Dict], Dict]:
72
86
  """
73
87
  加载或运行启发式扫描。
74
-
88
+
75
89
  优先从新的 candidates.jsonl 文件加载,如果不存在则回退到旧的 heuristic_issues.jsonl。
76
-
90
+
77
91
  返回: (candidates, summary)
78
92
  """
79
93
  candidates: List[Dict] = []
80
94
  summary: Dict = {}
81
-
95
+
82
96
  # 优先使用新的 candidates.jsonl 文件
83
- from jarvis.jarvis_sec.file_manager import load_candidates, get_candidates_file
97
+ from jarvis.jarvis_sec.file_manager import get_candidates_file
98
+ from jarvis.jarvis_sec.file_manager import load_candidates
99
+
84
100
  candidates = load_candidates(sec_dir)
85
-
101
+
86
102
  if candidates:
87
103
  try:
88
- typer.secho(f"[jarvis-sec] 从 {get_candidates_file(sec_dir)} 恢复启发式扫描", fg=typer.colors.BLUE)
89
- _progress_append({
90
- "event": "pre_scan_resumed",
91
- "path": str(get_candidates_file(sec_dir)),
92
- "issues_found": len(candidates)
93
- })
104
+ PrettyOutput.auto_print(
105
+ f"✨ [jarvis-sec] 从 {get_candidates_file(sec_dir)} 恢复启发式扫描",
106
+ timestamp=True,
107
+ )
108
+ _progress_append(
109
+ {
110
+ "event": "pre_scan_resumed",
111
+ "path": str(get_candidates_file(sec_dir)),
112
+ "issues_found": len(candidates),
113
+ }
114
+ )
94
115
  except Exception:
95
116
  pass
96
117
  else:
@@ -98,22 +119,32 @@ def load_or_run_heuristic_scan(
98
119
  _heuristic_path = sec_dir / "heuristic_issues.jsonl"
99
120
  if _heuristic_path.exists():
100
121
  try:
101
- typer.secho(f"[jarvis-sec] 从 {_heuristic_path} 恢复启发式扫描(旧格式)", fg=typer.colors.BLUE)
122
+ PrettyOutput.auto_print(
123
+ f"✨ [jarvis-sec] 从 {_heuristic_path} 恢复启发式扫描(旧格式)",
124
+ timestamp=True,
125
+ )
102
126
  with _heuristic_path.open("r", encoding="utf-8") as f:
103
127
  for line in f:
104
128
  if line.strip():
105
129
  candidates.append(json.loads(line))
106
- _progress_append({
107
- "event": "pre_scan_resumed",
108
- "path": str(_heuristic_path),
109
- "issues_found": len(candidates)
110
- })
130
+ _progress_append(
131
+ {
132
+ "event": "pre_scan_resumed",
133
+ "path": str(_heuristic_path),
134
+ "issues_found": len(candidates),
135
+ }
136
+ )
111
137
  except Exception as e:
112
- typer.secho(f"[jarvis-sec] 恢复启发式扫描失败,执行完整扫描: {e}", fg=typer.colors.YELLOW)
138
+ PrettyOutput.auto_print(
139
+ f"⚠️ [jarvis-sec] 恢复启发式扫描失败,执行完整扫描: {e}",
140
+ timestamp=True,
141
+ )
113
142
  candidates = [] # 重置以便执行完整扫描
114
-
143
+
115
144
  if not candidates:
116
- _progress_append({"event": "pre_scan_start", "entry_path": entry_path, "languages": langs})
145
+ _progress_append(
146
+ {"event": "pre_scan_start", "entry_path": entry_path, "languages": langs}
147
+ )
117
148
  status_mgr.update_pre_scan(message="开始启发式扫描...")
118
149
  pre_scan = direct_scan(entry_path, languages=langs, exclude_dirs=exclude_dirs)
119
150
  candidates = pre_scan.get("issues", [])
@@ -123,36 +154,43 @@ def load_or_run_heuristic_scan(
123
154
  current_files=scanned_files,
124
155
  total_files=scanned_files,
125
156
  issues_found=len(candidates),
126
- message=f"启发式扫描完成,发现 {len(candidates)} 个候选问题"
157
+ message=f"启发式扫描完成,发现 {len(candidates)} 个候选问题",
158
+ )
159
+ _progress_append(
160
+ {
161
+ "event": "pre_scan_done",
162
+ "entry_path": entry_path,
163
+ "languages": langs,
164
+ "scanned_files": scanned_files,
165
+ "issues_found": len(candidates),
166
+ }
127
167
  )
128
- _progress_append({
129
- "event": "pre_scan_done",
130
- "entry_path": entry_path,
131
- "languages": langs,
132
- "scanned_files": scanned_files,
133
- "issues_found": len(candidates)
134
- })
135
168
  # 持久化
136
169
  try:
137
170
  _heuristic_path.parent.mkdir(parents=True, exist_ok=True)
138
171
  with _heuristic_path.open("w", encoding="utf-8") as f:
139
172
  for item in candidates:
140
173
  f.write(json.dumps(item, ensure_ascii=False) + "\n")
141
- _progress_append({
142
- "event": "heuristic_report_written",
143
- "path": str(_heuristic_path),
144
- "issues_count": len(candidates),
145
- })
146
- typer.secho(f"[jarvis-sec] 已将 {len(candidates)} 个启发式扫描问题写入 {_heuristic_path}", fg=typer.colors.GREEN)
174
+ _progress_append(
175
+ {
176
+ "event": "heuristic_report_written",
177
+ "path": str(_heuristic_path),
178
+ "issues_count": len(candidates),
179
+ }
180
+ )
181
+ PrettyOutput.auto_print(
182
+ f"✅ [jarvis-sec] 已将 {len(candidates)} 个启发式扫描问题写入 {_heuristic_path}",
183
+ timestamp=True,
184
+ )
147
185
  except Exception:
148
186
  pass
149
187
  else:
150
188
  # 从断点恢复启发式扫描结果
151
189
  status_mgr.update_pre_scan(
152
190
  issues_found=len(candidates),
153
- message=f"从断点恢复,已发现 {len(candidates)} 个候选问题"
191
+ message=f"从断点恢复,已发现 {len(candidates)} 个候选问题",
154
192
  )
155
-
193
+
156
194
  return candidates, summary
157
195
 
158
196
 
@@ -182,14 +220,17 @@ def compact_candidate(it: Dict) -> Dict:
182
220
  def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
183
221
  """
184
222
  将候选问题精简为子任务清单,控制上下文长度,并分配全局唯一ID。
185
-
223
+
186
224
  返回: compact_candidates (已分配gid的候选列表)
187
225
  """
188
226
  compact_candidates = [compact_candidate(it) for it in candidates]
189
-
227
+
190
228
  # 检查是否所有候选都已经有gid(从heuristic_issues.jsonl恢复时)
191
- all_have_gid = all("gid" in it and isinstance(it.get("gid"), int) and it.get("gid", 0) >= 1 for it in compact_candidates)
192
-
229
+ all_have_gid = all(
230
+ "gid" in it and isinstance(it.get("gid"), int) and it.get("gid", 0) >= 1
231
+ for it in compact_candidates
232
+ )
233
+
193
234
  if not all_have_gid:
194
235
  # 如果有候选没有gid,需要分配
195
236
  # 优先保留已有的gid,为没有gid的候选分配新的gid
@@ -201,11 +242,15 @@ def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
201
242
  existing_gids.add(gid_val)
202
243
  except Exception:
203
244
  pass
204
-
245
+
205
246
  # 为没有gid的候选分配新的gid
206
247
  next_gid = 1
207
248
  for it in compact_candidates:
208
- if "gid" not in it or not isinstance(it.get("gid"), int) or it.get("gid", 0) < 1:
249
+ if (
250
+ "gid" not in it
251
+ or not isinstance(it.get("gid"), int)
252
+ or it.get("gid", 0) < 1
253
+ ):
209
254
  # 找到一个未使用的gid
210
255
  while next_gid in existing_gids:
211
256
  next_gid += 1
@@ -215,13 +260,14 @@ def prepare_candidates(candidates: List[Dict]) -> List[Dict]:
215
260
  next_gid += 1
216
261
  except Exception:
217
262
  pass
218
-
263
+
219
264
  return compact_candidates
220
265
 
221
266
 
222
267
  def group_candidates_by_file(candidates: List[Dict]) -> Dict[str, List[Dict]]:
223
268
  """按文件分组候选问题"""
224
269
  from collections import defaultdict
270
+
225
271
  groups: Dict[str, List[Dict]] = defaultdict(list)
226
272
  for it in candidates:
227
273
  groups[str(it.get("file") or "")].append(it)
@@ -230,12 +276,13 @@ def group_candidates_by_file(candidates: List[Dict]) -> Dict[str, List[Dict]]:
230
276
 
231
277
  def create_report_writer(sec_dir: Path, report_file: Optional[str]):
232
278
  """创建报告写入函数"""
233
- from jarvis.jarvis_sec.file_manager import save_analysis_result, load_clusters
234
-
279
+ from jarvis.jarvis_sec.file_manager import load_clusters
280
+ from jarvis.jarvis_sec.file_manager import save_analysis_result
281
+
235
282
  def _append_report(items, source: str, task_id: str, cand: Dict):
236
283
  """
237
284
  将当前子任务的检测结果追加写入 analysis.jsonl 文件。
238
-
285
+
239
286
  参数:
240
287
  - items: 验证通过的问题列表(has_risk: true)
241
288
  - source: 来源("analysis_only" 或 "verified")
@@ -244,26 +291,31 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
244
291
  """
245
292
  if not items:
246
293
  return
247
-
294
+
248
295
  try:
249
296
  # 从批次中提取信息
250
297
  batch = cand.get("batch", False)
251
298
  candidates = cand.get("candidates", [])
252
-
299
+
253
300
  if not batch or not candidates:
254
301
  # 如果没有批次信息,回退到旧格式(向后兼容)
255
- path = Path(report_file) if report_file else sec_dir / "agent_issues.jsonl"
302
+ path = (
303
+ Path(report_file) if report_file else sec_dir / "agent_issues.jsonl"
304
+ )
256
305
  path.parent.mkdir(parents=True, exist_ok=True)
257
306
  with path.open("a", encoding="utf-8") as f:
258
307
  for item in items:
259
308
  line = json.dumps(item, ensure_ascii=False)
260
309
  f.write(line + "\n")
261
310
  try:
262
- typer.secho(f"[jarvis-sec] 已将 {len(items)} 个问题写入 {path}(旧格式)", fg=typer.colors.GREEN)
311
+ PrettyOutput.auto_print(
312
+ f"✅ [jarvis-sec] 已将 {len(items)} 个问题写入 {path}(旧格式)",
313
+ timestamp=True,
314
+ )
263
315
  except Exception:
264
316
  pass
265
317
  return
266
-
318
+
267
319
  # 从批次中提取 file 和 gids
268
320
  batch_file = candidates[0].get("file") if candidates else ""
269
321
  batch_gids = []
@@ -274,25 +326,25 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
274
326
  batch_gids.append(gid)
275
327
  except Exception:
276
328
  pass
277
-
329
+
278
330
  # 从 clusters.jsonl 中查找对应的 cluster_id
279
331
  clusters = load_clusters(sec_dir)
280
332
  cluster_id = None
281
333
  batch_index = None
282
334
  cluster_index = None
283
-
335
+
284
336
  # 尝试从 task_id 中提取 batch_index(格式:JARVIS-SEC-Batch-1)
285
337
  try:
286
338
  if "Batch-" in task_id:
287
339
  batch_index = int(task_id.split("Batch-")[1])
288
340
  except Exception:
289
341
  pass
290
-
342
+
291
343
  # 查找匹配的聚类(通过 file 和 gids)
292
344
  for cluster in clusters:
293
345
  cluster_file = str(cluster.get("file", ""))
294
346
  cluster_gids = cluster.get("gids", [])
295
-
347
+
296
348
  if cluster_file == batch_file and set(cluster_gids) == set(batch_gids):
297
349
  cluster_id = cluster.get("cluster_id", "")
298
350
  if not cluster_id:
@@ -301,18 +353,18 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
301
353
  batch_index = cluster.get("batch_index", batch_index or 0)
302
354
  cluster_index = cluster.get("cluster_index", 0)
303
355
  break
304
-
356
+
305
357
  # 如果找不到匹配的聚类,生成一个临时的 cluster_id
306
358
  if not cluster_id:
307
359
  cluster_id = f"{batch_file}|{batch_index or 0}|0"
308
360
  batch_index = batch_index or 0
309
361
  cluster_index = 0
310
-
362
+
311
363
  # 分离验证为问题的gid和误报的gid
312
364
  verified_gids = []
313
365
  false_positive_gids = []
314
366
  issues = []
315
-
367
+
316
368
  # 从 items 中提取已验证的问题
317
369
  for item in items:
318
370
  try:
@@ -326,17 +378,21 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
326
378
  false_positive_gids.append(gid)
327
379
  except Exception:
328
380
  pass
329
-
381
+
330
382
  # 从 candidates 中提取所有未在 items 中的 gid(这些可能是误报)
331
383
  for c in candidates:
332
384
  try:
333
385
  gid = int(c.get("gid", 0))
334
- if gid >= 1 and gid not in verified_gids and gid not in false_positive_gids:
386
+ if (
387
+ gid >= 1
388
+ and gid not in verified_gids
389
+ and gid not in false_positive_gids
390
+ ):
335
391
  # 如果这个 gid 不在已验证的问题中,可能是误报
336
392
  false_positive_gids.append(gid)
337
393
  except Exception:
338
394
  pass
339
-
395
+
340
396
  # 构建分析结果记录
341
397
  analysis_result = {
342
398
  "cluster_id": cluster_id,
@@ -348,27 +404,30 @@ def create_report_writer(sec_dir: Path, report_file: Optional[str]):
348
404
  "false_positive_gids": false_positive_gids,
349
405
  "issues": issues,
350
406
  }
351
-
407
+
352
408
  # 保存到 analysis.jsonl
353
409
  save_analysis_result(sec_dir, analysis_result)
354
-
410
+
355
411
  try:
356
- typer.secho(f"[jarvis-sec] 已将批次 {batch_index} 的分析结果写入 analysis.jsonl(问题: {len(verified_gids)}, 误报: {len(false_positive_gids)})", fg=typer.colors.GREEN)
412
+ PrettyOutput.auto_print(
413
+ f"✅ [jarvis-sec] 已将批次 {batch_index} 的分析结果写入 analysis.jsonl(问题: {len(verified_gids)}, 误报: {len(false_positive_gids)})",
414
+ timestamp=True,
415
+ )
357
416
  except Exception:
358
417
  pass
359
418
  except Exception as e:
360
419
  # 报告写入失败不影响主流程
361
420
  try:
362
- typer.secho(f"[jarvis-sec] 警告:保存分析结果失败: {e}", fg=typer.colors.YELLOW)
421
+ PrettyOutput.auto_print(f"⚠️ [jarvis-sec] 警告:保存分析结果失败: {e}")
363
422
  except Exception:
364
423
  pass
365
-
424
+
366
425
  return _append_report
367
426
 
368
427
 
369
428
  def sig_of(c: Dict) -> str:
370
429
  """生成候选问题的签名"""
371
- return f"{c.get('language','')}|{c.get('file','')}|{c.get('line','')}|{c.get('pattern','')}"
430
+ return f"{c.get('language', '')}|{c.get('file', '')}|{c.get('line', '')}|{c.get('pattern', '')}"
372
431
 
373
432
 
374
433
  def load_processed_gids_from_issues(sec_dir: Path) -> set:
@@ -391,7 +450,10 @@ def load_processed_gids_from_issues(sec_dir: Path) -> set:
391
450
  pass
392
451
  if processed_gids:
393
452
  try:
394
- typer.secho(f"[jarvis-sec] 断点恢复:从 agent_issues.jsonl 读取到 {len(processed_gids)} 个已处理的 gid", fg=typer.colors.BLUE)
453
+ PrettyOutput.auto_print(
454
+ f"✨ [jarvis-sec] 断点恢复:从 agent_issues.jsonl 读取到 {len(processed_gids)} 个已处理的 gid",
455
+ timestamp=True,
456
+ )
395
457
  except Exception:
396
458
  pass
397
459
  except Exception:
@@ -402,6 +464,7 @@ def load_processed_gids_from_issues(sec_dir: Path) -> set:
402
464
  def count_issues_from_file(sec_dir: Path) -> int:
403
465
  """从 analysis.jsonl 读取问题数量"""
404
466
  from jarvis.jarvis_sec.file_manager import get_verified_issue_gids
467
+
405
468
  verified_gids = get_verified_issue_gids(sec_dir)
406
469
  return len(verified_gids)
407
470
 
@@ -423,7 +486,10 @@ def count_issues_from_file_old(sec_dir: Path) -> int:
423
486
  gid = item.get("gid", 0)
424
487
  if gid >= 1 and gid not in saved_gids:
425
488
  # 只统计验证通过的告警(has_risk: true 且有 verification_notes)
426
- if item.get("has_risk") is True and "verification_notes" in item:
489
+ if (
490
+ item.get("has_risk") is True
491
+ and "verification_notes" in item
492
+ ):
427
493
  count += 1
428
494
  saved_gids.add(gid)
429
495
  except Exception:
@@ -450,26 +516,38 @@ def load_all_issues_from_file(sec_dir: Path) -> List[Dict]:
450
516
  gid = item.get("gid", 0)
451
517
  if gid >= 1 and gid not in saved_gids_from_file:
452
518
  # 只保留验证通过的告警(has_risk: true 且有 verification_notes)
453
- if item.get("has_risk") is True and "verification_notes" in item:
519
+ if (
520
+ item.get("has_risk") is True
521
+ and "verification_notes" in item
522
+ ):
454
523
  all_issues.append(item)
455
524
  saved_gids_from_file.add(gid)
456
525
  except Exception:
457
526
  pass
458
-
527
+
459
528
  if all_issues:
460
529
  try:
461
- typer.secho(f"[jarvis-sec] 从 agent_issues.jsonl 加载了 {len(all_issues)} 个已保存的告警", fg=typer.colors.BLUE)
530
+ PrettyOutput.auto_print(
531
+ f"✨ [jarvis-sec] 从 agent_issues.jsonl 加载了 {len(all_issues)} 个已保存的告警",
532
+ timestamp=True,
533
+ )
462
534
  except Exception:
463
535
  pass
464
536
  else:
465
537
  try:
466
- typer.secho("[jarvis-sec] agent_issues.jsonl 不存在,当前运行未发现任何问题", fg=typer.colors.BLUE)
538
+ PrettyOutput.auto_print(
539
+ "✨ [jarvis-sec] agent_issues.jsonl 不存在,当前运行未发现任何问题",
540
+ timestamp=True,
541
+ )
467
542
  except Exception:
468
543
  pass
469
544
  except Exception as e:
470
545
  # 加载失败不影响主流程
471
546
  try:
472
- typer.secho(f"[jarvis-sec] 警告:从 agent_issues.jsonl 加载告警失败: {e}", fg=typer.colors.YELLOW)
547
+ PrettyOutput.auto_print(
548
+ f"⚠️ [jarvis-sec] 警告:从 agent_issues.jsonl 加载告警失败: {e}",
549
+ timestamp=True,
550
+ )
473
551
  except Exception:
474
552
  pass
475
553
  return all_issues
@@ -496,4 +574,3 @@ def load_processed_gids_from_agent_issues(sec_dir: Path) -> set:
496
574
  except Exception:
497
575
  pass
498
576
  return processed_gids
499
-