jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
jarvis/jarvis_code_agent/lint.py
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
"""
|
|
5
5
|
Lint工具配置模块
|
|
6
|
+
|
|
7
|
+
提供lint工具配置和命令生成功能。
|
|
6
8
|
"""
|
|
7
9
|
|
|
8
10
|
import os
|
|
9
|
-
from typing import Dict, List
|
|
11
|
+
from typing import Dict, List, Tuple, Optional
|
|
10
12
|
|
|
11
13
|
import yaml # type: ignore
|
|
12
14
|
|
|
@@ -27,14 +29,14 @@ LINT_TOOLS = {
|
|
|
27
29
|
# Go
|
|
28
30
|
".go": ["go vet"],
|
|
29
31
|
# Python
|
|
30
|
-
".py": ["
|
|
31
|
-
".pyw": ["
|
|
32
|
-
".pyi": ["
|
|
33
|
-
".pyx": ["
|
|
34
|
-
".pxd": ["
|
|
32
|
+
".py": ["ruff", "mypy"],
|
|
33
|
+
".pyw": ["ruff", "mypy"],
|
|
34
|
+
".pyi": ["ruff", "mypy"],
|
|
35
|
+
".pyx": ["ruff", "mypy"],
|
|
36
|
+
".pxd": ["ruff", "mypy"],
|
|
35
37
|
# Rust
|
|
36
|
-
".rs": ["cargo clippy"
|
|
37
|
-
".rlib": ["cargo clippy"
|
|
38
|
+
".rs": ["cargo clippy"],
|
|
39
|
+
".rlib": ["cargo clippy"],
|
|
38
40
|
# Java
|
|
39
41
|
".java": ["pmd"],
|
|
40
42
|
".class": ["pmd"],
|
|
@@ -113,8 +115,6 @@ LINT_TOOLS = {
|
|
|
113
115
|
".editorconfig": ["editorconfig-checker"],
|
|
114
116
|
".eslintrc": ["eslint"],
|
|
115
117
|
".prettierrc": ["prettier"],
|
|
116
|
-
"cmakelists.txt": ["cmake-format"],
|
|
117
|
-
".cmake": ["cmake-format"],
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
|
|
@@ -136,16 +136,278 @@ LINT_TOOLS.update(load_lint_tools_config())
|
|
|
136
136
|
def get_lint_tools(filename: str) -> List[str]:
|
|
137
137
|
"""
|
|
138
138
|
根据文件扩展名或文件名获取对应的lint工具列表
|
|
139
|
+
优先级:完整文件名匹配 > 扩展名匹配
|
|
139
140
|
|
|
140
141
|
Args:
|
|
141
|
-
|
|
142
|
+
filename: 文件路径或文件名(如'test.py'或'Makefile')
|
|
142
143
|
|
|
143
144
|
Returns:
|
|
144
145
|
对应的lint工具列表,如果找不到则返回空列表
|
|
145
146
|
"""
|
|
146
147
|
filename = os.path.basename(filename)
|
|
147
|
-
|
|
148
|
+
filename_lower = filename.lower()
|
|
149
|
+
|
|
150
|
+
# 优先尝试完整文件名匹配(例如 docker-compose.yml, .eslintrc, .prettierrc)
|
|
151
|
+
lint_tools = LINT_TOOLS.get(filename_lower, [])
|
|
148
152
|
if lint_tools:
|
|
149
153
|
return lint_tools
|
|
154
|
+
|
|
155
|
+
# 如果文件名匹配失败,再尝试扩展名匹配
|
|
150
156
|
ext = os.path.splitext(filename)[1]
|
|
151
|
-
|
|
157
|
+
if ext:
|
|
158
|
+
return LINT_TOOLS.get(ext.lower(), [])
|
|
159
|
+
|
|
160
|
+
return []
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# Lint工具命令模板映射
|
|
164
|
+
# 占位符说明:
|
|
165
|
+
# - {file_path}: 单个文件的完整路径
|
|
166
|
+
# - {files}: 多个文件路径,用空格分隔
|
|
167
|
+
# - {file_name}: 文件名(不含路径)
|
|
168
|
+
LINT_COMMAND_TEMPLATES: Dict[str, str] = {
|
|
169
|
+
# Python
|
|
170
|
+
"ruff": "ruff check {file_path}",
|
|
171
|
+
"mypy": "mypy {file_path}",
|
|
172
|
+
"pylint": "pylint {file_path}",
|
|
173
|
+
"flake8": "flake8 {file_path}",
|
|
174
|
+
# JavaScript/TypeScript
|
|
175
|
+
"eslint": "eslint {file_path}",
|
|
176
|
+
"tsc": "tsc --noEmit {file_path}",
|
|
177
|
+
# Rust
|
|
178
|
+
"cargo clippy": "cargo clippy --message-format=short",
|
|
179
|
+
# Go
|
|
180
|
+
"go vet": "go vet {file_path}",
|
|
181
|
+
"golint": "golint {file_path}",
|
|
182
|
+
# Java
|
|
183
|
+
"pmd": "pmd check -d {file_path}",
|
|
184
|
+
"checkstyle": "checkstyle -c {config} {file_path}",
|
|
185
|
+
# C/C++
|
|
186
|
+
"clang-tidy": "clang-tidy {file_path}",
|
|
187
|
+
"cppcheck": "cppcheck {file_path}",
|
|
188
|
+
# PHP
|
|
189
|
+
"phpstan": "phpstan analyse {file_path}",
|
|
190
|
+
# Ruby
|
|
191
|
+
"rubocop": "rubocop {file_path}",
|
|
192
|
+
# Swift
|
|
193
|
+
"swiftlint": "swiftlint lint {file_path}",
|
|
194
|
+
# Kotlin
|
|
195
|
+
"ktlint": "ktlint {file_path}",
|
|
196
|
+
# C#
|
|
197
|
+
"roslynator": "roslynator analyze {file_path}",
|
|
198
|
+
# SQL
|
|
199
|
+
"sqlfluff": "sqlfluff lint {file_path}",
|
|
200
|
+
# Shell/Bash
|
|
201
|
+
"shellcheck": "shellcheck {file_path}",
|
|
202
|
+
# HTML/CSS
|
|
203
|
+
"htmlhint": "htmlhint {file_path}",
|
|
204
|
+
"stylelint": "stylelint {file_path}",
|
|
205
|
+
# XML/JSON/YAML
|
|
206
|
+
"xmllint": "xmllint --noout {file_path}",
|
|
207
|
+
"jsonlint": "jsonlint {file_path}",
|
|
208
|
+
"yamllint": "yamllint {file_path}",
|
|
209
|
+
# Markdown
|
|
210
|
+
"markdownlint": "markdownlint {file_path}",
|
|
211
|
+
"rstcheck": "rstcheck {file_path}",
|
|
212
|
+
# Docker
|
|
213
|
+
"hadolint": "hadolint {file_path}",
|
|
214
|
+
# Makefile
|
|
215
|
+
"checkmake": "checkmake {file_path}",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def find_config_file(config_names: List[str], project_root: Optional[str] = None, file_path: Optional[str] = None) -> Optional[str]:
|
|
220
|
+
"""
|
|
221
|
+
查找配置文件
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
config_names: 配置文件名列表(按优先级排序)
|
|
225
|
+
project_root: 项目根目录
|
|
226
|
+
file_path: 当前文件路径(可选,用于从文件所在目录向上查找)
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
配置文件的绝对路径,如果未找到则返回None
|
|
230
|
+
"""
|
|
231
|
+
search_dirs = []
|
|
232
|
+
|
|
233
|
+
if project_root:
|
|
234
|
+
search_dirs.append(project_root)
|
|
235
|
+
|
|
236
|
+
# 如果提供了文件路径,从文件所在目录向上查找
|
|
237
|
+
if file_path:
|
|
238
|
+
if os.path.isabs(file_path):
|
|
239
|
+
current_dir = os.path.dirname(file_path)
|
|
240
|
+
elif project_root:
|
|
241
|
+
current_dir = os.path.dirname(os.path.join(project_root, file_path))
|
|
242
|
+
else:
|
|
243
|
+
current_dir = os.path.dirname(os.path.abspath(file_path))
|
|
244
|
+
|
|
245
|
+
# 向上查找直到项目根目录
|
|
246
|
+
while current_dir:
|
|
247
|
+
if current_dir not in search_dirs:
|
|
248
|
+
search_dirs.append(current_dir)
|
|
249
|
+
if project_root and current_dir == project_root:
|
|
250
|
+
break
|
|
251
|
+
parent = os.path.dirname(current_dir)
|
|
252
|
+
if parent == current_dir: # 到达根目录
|
|
253
|
+
break
|
|
254
|
+
current_dir = parent
|
|
255
|
+
|
|
256
|
+
# 按优先级查找配置文件
|
|
257
|
+
for config_name in config_names:
|
|
258
|
+
for search_dir in search_dirs:
|
|
259
|
+
config_path = os.path.join(search_dir, config_name)
|
|
260
|
+
if os.path.exists(config_path) and os.path.isfile(config_path):
|
|
261
|
+
return os.path.abspath(config_path)
|
|
262
|
+
|
|
263
|
+
return None
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# 工具配置文件映射(工具名 -> 可能的配置文件名列表)
|
|
267
|
+
TOOL_CONFIG_FILES: Dict[str, List[str]] = {
|
|
268
|
+
"checkstyle": ["checkstyle.xml", ".checkstyle.xml", "checkstyle-config.xml"],
|
|
269
|
+
"eslint": [".eslintrc.js", ".eslintrc.json", ".eslintrc.yml", ".eslintrc.yaml", ".eslintrc"],
|
|
270
|
+
"stylelint": [".stylelintrc", ".stylelintrc.json", ".stylelintrc.yml", ".stylelintrc.yaml", "stylelint.config.js"],
|
|
271
|
+
"yamllint": [".yamllint", ".yamllint.yml", ".yamllint.yaml"],
|
|
272
|
+
"markdownlint": [".markdownlint.json", ".markdownlintrc"],
|
|
273
|
+
"rubocop": [".rubocop.yml", ".rubocop.yaml", ".rubocop.toml"],
|
|
274
|
+
"phpstan": ["phpstan.neon", "phpstan.neon.dist"],
|
|
275
|
+
"sqlfluff": [".sqlfluff", "sqlfluff.ini", ".sqlfluff.ini"],
|
|
276
|
+
"hadolint": [".hadolint.yaml", ".hadolint.yml"],
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
# 必须配置文件的工具(未找到配置文件时不能执行)
|
|
280
|
+
REQUIRED_CONFIG_TOOLS = {"checkstyle"}
|
|
281
|
+
|
|
282
|
+
# 可选配置文件的工具(未找到配置文件时可以使用默认配置)
|
|
283
|
+
OPTIONAL_CONFIG_TOOLS = {"eslint", "stylelint", "yamllint", "markdownlint", "rubocop", "phpstan", "sqlfluff", "hadolint"}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_lint_command(tool_name: str, file_path: str, project_root: Optional[str] = None) -> Optional[str]:
|
|
287
|
+
"""
|
|
288
|
+
获取lint工具的具体命令
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
tool_name: lint工具名称(如 'ruff', 'eslint')
|
|
292
|
+
file_path: 文件路径(相对或绝对路径)
|
|
293
|
+
project_root: 项目根目录(可选,用于处理相对路径)
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
命令字符串,如果工具不支持则返回None
|
|
297
|
+
"""
|
|
298
|
+
template = LINT_COMMAND_TEMPLATES.get(tool_name)
|
|
299
|
+
if not template:
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
# 特殊处理:某些工具不需要文件路径(如 cargo clippy)
|
|
303
|
+
if "{file_path}" not in template and "{file_name}" not in template:
|
|
304
|
+
# 不需要文件路径,直接返回模板
|
|
305
|
+
return template
|
|
306
|
+
|
|
307
|
+
# 如果是绝对路径,直接使用;否则转换为绝对路径
|
|
308
|
+
if os.path.isabs(file_path):
|
|
309
|
+
abs_file_path = file_path
|
|
310
|
+
elif project_root:
|
|
311
|
+
abs_file_path = os.path.join(project_root, file_path)
|
|
312
|
+
else:
|
|
313
|
+
abs_file_path = os.path.abspath(file_path)
|
|
314
|
+
|
|
315
|
+
# 准备占位符替换字典
|
|
316
|
+
placeholders = {
|
|
317
|
+
"file_path": abs_file_path,
|
|
318
|
+
"file_name": os.path.basename(abs_file_path),
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# 如果模板需要配置文件,尝试查找
|
|
322
|
+
if "{config}" in template:
|
|
323
|
+
config_names = TOOL_CONFIG_FILES.get(tool_name, [])
|
|
324
|
+
if config_names:
|
|
325
|
+
config_path = find_config_file(config_names, project_root, file_path)
|
|
326
|
+
if config_path:
|
|
327
|
+
placeholders["config"] = config_path
|
|
328
|
+
else:
|
|
329
|
+
# 未找到配置文件
|
|
330
|
+
if tool_name in REQUIRED_CONFIG_TOOLS:
|
|
331
|
+
# 必须配置的工具,未找到配置文件则返回None
|
|
332
|
+
return None
|
|
333
|
+
elif tool_name in OPTIONAL_CONFIG_TOOLS:
|
|
334
|
+
# 可选配置的工具,使用默认配置(移除config参数或使用默认值)
|
|
335
|
+
# 这里我们移除 {config} 占位符,让工具使用默认配置
|
|
336
|
+
# 但需要修改模板,所以这里我们返回None,让调用方知道需要处理
|
|
337
|
+
# 实际上,对于可选配置的工具,模板中不应该使用 {config},或者应该提供默认值
|
|
338
|
+
# 为了简化,我们这里返回None,表示无法生成命令
|
|
339
|
+
# 更好的做法是在模板中提供默认值,如: "checkstyle {file_path}" 或 "checkstyle -c default.xml {file_path}"
|
|
340
|
+
return None
|
|
341
|
+
else:
|
|
342
|
+
# 未定义的工具,返回None
|
|
343
|
+
return None
|
|
344
|
+
else:
|
|
345
|
+
# 工具需要配置但未定义配置文件名,返回None
|
|
346
|
+
return None
|
|
347
|
+
|
|
348
|
+
# 替换占位符
|
|
349
|
+
try:
|
|
350
|
+
command = template.format(**placeholders)
|
|
351
|
+
except KeyError:
|
|
352
|
+
# 如果模板中有未定义的占位符,返回None
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
return command
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def get_lint_commands_for_files(
|
|
359
|
+
files: List[str],
|
|
360
|
+
project_root: Optional[str] = None
|
|
361
|
+
) -> List[Tuple[str, str, str]]:
|
|
362
|
+
"""
|
|
363
|
+
获取多个文件的lint命令列表
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
files: 文件路径列表
|
|
367
|
+
project_root: 项目根目录(可选)
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
[(tool_name, file_path, command), ...] 格式的命令列表
|
|
371
|
+
"""
|
|
372
|
+
commands = []
|
|
373
|
+
# 记录不需要文件路径的工具(如 cargo clippy),避免重复执行
|
|
374
|
+
project_level_tools = set()
|
|
375
|
+
|
|
376
|
+
for file_path in files:
|
|
377
|
+
tools = get_lint_tools(file_path)
|
|
378
|
+
for tool_name in tools:
|
|
379
|
+
# 检查是否是项目级别的工具(不需要文件路径)
|
|
380
|
+
template = LINT_COMMAND_TEMPLATES.get(tool_name)
|
|
381
|
+
if template and "{file_path}" not in template and "{file_name}" not in template:
|
|
382
|
+
# 项目级别工具,每个项目只执行一次
|
|
383
|
+
if tool_name not in project_level_tools:
|
|
384
|
+
project_level_tools.add(tool_name)
|
|
385
|
+
command = get_lint_command(tool_name, file_path, project_root)
|
|
386
|
+
if command:
|
|
387
|
+
# 使用第一个文件作为标识
|
|
388
|
+
commands.append((tool_name, file_path, command))
|
|
389
|
+
else:
|
|
390
|
+
# 文件级别工具,每个文件都执行
|
|
391
|
+
command = get_lint_command(tool_name, file_path, project_root)
|
|
392
|
+
if command:
|
|
393
|
+
commands.append((tool_name, file_path, command))
|
|
394
|
+
|
|
395
|
+
return commands
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def group_commands_by_tool(commands: List[Tuple[str, str, str]]) -> Dict[str, List[Tuple[str, str]]]:
|
|
399
|
+
"""
|
|
400
|
+
按工具分组命令
|
|
401
|
+
|
|
402
|
+
Args:
|
|
403
|
+
commands: [(tool_name, file_path, command), ...] 格式的命令列表
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
{tool_name: [(file_path, command), ...]} 格式的字典
|
|
407
|
+
"""
|
|
408
|
+
grouped = {}
|
|
409
|
+
for tool_name, file_path, command in commands:
|
|
410
|
+
if tool_name not in grouped:
|
|
411
|
+
grouped[tool_name] = []
|
|
412
|
+
grouped[tool_name].append((file_path, command))
|
|
413
|
+
return grouped
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""CodeAgent 工具函数模块。
|
|
3
|
+
|
|
4
|
+
提供项目概况等工具函数。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
from jarvis.jarvis_utils.git_utils import get_recent_commits_with_files
|
|
12
|
+
from jarvis.jarvis_utils.utils import get_loc_stats
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_git_tracked_files_info(project_root: str, max_files: int = 100) -> Optional[str]:
|
|
16
|
+
"""获取git托管的文件列表或目录结构
|
|
17
|
+
|
|
18
|
+
如果文件数量超过max_files,则返回目录结构(不含文件)
|
|
19
|
+
|
|
20
|
+
参数:
|
|
21
|
+
project_root: 项目根目录
|
|
22
|
+
max_files: 文件数量阈值,超过此值则返回目录结构
|
|
23
|
+
|
|
24
|
+
返回:
|
|
25
|
+
str: 文件列表或目录结构的字符串表示,失败时返回None
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
# 获取所有git托管的文件
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
["git", "ls-files"],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
encoding="utf-8",
|
|
34
|
+
errors="replace",
|
|
35
|
+
check=True,
|
|
36
|
+
cwd=project_root,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if result.returncode != 0 or not result.stdout.strip():
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
files = [line.strip() for line in result.stdout.strip().splitlines() if line.strip()]
|
|
43
|
+
file_count = len(files)
|
|
44
|
+
|
|
45
|
+
if file_count == 0:
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
# 如果文件数量超过阈值,返回目录结构
|
|
49
|
+
if file_count > max_files:
|
|
50
|
+
# 提取所有目录路径
|
|
51
|
+
dirs = set()
|
|
52
|
+
for file_path in files:
|
|
53
|
+
# 获取文件所在的所有父目录
|
|
54
|
+
parts = file_path.split("/")
|
|
55
|
+
for i in range(1, len(parts)):
|
|
56
|
+
dir_path = "/".join(parts[:i])
|
|
57
|
+
if dir_path:
|
|
58
|
+
dirs.add(dir_path)
|
|
59
|
+
|
|
60
|
+
# 构建树形目录结构
|
|
61
|
+
dir_tree = {}
|
|
62
|
+
for dir_path in sorted(dirs):
|
|
63
|
+
parts = dir_path.split("/")
|
|
64
|
+
current = dir_tree
|
|
65
|
+
for part in parts:
|
|
66
|
+
if part not in current:
|
|
67
|
+
current[part] = {}
|
|
68
|
+
current = current[part]
|
|
69
|
+
|
|
70
|
+
def format_tree(tree: dict, prefix: str = "", is_last: bool = True) -> List[str]:
|
|
71
|
+
"""格式化目录树"""
|
|
72
|
+
lines = []
|
|
73
|
+
items = sorted(tree.items())
|
|
74
|
+
for i, (name, subtree) in enumerate(items):
|
|
75
|
+
is_last_item = i == len(items) - 1
|
|
76
|
+
connector = "└── " if is_last_item else "├── "
|
|
77
|
+
lines.append(f"{prefix}{connector}{name}/")
|
|
78
|
+
|
|
79
|
+
extension = " " if is_last_item else "│ "
|
|
80
|
+
if subtree:
|
|
81
|
+
lines.extend(format_tree(subtree, prefix + extension, is_last_item))
|
|
82
|
+
return lines
|
|
83
|
+
|
|
84
|
+
tree_lines = format_tree(dir_tree)
|
|
85
|
+
return f"Git托管目录结构(共{file_count}个文件):\n" + "\n".join(tree_lines)
|
|
86
|
+
else:
|
|
87
|
+
# 文件数量不多,直接返回文件列表
|
|
88
|
+
files_str = "\n".join(f" - {file}" for file in sorted(files))
|
|
89
|
+
return f"Git托管文件列表(共{file_count}个文件):\n{files_str}"
|
|
90
|
+
|
|
91
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
92
|
+
return None
|
|
93
|
+
except Exception:
|
|
94
|
+
# 其他异常,静默失败
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_project_overview(project_root: str) -> str:
|
|
99
|
+
"""获取项目概况信息
|
|
100
|
+
|
|
101
|
+
参数:
|
|
102
|
+
project_root: 项目根目录
|
|
103
|
+
|
|
104
|
+
返回:
|
|
105
|
+
项目概况字符串
|
|
106
|
+
"""
|
|
107
|
+
project_info = []
|
|
108
|
+
|
|
109
|
+
# 获取代码统计
|
|
110
|
+
try:
|
|
111
|
+
loc_stats = get_loc_stats()
|
|
112
|
+
if loc_stats:
|
|
113
|
+
project_info.append(f"代码统计:\n{loc_stats}")
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
# 获取Git托管的文件信息
|
|
118
|
+
try:
|
|
119
|
+
git_files_info = get_git_tracked_files_info(project_root)
|
|
120
|
+
if git_files_info:
|
|
121
|
+
project_info.append(git_files_info)
|
|
122
|
+
except Exception:
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
# 获取最近提交信息
|
|
126
|
+
try:
|
|
127
|
+
commits_info = get_recent_commits_with_files()
|
|
128
|
+
if commits_info:
|
|
129
|
+
commits_str = "\n".join(
|
|
130
|
+
f"提交 {i+1}: {commit['hash'][:7]} - {commit['message']} ({len(commit['files'])}个文件)\n"
|
|
131
|
+
+ "\n".join(f" - {file}" for file in commit["files"][:5])
|
|
132
|
+
+ ("\n ..." if len(commit["files"]) > 5 else "")
|
|
133
|
+
for i, commit in enumerate(commits_info[:5])
|
|
134
|
+
)
|
|
135
|
+
project_info.append(f"最近提交:\n{commits_str}")
|
|
136
|
+
except Exception:
|
|
137
|
+
pass
|
|
138
|
+
|
|
139
|
+
if project_info:
|
|
140
|
+
return "项目概况:\n" + "\n\n".join(project_info)
|
|
141
|
+
return ""
|
|
142
|
+
|
|
@@ -5,12 +5,26 @@ Utility module for loading language-specific code review checklists.
|
|
|
5
5
|
from typing import Dict, Optional
|
|
6
6
|
|
|
7
7
|
# Import checklist modules
|
|
8
|
-
from jarvis.jarvis_code_analysis.checklists import (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
from jarvis.jarvis_code_analysis.checklists import (
|
|
9
|
+
c_cpp,
|
|
10
|
+
csharp,
|
|
11
|
+
data_format,
|
|
12
|
+
devops,
|
|
13
|
+
docs,
|
|
14
|
+
go,
|
|
15
|
+
infrastructure,
|
|
16
|
+
java,
|
|
17
|
+
javascript,
|
|
18
|
+
kotlin,
|
|
19
|
+
php,
|
|
20
|
+
python,
|
|
21
|
+
ruby,
|
|
22
|
+
rust,
|
|
23
|
+
shell,
|
|
24
|
+
sql,
|
|
25
|
+
swift,
|
|
26
|
+
web,
|
|
27
|
+
)
|
|
14
28
|
|
|
15
29
|
# Map of language identifiers to their checklist content
|
|
16
30
|
CHECKLIST_MAP = {
|