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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +458 -152
  3. jarvis/jarvis_agent/agent_manager.py +17 -13
  4. jarvis/jarvis_agent/builtin_input_handler.py +2 -6
  5. jarvis/jarvis_agent/config_editor.py +2 -7
  6. jarvis/jarvis_agent/event_bus.py +82 -12
  7. jarvis/jarvis_agent/file_context_handler.py +329 -0
  8. jarvis/jarvis_agent/file_methodology_manager.py +3 -4
  9. jarvis/jarvis_agent/jarvis.py +628 -55
  10. jarvis/jarvis_agent/language_extractors/__init__.py +57 -0
  11. jarvis/jarvis_agent/language_extractors/c_extractor.py +21 -0
  12. jarvis/jarvis_agent/language_extractors/cpp_extractor.py +21 -0
  13. jarvis/jarvis_agent/language_extractors/go_extractor.py +21 -0
  14. jarvis/jarvis_agent/language_extractors/java_extractor.py +84 -0
  15. jarvis/jarvis_agent/language_extractors/javascript_extractor.py +79 -0
  16. jarvis/jarvis_agent/language_extractors/python_extractor.py +21 -0
  17. jarvis/jarvis_agent/language_extractors/rust_extractor.py +21 -0
  18. jarvis/jarvis_agent/language_extractors/typescript_extractor.py +84 -0
  19. jarvis/jarvis_agent/language_support_info.py +486 -0
  20. jarvis/jarvis_agent/main.py +34 -10
  21. jarvis/jarvis_agent/memory_manager.py +7 -16
  22. jarvis/jarvis_agent/methodology_share_manager.py +10 -16
  23. jarvis/jarvis_agent/prompt_manager.py +1 -1
  24. jarvis/jarvis_agent/prompts.py +193 -171
  25. jarvis/jarvis_agent/protocols.py +8 -12
  26. jarvis/jarvis_agent/run_loop.py +105 -9
  27. jarvis/jarvis_agent/session_manager.py +2 -3
  28. jarvis/jarvis_agent/share_manager.py +20 -22
  29. jarvis/jarvis_agent/shell_input_handler.py +1 -2
  30. jarvis/jarvis_agent/stdio_redirect.py +295 -0
  31. jarvis/jarvis_agent/task_analyzer.py +31 -6
  32. jarvis/jarvis_agent/task_manager.py +11 -27
  33. jarvis/jarvis_agent/tool_executor.py +2 -3
  34. jarvis/jarvis_agent/tool_share_manager.py +12 -24
  35. jarvis/jarvis_agent/utils.py +5 -1
  36. jarvis/jarvis_agent/web_bridge.py +189 -0
  37. jarvis/jarvis_agent/web_output_sink.py +53 -0
  38. jarvis/jarvis_agent/web_server.py +786 -0
  39. jarvis/jarvis_c2rust/__init__.py +26 -0
  40. jarvis/jarvis_c2rust/cli.py +575 -0
  41. jarvis/jarvis_c2rust/collector.py +250 -0
  42. jarvis/jarvis_c2rust/constants.py +26 -0
  43. jarvis/jarvis_c2rust/library_replacer.py +1254 -0
  44. jarvis/jarvis_c2rust/llm_module_agent.py +1272 -0
  45. jarvis/jarvis_c2rust/loaders.py +207 -0
  46. jarvis/jarvis_c2rust/models.py +28 -0
  47. jarvis/jarvis_c2rust/optimizer.py +2157 -0
  48. jarvis/jarvis_c2rust/scanner.py +1681 -0
  49. jarvis/jarvis_c2rust/transpiler.py +2983 -0
  50. jarvis/jarvis_c2rust/utils.py +385 -0
  51. jarvis/jarvis_code_agent/build_validation_config.py +132 -0
  52. jarvis/jarvis_code_agent/code_agent.py +1371 -220
  53. jarvis/jarvis_code_agent/code_analyzer/__init__.py +65 -0
  54. jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
  55. jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
  56. jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +106 -0
  57. jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +74 -0
  58. jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
  59. jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +72 -0
  60. jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +70 -0
  61. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +53 -0
  62. jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +47 -0
  63. jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +61 -0
  64. jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +110 -0
  65. jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +154 -0
  66. jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +110 -0
  67. jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +153 -0
  68. jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
  69. jarvis/jarvis_code_agent/code_analyzer/context_manager.py +648 -0
  70. jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
  71. jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
  72. jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
  73. jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
  74. jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
  75. jarvis/jarvis_code_agent/code_analyzer/language_support.py +110 -0
  76. jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +49 -0
  77. jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +299 -0
  78. jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +215 -0
  79. jarvis/jarvis_code_agent/code_analyzer/languages/java_language.py +212 -0
  80. jarvis/jarvis_code_agent/code_analyzer/languages/javascript_language.py +254 -0
  81. jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +269 -0
  82. jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +281 -0
  83. jarvis/jarvis_code_agent/code_analyzer/languages/typescript_language.py +280 -0
  84. jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +605 -0
  85. jarvis/jarvis_code_agent/code_analyzer/structured_code.py +556 -0
  86. jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +252 -0
  87. jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +58 -0
  88. jarvis/jarvis_code_agent/lint.py +501 -8
  89. jarvis/jarvis_code_agent/utils.py +141 -0
  90. jarvis/jarvis_code_analysis/code_review.py +493 -584
  91. jarvis/jarvis_data/config_schema.json +128 -12
  92. jarvis/jarvis_git_squash/main.py +4 -5
  93. jarvis/jarvis_git_utils/git_commiter.py +82 -75
  94. jarvis/jarvis_mcp/sse_mcp_client.py +22 -29
  95. jarvis/jarvis_mcp/stdio_mcp_client.py +12 -13
  96. jarvis/jarvis_mcp/streamable_mcp_client.py +15 -14
  97. jarvis/jarvis_memory_organizer/memory_organizer.py +55 -74
  98. jarvis/jarvis_methodology/main.py +32 -48
  99. jarvis/jarvis_multi_agent/__init__.py +287 -55
  100. jarvis/jarvis_multi_agent/main.py +36 -4
  101. jarvis/jarvis_platform/base.py +524 -202
  102. jarvis/jarvis_platform/human.py +7 -8
  103. jarvis/jarvis_platform/kimi.py +30 -36
  104. jarvis/jarvis_platform/openai.py +88 -25
  105. jarvis/jarvis_platform/registry.py +26 -10
  106. jarvis/jarvis_platform/tongyi.py +24 -25
  107. jarvis/jarvis_platform/yuanbao.py +32 -43
  108. jarvis/jarvis_platform_manager/main.py +66 -77
  109. jarvis/jarvis_platform_manager/service.py +8 -13
  110. jarvis/jarvis_rag/cli.py +53 -55
  111. jarvis/jarvis_rag/embedding_manager.py +13 -18
  112. jarvis/jarvis_rag/llm_interface.py +8 -9
  113. jarvis/jarvis_rag/query_rewriter.py +10 -21
  114. jarvis/jarvis_rag/rag_pipeline.py +24 -27
  115. jarvis/jarvis_rag/reranker.py +4 -5
  116. jarvis/jarvis_rag/retriever.py +28 -30
  117. jarvis/jarvis_sec/__init__.py +305 -0
  118. jarvis/jarvis_sec/agents.py +143 -0
  119. jarvis/jarvis_sec/analysis.py +276 -0
  120. jarvis/jarvis_sec/checkers/__init__.py +32 -0
  121. jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
  122. jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
  123. jarvis/jarvis_sec/cli.py +139 -0
  124. jarvis/jarvis_sec/clustering.py +1439 -0
  125. jarvis/jarvis_sec/file_manager.py +427 -0
  126. jarvis/jarvis_sec/parsers.py +73 -0
  127. jarvis/jarvis_sec/prompts.py +268 -0
  128. jarvis/jarvis_sec/report.py +336 -0
  129. jarvis/jarvis_sec/review.py +453 -0
  130. jarvis/jarvis_sec/status.py +264 -0
  131. jarvis/jarvis_sec/types.py +20 -0
  132. jarvis/jarvis_sec/utils.py +499 -0
  133. jarvis/jarvis_sec/verification.py +848 -0
  134. jarvis/jarvis_sec/workflow.py +226 -0
  135. jarvis/jarvis_smart_shell/main.py +38 -87
  136. jarvis/jarvis_stats/cli.py +2 -2
  137. jarvis/jarvis_stats/stats.py +8 -8
  138. jarvis/jarvis_stats/storage.py +15 -21
  139. jarvis/jarvis_stats/visualizer.py +1 -1
  140. jarvis/jarvis_tools/clear_memory.py +3 -20
  141. jarvis/jarvis_tools/cli/main.py +21 -23
  142. jarvis/jarvis_tools/edit_file.py +1019 -132
  143. jarvis/jarvis_tools/execute_script.py +83 -25
  144. jarvis/jarvis_tools/file_analyzer.py +6 -9
  145. jarvis/jarvis_tools/generate_new_tool.py +14 -21
  146. jarvis/jarvis_tools/lsp_client.py +1552 -0
  147. jarvis/jarvis_tools/methodology.py +2 -3
  148. jarvis/jarvis_tools/read_code.py +1736 -35
  149. jarvis/jarvis_tools/read_symbols.py +140 -0
  150. jarvis/jarvis_tools/read_webpage.py +12 -13
  151. jarvis/jarvis_tools/registry.py +427 -200
  152. jarvis/jarvis_tools/retrieve_memory.py +20 -19
  153. jarvis/jarvis_tools/rewrite_file.py +72 -158
  154. jarvis/jarvis_tools/save_memory.py +3 -15
  155. jarvis/jarvis_tools/search_web.py +18 -18
  156. jarvis/jarvis_tools/sub_agent.py +36 -43
  157. jarvis/jarvis_tools/sub_code_agent.py +25 -26
  158. jarvis/jarvis_tools/virtual_tty.py +55 -33
  159. jarvis/jarvis_utils/clipboard.py +7 -10
  160. jarvis/jarvis_utils/config.py +232 -45
  161. jarvis/jarvis_utils/embedding.py +8 -5
  162. jarvis/jarvis_utils/fzf.py +8 -8
  163. jarvis/jarvis_utils/git_utils.py +225 -36
  164. jarvis/jarvis_utils/globals.py +3 -3
  165. jarvis/jarvis_utils/http.py +1 -1
  166. jarvis/jarvis_utils/input.py +99 -48
  167. jarvis/jarvis_utils/jsonnet_compat.py +465 -0
  168. jarvis/jarvis_utils/methodology.py +52 -48
  169. jarvis/jarvis_utils/utils.py +819 -491
  170. jarvis_ai_assistant-0.7.6.dist-info/METADATA +600 -0
  171. jarvis_ai_assistant-0.7.6.dist-info/RECORD +218 -0
  172. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/entry_points.txt +4 -0
  173. jarvis/jarvis_agent/config.py +0 -92
  174. jarvis/jarvis_agent/edit_file_handler.py +0 -296
  175. jarvis/jarvis_platform/ai8.py +0 -332
  176. jarvis/jarvis_tools/ask_user.py +0 -54
  177. jarvis_ai_assistant-0.3.30.dist-info/METADATA +0 -381
  178. jarvis_ai_assistant-0.3.30.dist-info/RECORD +0 -137
  179. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/WHEEL +0 -0
  180. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/licenses/LICENSE +0 -0
  181. {jarvis_ai_assistant-0.3.30.dist-info → jarvis_ai_assistant-0.7.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,226 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Jarvis 安全分析套件 —— Workflow(含可复现直扫基线)
4
+
5
+ 目标:
6
+ - 识别指定模块的安全问题(内存管理、缓冲区操作、错误处理等),检出率≥60% 为目标。
7
+ - 在不依赖外部服务的前提下,提供一个“可复现、可离线”的直扫基线(direct scan)。
8
+ - 当前采用“先直扫拆分子任务,再由单Agent逐条分析”的模式;保留接口便于后续切换。
9
+
10
+ 本模块提供:
11
+ - direct_scan(entry_path, languages=None, exclude_dirs=None) -> Dict:纯Python+正则/命令行辅助扫描,生成结构化结果
12
+ - format_markdown_report(result_json: Dict) -> str:将结构化结果转为可读的 Markdown
13
+
14
+ - run_with_agent(entry_path, languages=None) -> str:使用单Agent逐条子任务分析模式(复用 jarvis.jarvis_sec.__init__ 的实现)
15
+ """
16
+
17
+ from dataclasses import asdict
18
+ from pathlib import Path
19
+ from typing import Any, Dict, Iterable, List, Optional, cast
20
+
21
+ import typer
22
+
23
+ from jarvis.jarvis_sec.checkers import analyze_c_files, analyze_rust_files
24
+ from jarvis.jarvis_sec.types import Issue
25
+
26
+
27
+ # ---------------------------
28
+ # 数据结构
29
+ # ---------------------------
30
+
31
+ # Issue dataclass is provided by jarvis.jarvis_sec.types to avoid circular imports
32
+
33
+
34
+ # ---------------------------
35
+ # 工具函数
36
+ # ---------------------------
37
+
38
+
39
+ def _iter_source_files(
40
+ entry_path: str,
41
+ languages: Optional[List[str]] = None,
42
+ exclude_dirs: Optional[List[str]] = None,
43
+ ) -> Iterable[Path]:
44
+ """
45
+ 递归枚举源文件,支持按扩展名过滤与目录排除。
46
+ 默认语言扩展:c, cpp, h, hpp, rs
47
+ """
48
+ entry = Path(entry_path)
49
+ if not entry.exists():
50
+ return
51
+
52
+ exts = set((languages or ["c", "cpp", "h", "hpp", "rs"]))
53
+ excludes = set(exclude_dirs or [".git", "build", "out", "target", "dist", "bin", "obj", "third_party", "vendor", "deps", "dependencies", "libs", "libraries", "external", "node_modules", "test", "tests", "__tests__", "spec", "testsuite", "testdata", "benchmark", "benchmarks", "perf", "performance", "bench", "benches", "profiling", "profiler", "example", "examples", "tmp", "temp", "cache", ".cache", "docs", "doc", "documentation", "generated", "gen", "mocks", "fixtures", "samples", "sample", "playground", "sandbox"])
54
+
55
+ for p in entry.rglob("*"):
56
+ if not p.is_file():
57
+ continue
58
+ # 目录排除(任意祖先包含即排除)
59
+ skip = False
60
+ for parent in p.parents:
61
+ if parent.name in excludes:
62
+ skip = True
63
+ break
64
+ if skip:
65
+ continue
66
+
67
+ suf = p.suffix.lstrip(".").lower()
68
+ if suf in exts:
69
+ yield p.relative_to(entry)
70
+
71
+
72
+
73
+ # ---------------------------
74
+ # 汇总与报告
75
+ # ---------------------------
76
+
77
+ def direct_scan(
78
+ entry_path: str,
79
+ languages: Optional[List[str]] = None,
80
+ exclude_dirs: Optional[List[str]] = None,
81
+ ) -> Dict:
82
+ """
83
+ 直扫基线:对 C/C++/Rust 进行启发式扫描,输出结构化 JSON。
84
+ - 改进:委派至模块化检查器(oh_sec.checkers),统一规则与置信度模型。
85
+ """
86
+ base = Path(entry_path).resolve()
87
+ # 计算实际使用的排除目录列表
88
+ default_excludes = [".git", "build", "out", "target", "dist", "bin", "obj", "third_party", "vendor", "deps", "dependencies", "libs", "libraries", "external", "node_modules", "test", "tests", "__tests__", "spec", "testsuite", "testdata", "benchmark", "benchmarks", "perf", "performance", "bench", "benches", "profiling", "profiler", "example", "examples", "tmp", "temp", "cache", ".cache", "docs", "doc", "documentation", "generated", "gen", "mocks", "fixtures", "samples", "sample", "playground", "sandbox"]
89
+ actual_excludes = exclude_dirs if exclude_dirs is not None else default_excludes
90
+
91
+ # 检查代码库中实际存在的排除目录
92
+ excludes_set = set(actual_excludes)
93
+ actual_excluded_dirs = []
94
+ for item in base.rglob("*"):
95
+ if item.is_dir() and item.name in excludes_set:
96
+ rel_path = item.relative_to(base)
97
+ if str(rel_path) not in actual_excluded_dirs:
98
+ actual_excluded_dirs.append(str(rel_path))
99
+
100
+ if actual_excluded_dirs:
101
+ typer.secho("[jarvis-sec] 实际排除的目录:", fg=typer.colors.BLUE)
102
+ for dir_path in sorted(actual_excluded_dirs):
103
+ typer.secho(f" - {dir_path}", fg=typer.colors.BLUE)
104
+ else:
105
+ typer.secho(f"[jarvis-sec] 未发现需要排除的目录(配置的排除目录: {', '.join(sorted(actual_excludes))})", fg=typer.colors.BLUE)
106
+
107
+ files = list(_iter_source_files(entry_path, languages, exclude_dirs))
108
+
109
+ # 按语言分组
110
+ c_like_exts = {".c", ".cpp", ".h", ".hpp"}
111
+ rust_exts = {".rs"}
112
+ c_files: List[Path] = [p for p in files if p.suffix.lower() in c_like_exts]
113
+ r_files: List[Path] = [p for p in files if p.suffix.lower() in rust_exts]
114
+
115
+ # 调用检查器(保持相对路径,基于 base_path 解析)
116
+ issues_c = analyze_c_files(str(base), [str(p) for p in c_files]) if c_files else []
117
+ issues_r = analyze_rust_files(str(base), [str(p) for p in r_files]) if r_files else []
118
+ issues: List[Issue] = issues_c + issues_r
119
+
120
+
121
+ summary: Dict[str, Any] = {
122
+ "total": len(issues),
123
+ "by_language": {"c/cpp": 0, "rust": 0},
124
+ "by_category": {},
125
+ "top_risk_files": [],
126
+ "scanned_files": len(files),
127
+ "scanned_root": str(base),
128
+ }
129
+ file_score: Dict[str, int] = {}
130
+ # Safely update language/category counts with explicit typing
131
+ lang_counts = cast(Dict[str, int], summary["by_language"])
132
+ cat_counts = cast(Dict[str, int], summary["by_category"])
133
+ for it in issues:
134
+ lang_counts[it.language] = lang_counts.get(it.language, 0) + 1
135
+ cat_counts[it.category] = cat_counts.get(it.category, 0) + 1
136
+ file_score[it.file] = file_score.get(it.file, 0) + 1
137
+ # Top 风险文件
138
+ summary["top_risk_files"] = [f for f, _ in sorted(file_score.items(), key=lambda x: x[1], reverse=True)[:10]]
139
+
140
+ result = {
141
+ "summary": summary,
142
+ "issues": [asdict(i) for i in issues],
143
+ }
144
+ return result
145
+
146
+
147
+ def format_markdown_report(result_json: Dict) -> str:
148
+ """
149
+ 将结构化 JSON 转为 Markdown 可读报告。
150
+ """
151
+ s = result_json.get("summary", {})
152
+ issues: List[Dict] = result_json.get("issues", [])
153
+ md: List[str] = []
154
+ md.append("# Jarvis 安全问题分析报告(直扫基线)")
155
+ md.append("")
156
+ md.append(f"- 扫描根目录: {s.get('scanned_root', '')}")
157
+ md.append(f"- 扫描文件数: {s.get('scanned_files', 0)}")
158
+ md.append(f"- 检出问题总数: {s.get('total', 0)}")
159
+ md.append("")
160
+ md.append("## 统计概览")
161
+ by_lang = s.get("by_language", {})
162
+ md.append(f"- 按语言: c/cpp={by_lang.get('c/cpp', 0)}, rust={by_lang.get('rust', 0)}")
163
+ md.append("- 按类别:")
164
+ for k, v in s.get("by_category", {}).items():
165
+ md.append(f" - {k}: {v}")
166
+ if s.get("top_risk_files"):
167
+ md.append("- Top 风险文件:")
168
+ for f in s["top_risk_files"]:
169
+ md.append(f" - {f}")
170
+ md.append("")
171
+ md.append("## 详细问题")
172
+ for i, it in enumerate(issues, start=1):
173
+ md.append(f"### [{i}] {it.get('file')}:{it.get('line')} ({it.get('language')}, {it.get('category')})")
174
+ md.append(f"- 模式: {it.get('pattern')}")
175
+ md.append(f"- 证据: `{it.get('evidence')}`")
176
+ md.append(f"- 描述: {it.get('description')}")
177
+ md.append(f"- 建议: {it.get('suggestion')}")
178
+ md.append(f"- 置信度: {it.get('confidence')}, 严重性: {it.get('severity')}")
179
+ md.append("")
180
+ return "\n".join(md)
181
+
182
+
183
+ def run_with_agent(
184
+ entry_path: str,
185
+ languages: Optional[List[str]] = None,
186
+ llm_group: Optional[str] = None,
187
+ report_file: Optional[str] = None,
188
+ cluster_limit: int = 50,
189
+ exclude_dirs: Optional[List[str]] = None,
190
+ enable_verification: bool = True,
191
+ force_save_memory: bool = False,
192
+ output_file: Optional[str] = None,
193
+ ) -> str:
194
+ """
195
+ 使用单Agent逐条子任务分析模式运行(与 jarvis.jarvis_sec.__init__ 中保持一致)。
196
+ - 先执行本地直扫,生成候选问题
197
+ - 为每条候选创建一次普通Agent任务进行分析与验证
198
+ - 聚合为最终报告(JSON + Markdown)返回
199
+
200
+ 其他:
201
+ - llm_group: 本次分析使用的模型组(仅透传给 Agent,不修改全局配置)
202
+ - report_file: JSONL 报告文件路径(可选,透传)
203
+ - cluster_limit: 聚类时每批次最多处理的告警数(默认 50),当单个文件告警过多时按批次进行聚类
204
+ - exclude_dirs: 要排除的目录列表(可选),默认已包含构建产物(build, out, target, dist, bin, obj)、依赖目录(third_party, vendor, deps, dependencies, libs, libraries, external, node_modules)、测试目录(test, tests, __tests__, spec, testsuite, testdata)、性能测试目录(benchmark, benchmarks, perf, performance, bench, benches, profiling, profiler)、示例目录(example, examples)、临时/缓存(tmp, temp, cache, .cache)、文档(docs, doc, documentation)、生成代码(generated, gen)和其他(mocks, fixtures, samples, sample, playground, sandbox)
205
+ - enable_verification: 是否启用二次验证(默认 True),关闭后分析Agent确认的问题将直接写入报告
206
+ """
207
+ from jarvis.jarvis_sec import run_security_analysis # 延迟导入,避免循环
208
+ return run_security_analysis(
209
+ entry_path,
210
+ languages=languages,
211
+ llm_group=llm_group,
212
+ report_file=report_file,
213
+ cluster_limit=cluster_limit,
214
+ exclude_dirs=exclude_dirs,
215
+ enable_verification=enable_verification,
216
+ force_save_memory=force_save_memory,
217
+ output_file=output_file,
218
+ )
219
+
220
+
221
+ __all__ = [
222
+ "Issue",
223
+ "direct_scan",
224
+ "format_markdown_report",
225
+ "run_with_agent",
226
+ ]
@@ -9,7 +9,6 @@ from jarvis.jarvis_platform.registry import PlatformRegistry
9
9
  from jarvis.jarvis_utils.config import get_shell_name, set_config
10
10
  from jarvis.jarvis_utils.input import get_multiline_input
11
11
  from jarvis.jarvis_utils.utils import init_env
12
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
13
12
 
14
13
  app = typer.Typer(
15
14
  help="将自然语言要求转换为shell命令",
@@ -115,8 +114,8 @@ def install_jss_completion(
115
114
  ) -> None:
116
115
  """为指定的shell安装'命令未找到'处理器,实现自然语言命令建议"""
117
116
  if shell not in ("fish", "bash", "zsh"):
118
- PrettyOutput.print(
119
- f"错误: 不支持的shell类型: {shell}, 仅支持fish, bash, zsh", OutputType.ERROR
117
+ print(
118
+ f"错误: 不支持的shell类型: {shell}, 仅支持fish, bash, zsh"
120
119
  )
121
120
  raise typer.Exit(code=1)
122
121
 
@@ -125,7 +124,7 @@ def install_jss_completion(
125
124
  start_marker, end_marker = _get_markers()
126
125
 
127
126
  if not os.path.exists(config_file):
128
- PrettyOutput.print("未找到 config.fish 文件,将创建新文件", OutputType.INFO)
127
+ print("ℹ️ 未找到 config.fish 文件,将创建新文件")
129
128
  os.makedirs(os.path.dirname(config_file), exist_ok=True)
130
129
  with open(config_file, "w") as f:
131
130
  f.write("")
@@ -134,9 +133,8 @@ def install_jss_completion(
134
133
  content = f.read()
135
134
 
136
135
  if start_marker in content:
137
- PrettyOutput.print(
138
- "JSS fish completion 已安装,请执行: source ~/.config/fish/config.fish",
139
- OutputType.SUCCESS,
136
+ print(
137
+ "JSS fish completion 已安装,请执行: source ~/.config/fish/config.fish"
140
138
  )
141
139
  return
142
140
 
@@ -157,16 +155,15 @@ end
157
155
  {end_marker}
158
156
  """
159
157
  )
160
- PrettyOutput.print(
161
- "JSS fish completion 已安装,请执行: source ~/.config/fish/config.fish",
162
- OutputType.SUCCESS,
158
+ print(
159
+ "JSS fish completion 已安装,请执行: source ~/.config/fish/config.fish"
163
160
  )
164
161
  elif shell == "bash":
165
162
  config_file = _get_bash_config_file()
166
163
  start_marker, end_marker = _get_bash_markers()
167
164
 
168
165
  if not os.path.exists(config_file):
169
- PrettyOutput.print("未找到 ~/.bashrc 文件,将创建新文件", OutputType.INFO)
166
+ print("ℹ️ 未找到 ~/.bashrc 文件,将创建新文件")
170
167
  os.makedirs(os.path.dirname(config_file), exist_ok=True)
171
168
  with open(config_file, "w") as f:
172
169
  f.write("")
@@ -175,9 +172,8 @@ end
175
172
  content = f.read()
176
173
 
177
174
  if start_marker in content:
178
- PrettyOutput.print(
179
- "JSS bash completion 已安装,请执行: source ~/.bashrc",
180
- OutputType.SUCCESS,
175
+ print(
176
+ "JSS bash completion 已安装,请执行: source ~/.bashrc"
181
177
  )
182
178
  return
183
179
  else:
@@ -221,16 +217,15 @@ command_not_found_handle() {{
221
217
  {end_marker}
222
218
  """
223
219
  )
224
- PrettyOutput.print(
225
- "JSS bash completion 已安装,请执行: source ~/.bashrc",
226
- OutputType.SUCCESS,
220
+ print(
221
+ "JSS bash completion 已安装,请执行: source ~/.bashrc"
227
222
  )
228
223
  elif shell == "zsh":
229
224
  config_file = _get_zsh_config_file()
230
225
  start_marker, end_marker = _get_zsh_markers()
231
226
 
232
227
  if not os.path.exists(config_file):
233
- PrettyOutput.print("未找到 ~/.zshrc 文件,将创建新文件", OutputType.INFO)
228
+ print("ℹ️ 未找到 ~/.zshrc 文件,将创建新文件")
234
229
  os.makedirs(os.path.dirname(config_file), exist_ok=True)
235
230
  with open(config_file, "w") as f:
236
231
  f.write("")
@@ -239,8 +234,8 @@ command_not_found_handle() {{
239
234
  content = f.read()
240
235
 
241
236
  if start_marker in content:
242
- PrettyOutput.print(
243
- "JSS zsh completion 已安装,请执行: source ~/.zshrc", OutputType.SUCCESS
237
+ print(
238
+ "JSS zsh completion 已安装,请执行: source ~/.zshrc"
244
239
  )
245
240
  return
246
241
 
@@ -288,55 +283,11 @@ command_not_found_handler() {{
288
283
  {end_marker}
289
284
  """
290
285
  )
291
- PrettyOutput.print(
292
- "JSS zsh completion 已安装,请执行: source ~/.zshrc", OutputType.SUCCESS
286
+ print(
287
+ "JSS zsh completion 已安装,请执行: source ~/.zshrc"
293
288
  )
294
289
  return
295
290
 
296
- with open(config_file, "a") as f:
297
- f.write(
298
- f"""
299
- {start_marker}
300
- # Bash 'command not found' handler for JSS
301
- # 行为:
302
- # - 生成可编辑的建议命令,用户可直接编辑后回车执行
303
- # - 非交互模式下仅打印建议
304
- command_not_found_handle() {{
305
- local cmd="$1"
306
- shift || true
307
- local text="$cmd $*"
308
-
309
- # 与 fish 行为保持一致:对过短输入不处理
310
- if [ ${{#text}} -lt 10 ]; then
311
- return 127
312
- fi
313
-
314
- local suggestion edited
315
- suggestion=$(jss request "$text")
316
- if [ -n "$suggestion" ]; then
317
- # 交互式:用 readline 预填命令,用户可直接回车执行或编辑
318
- if [[ $- == *i* ]]; then
319
- edited="$suggestion"
320
- # -e 启用 readline;-i 预填默认值;无提示前缀,使体验更接近 fish 的“替换命令行”
321
- read -e -i "$edited" edited
322
- if [ -n "$edited" ]; then
323
- eval "$edited"
324
- return $?
325
- fi
326
- else
327
- # 非交互:仅打印建议
328
- printf '%s\n' "$suggestion"
329
- fi
330
- fi
331
- return 127
332
- }}
333
- {end_marker}
334
- """
335
- )
336
- PrettyOutput.print(
337
- "JSS bash completion 已安装,请执行: source ~/.bashrc", OutputType.SUCCESS
338
- )
339
-
340
291
 
341
292
  @app.command("uninstall")
342
293
  def uninstall_jss_completion(
@@ -344,8 +295,8 @@ def uninstall_jss_completion(
344
295
  ) -> None:
345
296
  """卸载JSS shell'命令未找到'处理器"""
346
297
  if shell not in ("fish", "bash", "zsh"):
347
- PrettyOutput.print(
348
- f"错误: 不支持的shell类型: {shell}, 仅支持fish, bash, zsh", OutputType.ERROR
298
+ print(
299
+ f"错误: 不支持的shell类型: {shell}, 仅支持fish, bash, zsh"
349
300
  )
350
301
  raise typer.Exit(code=1)
351
302
 
@@ -354,8 +305,8 @@ def uninstall_jss_completion(
354
305
  start_marker, end_marker = _get_markers()
355
306
 
356
307
  if not os.path.exists(config_file):
357
- PrettyOutput.print(
358
- "未找到 JSS fish completion 配置,无需卸载", OutputType.INFO
308
+ print(
309
+ "ℹ️ 未找到 JSS fish completion 配置,无需卸载"
359
310
  )
360
311
  return
361
312
 
@@ -363,8 +314,8 @@ def uninstall_jss_completion(
363
314
  content = f.read()
364
315
 
365
316
  if start_marker not in content:
366
- PrettyOutput.print(
367
- "未找到 JSS fish completion 配置,无需卸载", OutputType.INFO
317
+ print(
318
+ "ℹ️ 未找到 JSS fish completion 配置,无需卸载"
368
319
  )
369
320
  return
370
321
 
@@ -373,17 +324,16 @@ def uninstall_jss_completion(
373
324
  with open(config_file, "w") as f:
374
325
  f.write(new_content)
375
326
 
376
- PrettyOutput.print(
377
- "JSS fish completion 已卸载,请执行: source ~/.config/fish/config.fish",
378
- OutputType.SUCCESS,
327
+ print(
328
+ "JSS fish completion 已卸载,请执行: source ~/.config/fish/config.fish"
379
329
  )
380
330
  elif shell == "bash":
381
331
  config_file = _get_bash_config_file()
382
332
  start_marker, end_marker = _get_bash_markers()
383
333
 
384
334
  if not os.path.exists(config_file):
385
- PrettyOutput.print(
386
- "未找到 JSS bash completion 配置,无需卸载", OutputType.INFO
335
+ print(
336
+ "ℹ️ 未找到 JSS bash completion 配置,无需卸载"
387
337
  )
388
338
  return
389
339
 
@@ -391,8 +341,8 @@ def uninstall_jss_completion(
391
341
  content = f.read()
392
342
 
393
343
  if start_marker not in content:
394
- PrettyOutput.print(
395
- "未找到 JSS bash completion 配置,无需卸载", OutputType.INFO
344
+ print(
345
+ "ℹ️ 未找到 JSS bash completion 配置,无需卸载"
396
346
  )
397
347
  return
398
348
 
@@ -401,16 +351,16 @@ def uninstall_jss_completion(
401
351
  with open(config_file, "w") as f:
402
352
  f.write(new_content)
403
353
 
404
- PrettyOutput.print(
405
- "JSS bash completion 已卸载,请执行: source ~/.bashrc", OutputType.SUCCESS
354
+ print(
355
+ "JSS bash completion 已卸载,请执行: source ~/.bashrc"
406
356
  )
407
357
  elif shell == "zsh":
408
358
  config_file = _get_zsh_config_file()
409
359
  start_marker, end_marker = _get_zsh_markers()
410
360
 
411
361
  if not os.path.exists(config_file):
412
- PrettyOutput.print(
413
- "未找到 JSS zsh completion 配置,无需卸载", OutputType.INFO
362
+ print(
363
+ "ℹ️ 未找到 JSS zsh completion 配置,无需卸载"
414
364
  )
415
365
  return
416
366
 
@@ -418,8 +368,8 @@ def uninstall_jss_completion(
418
368
  content = f.read()
419
369
 
420
370
  if start_marker not in content:
421
- PrettyOutput.print(
422
- "未找到 JSS zsh completion 配置,无需卸载", OutputType.INFO
371
+ print(
372
+ "ℹ️ 未找到 JSS zsh completion 配置,无需卸载"
423
373
  )
424
374
  return
425
375
 
@@ -428,8 +378,8 @@ def uninstall_jss_completion(
428
378
  with open(config_file, "w") as f:
429
379
  f.write(new_content)
430
380
 
431
- PrettyOutput.print(
432
- "JSS zsh completion 已卸载,请执行: source ~/.zshrc", OutputType.SUCCESS
381
+ print(
382
+ "JSS zsh completion 已卸载,请执行: source ~/.zshrc"
433
383
  )
434
384
 
435
385
 
@@ -444,6 +394,7 @@ def process_request(request: str) -> Optional[str]:
444
394
  """
445
395
  try:
446
396
  # Get language model instance
397
+ # 使用normal平台,智能shell命令生成是一般任务
447
398
  model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
448
399
 
449
400
  shell = get_shell_name()
@@ -190,7 +190,7 @@ def list():
190
190
  try:
191
191
  dt = datetime.fromisoformat(last_updated)
192
192
  last_updated = dt.strftime("%Y-%m-%d %H:%M")
193
- except:
193
+ except Exception:
194
194
  pass
195
195
 
196
196
  # 获取数据点数和标签
@@ -379,7 +379,7 @@ def demo():
379
379
  def main():
380
380
  """主入口函数"""
381
381
  # 初始化环境,防止设置初始化太迟
382
- init_env("欢迎使用 Jarvis-Stats,您的统计分析工具已准备就绪!", None)
382
+ init_env()
383
383
  app()
384
384
 
385
385
 
@@ -9,7 +9,7 @@ from typing import Dict, List, Optional, Union, Any
9
9
 
10
10
  from jarvis.jarvis_stats.storage import StatsStorage
11
11
  from jarvis.jarvis_stats.visualizer import StatsVisualizer
12
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
12
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput # 保留用于语法高亮
13
13
 
14
14
 
15
15
  class StatsManager:
@@ -480,7 +480,7 @@ class StatsManager:
480
480
  try:
481
481
  dt = datetime.fromisoformat(last_updated)
482
482
  last_updated = dt.strftime("%Y-%m-%d %H:%M")
483
- except:
483
+ except Exception:
484
484
  pass
485
485
 
486
486
  total_value = sum(r.get("value", 0) for r in records)
@@ -549,8 +549,8 @@ class StatsManager:
549
549
  )
550
550
 
551
551
  if not aggregated:
552
- PrettyOutput.print(
553
- f"没有找到指标 '{metric_name}' 的数据", OutputType.WARNING
552
+ print(
553
+ f"⚠️ 没有找到指标 '{metric_name}' 的数据"
554
554
  )
555
555
  return
556
556
 
@@ -585,7 +585,7 @@ class StatsManager:
585
585
  show_values=True,
586
586
  )
587
587
 
588
- PrettyOutput.print(chart, OutputType.CODE, lang="text")
588
+ PrettyOutput.print(chart, OutputType.CODE, lang="text") # 保留用于语法高亮
589
589
 
590
590
  # 显示时间范围
591
591
  from rich.panel import Panel
@@ -681,8 +681,8 @@ class StatsManager:
681
681
  )
682
682
 
683
683
  if not aggregated:
684
- PrettyOutput.print(
685
- f"没有找到指标 '{metric_name}' 的数据", OutputType.WARNING
684
+ print(
685
+ f"⚠️ 没有找到指标 '{metric_name}' 的数据"
686
686
  )
687
687
  return
688
688
 
@@ -693,7 +693,7 @@ class StatsManager:
693
693
  # 显示汇总
694
694
  summary = visualizer.show_summary(aggregated, metric_name, unit, tags)
695
695
  if summary: # 如果返回了内容才打印(兼容性)
696
- PrettyOutput.print(summary, OutputType.INFO)
696
+ print(f"ℹ️ {summary}")
697
697
 
698
698
  # 显示时间范围
699
699
  from rich.panel import Panel
@@ -71,7 +71,7 @@ class StatsStorage:
71
71
  with open(filepath, "r", encoding="utf-8") as f:
72
72
  data = json.load(f)
73
73
  return data
74
- except (json.JSONDecodeError, IOError):
74
+ except Exception:
75
75
  if attempt < max_retries - 1:
76
76
  time.sleep(0.1 * (attempt + 1)) # 递增延迟
77
77
  continue
@@ -160,6 +160,9 @@ class StatsStorage:
160
160
 
161
161
  # 更新元数据
162
162
  meta = self._load_json(self.meta_file)
163
+ # 确保 meta 字典中有 "metrics" 键
164
+ if "metrics" not in meta:
165
+ meta["metrics"] = {}
163
166
  if metric_name not in meta["metrics"]:
164
167
  meta["metrics"][metric_name] = {
165
168
  "unit": unit,
@@ -218,9 +221,9 @@ class StatsStorage:
218
221
  new_total = current_total + float(value)
219
222
  self._save_text_atomic(total_file, str(new_total))
220
223
  else:
221
- # 首次生成:扫描历史数据(包含刚写入的这条记录)并写入
222
- # 注意:get_metric_total 内部会完成扫描并写入 totals 文件,这里无需再额外写入或累加
223
- _ = self.get_metric_total(metric_name)
224
+ # 首次生成:直接写入当前值,避免扫描所有历史文件
225
+ # 如果后续需要精确总量,可以通过 get_metric_total 重新计算
226
+ self._save_text_atomic(total_file, str(float(value)))
224
227
  except Exception:
225
228
  # 静默失败,不影响主流程
226
229
  pass
@@ -452,21 +455,11 @@ class StatsStorage:
452
455
 
453
456
  def list_metrics(self) -> List[str]:
454
457
  """列出所有指标"""
455
- # 从元数据文件获取指标
458
+ # 从元数据文件获取指标(主要来源,避免扫描所有历史文件)
456
459
  meta = self._load_json(self.meta_file)
457
460
  metrics_from_meta = set(meta.get("metrics", {}).keys())
458
461
 
459
- # 扫描所有数据文件获取实际存在的指标
460
- metrics_from_data: Set[str] = set()
461
- for data_file in self.data_dir.glob("stats_*.json"):
462
- try:
463
- data = self._load_json(data_file)
464
- metrics_from_data.update(data.keys())
465
- except (json.JSONDecodeError, OSError):
466
- # 忽略无法读取的文件
467
- continue
468
-
469
- # 扫描总量缓存目录中已有的指标文件
462
+ # 扫描总量缓存目录中已有的指标文件(快速)
470
463
  metrics_from_totals: Set[str] = set()
471
464
  try:
472
465
  for f in self.totals_dir.glob("*"):
@@ -475,10 +468,9 @@ class StatsStorage:
475
468
  except Exception:
476
469
  pass
477
470
 
478
- # 合并三个来源的指标并返回排序后的列表
479
- all_metrics = metrics_from_meta.union(metrics_from_data).union(
480
- metrics_from_totals
481
- )
471
+ # 合并两个来源的指标并返回排序后的列表
472
+ # 注意:不再扫描所有历史数据文件,避免性能问题
473
+ all_metrics = metrics_from_meta.union(metrics_from_totals)
482
474
  return sorted(list(all_metrics))
483
475
 
484
476
  def aggregate_metrics(
@@ -560,7 +552,9 @@ class StatsStorage:
560
552
  """
561
553
  # 检查指标是否存在
562
554
  meta = self._load_json(self.meta_file)
563
- if metric_name not in meta.get("metrics", {}):
555
+ if "metrics" not in meta:
556
+ meta["metrics"] = {}
557
+ if metric_name not in meta["metrics"]:
564
558
  return False
565
559
 
566
560
  # 从元数据中删除指标
@@ -41,7 +41,7 @@ class StatsVisualizer:
41
41
  try:
42
42
  columns = os.get_terminal_size().columns
43
43
  return columns
44
- except:
44
+ except Exception:
45
45
  return 80
46
46
 
47
47
  def plot_line_chart(