jarvis-ai-assistant 0.1.192__py3-none-any.whl → 0.1.194__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 +69 -37
- jarvis/jarvis_agent/builtin_input_handler.py +26 -4
- jarvis/jarvis_agent/jarvis.py +38 -22
- jarvis/jarvis_agent/main.py +20 -12
- jarvis/jarvis_agent/output_handler.py +7 -7
- jarvis/jarvis_agent/shell_input_handler.py +14 -11
- jarvis/jarvis_code_agent/code_agent.py +93 -90
- jarvis/jarvis_code_agent/lint.py +92 -105
- jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
- jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
- jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
- jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
- jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
- jarvis/jarvis_code_analysis/checklists/go.py +1 -1
- jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
- jarvis/jarvis_code_analysis/checklists/java.py +1 -1
- jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
- jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
- jarvis/jarvis_code_analysis/checklists/loader.py +51 -35
- jarvis/jarvis_code_analysis/checklists/php.py +1 -1
- jarvis/jarvis_code_analysis/checklists/python.py +1 -1
- jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
- jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
- jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
- jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
- jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
- jarvis/jarvis_code_analysis/checklists/web.py +1 -1
- jarvis/jarvis_code_analysis/code_review.py +293 -192
- jarvis/jarvis_dev/main.py +73 -56
- jarvis/jarvis_git_details/main.py +29 -33
- jarvis/jarvis_git_squash/main.py +13 -11
- jarvis/jarvis_git_utils/git_commiter.py +12 -3
- jarvis/jarvis_mcp/__init__.py +8 -10
- jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
- jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
- jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
- jarvis/jarvis_methodology/main.py +81 -47
- jarvis/jarvis_multi_agent/__init__.py +24 -16
- jarvis/jarvis_multi_agent/main.py +10 -4
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/base.py +49 -21
- jarvis/jarvis_platform/human.py +5 -3
- jarvis/jarvis_platform/kimi.py +96 -72
- jarvis/jarvis_platform/openai.py +23 -28
- jarvis/jarvis_platform/registry.py +50 -33
- jarvis/jarvis_platform/tongyi.py +16 -10
- jarvis/jarvis_platform/yuanbao.py +205 -147
- jarvis/jarvis_platform_manager/main.py +4 -2
- jarvis/jarvis_smart_shell/main.py +35 -29
- jarvis/jarvis_tools/ask_user.py +8 -16
- jarvis/jarvis_tools/base.py +3 -2
- jarvis/jarvis_tools/chdir.py +7 -19
- jarvis/jarvis_tools/cli/main.py +14 -10
- jarvis/jarvis_tools/code_plan.py +10 -31
- jarvis/jarvis_tools/create_code_agent.py +10 -13
- jarvis/jarvis_tools/create_sub_agent.py +10 -22
- jarvis/jarvis_tools/edit_file.py +98 -76
- jarvis/jarvis_tools/execute_script.py +46 -46
- jarvis/jarvis_tools/file_analyzer.py +22 -34
- jarvis/jarvis_tools/file_operation.py +69 -62
- jarvis/jarvis_tools/generate_new_tool.py +0 -2
- jarvis/jarvis_tools/methodology.py +19 -23
- jarvis/jarvis_tools/read_code.py +42 -33
- jarvis/jarvis_tools/read_webpage.py +7 -16
- jarvis/jarvis_tools/registry.py +65 -32
- jarvis/jarvis_tools/rewrite_file.py +26 -29
- jarvis/jarvis_tools/search_web.py +5 -8
- jarvis/jarvis_tools/virtual_tty.py +133 -122
- jarvis/jarvis_utils/__init__.py +0 -1
- jarvis/jarvis_utils/builtin_replace_map.py +96 -8
- jarvis/jarvis_utils/config.py +59 -32
- jarvis/jarvis_utils/embedding.py +17 -14
- jarvis/jarvis_utils/file_processors.py +16 -9
- jarvis/jarvis_utils/git_utils.py +140 -99
- jarvis/jarvis_utils/globals.py +1 -1
- jarvis/jarvis_utils/input.py +84 -52
- jarvis/jarvis_utils/methodology.py +28 -21
- jarvis/jarvis_utils/output.py +159 -78
- jarvis/jarvis_utils/tag.py +2 -1
- jarvis/jarvis_utils/utils.py +85 -51
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
- jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
- jarvis/jarvis_agent/file_input_handler.py +0 -112
- jarvis/jarvis_event/__init__.py +0 -0
- jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/top_level.txt +0 -0
@@ -8,8 +8,7 @@ from typing import Any, Dict, List
|
|
8
8
|
from yaspin import yaspin
|
9
9
|
|
10
10
|
from jarvis.jarvis_agent import Agent
|
11
|
-
from jarvis.jarvis_code_analysis.checklists.loader import
|
12
|
-
get_language_checklist
|
11
|
+
from jarvis.jarvis_code_analysis.checklists.loader import get_language_checklist
|
13
12
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
14
13
|
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
15
14
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
@@ -20,7 +19,7 @@ from jarvis.jarvis_utils.utils import init_env, is_context_overflow
|
|
20
19
|
class CodeReviewTool:
|
21
20
|
name = "code_review"
|
22
21
|
description = "自动代码审查工具,用于分析代码变更"
|
23
|
-
labels = [
|
22
|
+
labels = ["code", "analysis", "review"]
|
24
23
|
parameters = {
|
25
24
|
"type": "object",
|
26
25
|
"properties": {
|
@@ -28,31 +27,31 @@ class CodeReviewTool:
|
|
28
27
|
"type": "string",
|
29
28
|
"description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围,'file' 审查特定文件",
|
30
29
|
"enum": ["commit", "current", "range", "file"],
|
31
|
-
"default": "current"
|
30
|
+
"default": "current",
|
32
31
|
},
|
33
32
|
"commit_sha": {
|
34
33
|
"type": "string",
|
35
|
-
"description": "要分析的提交SHA(review_type='commit'时必填)"
|
34
|
+
"description": "要分析的提交SHA(review_type='commit'时必填)",
|
36
35
|
},
|
37
36
|
"start_commit": {
|
38
37
|
"type": "string",
|
39
|
-
"description": "起始提交SHA(review_type='range'时必填)"
|
38
|
+
"description": "起始提交SHA(review_type='range'时必填)",
|
40
39
|
},
|
41
40
|
"end_commit": {
|
42
41
|
"type": "string",
|
43
|
-
"description": "结束提交SHA(review_type='range'时必填)"
|
42
|
+
"description": "结束提交SHA(review_type='range'时必填)",
|
44
43
|
},
|
45
44
|
"file_path": {
|
46
45
|
"type": "string",
|
47
|
-
"description": "要审查的文件路径(review_type='file'时必填)"
|
46
|
+
"description": "要审查的文件路径(review_type='file'时必填)",
|
48
47
|
},
|
49
48
|
"root_dir": {
|
50
49
|
"type": "string",
|
51
50
|
"description": "代码库根目录路径(可选)",
|
52
|
-
"default": "."
|
53
|
-
}
|
51
|
+
"default": ".",
|
52
|
+
},
|
54
53
|
},
|
55
|
-
"required": []
|
54
|
+
"required": [],
|
56
55
|
}
|
57
56
|
|
58
57
|
def _detect_languages_from_files(self, file_paths: List[str]) -> List[str]:
|
@@ -62,143 +61,203 @@ class CodeReviewTool:
|
|
62
61
|
"""
|
63
62
|
if not file_paths:
|
64
63
|
return []
|
65
|
-
|
64
|
+
|
66
65
|
# Extension-based language detection
|
67
66
|
languages = set()
|
68
67
|
for file_path in file_paths:
|
69
68
|
file_path = file_path.lower()
|
70
69
|
_, ext = os.path.splitext(file_path)
|
71
|
-
|
70
|
+
|
72
71
|
# Get base name for special files without extensions
|
73
72
|
base_name = os.path.basename(file_path)
|
74
|
-
|
73
|
+
|
75
74
|
# C/C++
|
76
|
-
if ext in [
|
77
|
-
|
78
|
-
|
75
|
+
if ext in [
|
76
|
+
".c",
|
77
|
+
".cpp",
|
78
|
+
".cc",
|
79
|
+
".cxx",
|
80
|
+
".h",
|
81
|
+
".hpp",
|
82
|
+
".hxx",
|
83
|
+
".inl",
|
84
|
+
".ipp",
|
85
|
+
]:
|
86
|
+
languages.add("c_cpp")
|
87
|
+
|
79
88
|
# Go
|
80
|
-
elif ext in [
|
81
|
-
languages.add(
|
82
|
-
|
89
|
+
elif ext in [".go"]:
|
90
|
+
languages.add("go")
|
91
|
+
|
83
92
|
# Python
|
84
|
-
elif ext in [
|
85
|
-
|
86
|
-
|
93
|
+
elif ext in [".py", ".pyw", ".pyi", ".pyx", ".pxd"] or base_name in [
|
94
|
+
"requirements.txt",
|
95
|
+
"setup.py",
|
96
|
+
"pyproject.toml",
|
97
|
+
]:
|
98
|
+
languages.add("python")
|
99
|
+
|
87
100
|
# Rust
|
88
|
-
elif ext in [
|
89
|
-
languages.add(
|
90
|
-
|
101
|
+
elif ext in [".rs", ".rlib"] or base_name in ["Cargo.toml", "Cargo.lock"]:
|
102
|
+
languages.add("rust")
|
103
|
+
|
91
104
|
# Java
|
92
|
-
elif ext in [
|
93
|
-
|
94
|
-
|
105
|
+
elif ext in [".java", ".class", ".jar"] or base_name in [
|
106
|
+
"pom.xml",
|
107
|
+
"build.gradle",
|
108
|
+
]:
|
109
|
+
languages.add("java")
|
110
|
+
|
95
111
|
# JavaScript
|
96
|
-
elif ext in [
|
97
|
-
languages.add(
|
98
|
-
|
112
|
+
elif ext in [".js", ".mjs", ".cjs", ".jsx"]:
|
113
|
+
languages.add("javascript")
|
114
|
+
|
99
115
|
# TypeScript
|
100
|
-
elif ext in [
|
101
|
-
languages.add(
|
102
|
-
|
116
|
+
elif ext in [".ts", ".tsx", ".cts", ".mts"]:
|
117
|
+
languages.add("typescript")
|
118
|
+
|
103
119
|
# PHP
|
104
|
-
elif ext in [
|
105
|
-
languages.add(
|
106
|
-
|
120
|
+
elif ext in [".php", ".phtml", ".php5", ".php7", ".phps"]:
|
121
|
+
languages.add("php")
|
122
|
+
|
107
123
|
# Ruby
|
108
|
-
elif ext in [
|
109
|
-
|
110
|
-
|
124
|
+
elif ext in [".rb", ".rake", ".gemspec"] or base_name in [
|
125
|
+
"Gemfile",
|
126
|
+
"Rakefile",
|
127
|
+
]:
|
128
|
+
languages.add("ruby")
|
129
|
+
|
111
130
|
# Swift
|
112
|
-
elif ext in [
|
113
|
-
languages.add(
|
114
|
-
|
131
|
+
elif ext in [".swift"]:
|
132
|
+
languages.add("swift")
|
133
|
+
|
115
134
|
# Kotlin
|
116
|
-
elif ext in [
|
117
|
-
languages.add(
|
118
|
-
|
135
|
+
elif ext in [".kt", ".kts"]:
|
136
|
+
languages.add("kotlin")
|
137
|
+
|
119
138
|
# C#
|
120
|
-
elif ext in [
|
121
|
-
languages.add(
|
122
|
-
|
139
|
+
elif ext in [".cs", ".csx"]:
|
140
|
+
languages.add("csharp")
|
141
|
+
|
123
142
|
# SQL
|
124
|
-
elif ext in [
|
125
|
-
languages.add(
|
126
|
-
|
143
|
+
elif ext in [".sql"]:
|
144
|
+
languages.add("sql")
|
145
|
+
|
127
146
|
# Shell/Bash
|
128
|
-
elif
|
129
|
-
|
130
|
-
|
147
|
+
elif (
|
148
|
+
ext in [".sh", ".bash"]
|
149
|
+
or base_name.startswith(".bash")
|
150
|
+
or base_name.startswith(".zsh")
|
151
|
+
):
|
152
|
+
languages.add("shell")
|
153
|
+
|
131
154
|
# HTML/CSS
|
132
|
-
elif ext in [
|
133
|
-
languages.add(
|
134
|
-
elif ext in [
|
135
|
-
languages.add(
|
136
|
-
|
155
|
+
elif ext in [".html", ".htm", ".xhtml"]:
|
156
|
+
languages.add("html")
|
157
|
+
elif ext in [".css", ".scss", ".sass", ".less"]:
|
158
|
+
languages.add("css")
|
159
|
+
|
137
160
|
# XML/JSON/YAML (config files)
|
138
|
-
elif ext in [
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
161
|
+
elif ext in [
|
162
|
+
".xml",
|
163
|
+
".xsd",
|
164
|
+
".dtd",
|
165
|
+
".tld",
|
166
|
+
".jsp",
|
167
|
+
".jspx",
|
168
|
+
".tag",
|
169
|
+
".tagx",
|
170
|
+
]:
|
171
|
+
languages.add("xml")
|
172
|
+
elif ext in [".json", ".jsonl", ".json5"]:
|
173
|
+
languages.add("json")
|
174
|
+
elif ext in [".yaml", ".yml"]:
|
175
|
+
languages.add("yaml")
|
176
|
+
|
145
177
|
# Markdown/Documentation
|
146
|
-
elif ext in [
|
147
|
-
languages.add(
|
148
|
-
|
178
|
+
elif ext in [".md", ".markdown", ".rst", ".adoc"]:
|
179
|
+
languages.add("markdown")
|
180
|
+
|
149
181
|
# Docker
|
150
|
-
elif ext in [
|
151
|
-
|
152
|
-
|
182
|
+
elif ext in [".dockerfile"] or base_name in [
|
183
|
+
"Dockerfile",
|
184
|
+
"docker-compose.yml",
|
185
|
+
"docker-compose.yaml",
|
186
|
+
]:
|
187
|
+
languages.add("docker")
|
188
|
+
|
153
189
|
# Terraform
|
154
|
-
elif ext in [
|
155
|
-
languages.add(
|
156
|
-
|
190
|
+
elif ext in [".tf", ".tfvars"]:
|
191
|
+
languages.add("terraform")
|
192
|
+
|
157
193
|
# Makefile
|
158
|
-
elif ext in [
|
159
|
-
languages.add(
|
160
|
-
|
194
|
+
elif ext in [".mk"] or base_name == "Makefile":
|
195
|
+
languages.add("makefile")
|
196
|
+
|
161
197
|
# Map to our primary language groups for checklist purposes
|
162
198
|
primary_languages = set()
|
163
199
|
language_mapping = {
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
200
|
+
"c_cpp": "c_cpp",
|
201
|
+
"go": "go",
|
202
|
+
"python": "python",
|
203
|
+
"rust": "rust",
|
204
|
+
"java": "java",
|
205
|
+
"javascript": "javascript",
|
206
|
+
"typescript": "typescript",
|
207
|
+
"php": "php",
|
208
|
+
"ruby": "ruby",
|
209
|
+
"swift": "swift",
|
210
|
+
"kotlin": "kotlin",
|
211
|
+
"csharp": "csharp",
|
212
|
+
"sql": "sql",
|
213
|
+
"shell": "shell",
|
214
|
+
"html": "html",
|
215
|
+
"css": "css",
|
216
|
+
"xml": "xml",
|
217
|
+
"json": "json",
|
218
|
+
"yaml": "yaml",
|
219
|
+
"markdown": "docs",
|
220
|
+
"docker": "docker",
|
221
|
+
"terraform": "terraform",
|
222
|
+
"makefile": "devops",
|
187
223
|
}
|
188
|
-
|
224
|
+
|
189
225
|
# Map detected languages to primary language groups
|
190
226
|
for lang in languages:
|
191
227
|
primary_lang = language_mapping.get(lang)
|
192
228
|
if primary_lang:
|
193
229
|
# Only keep languages we have checklists for
|
194
|
-
if primary_lang in [
|
195
|
-
|
196
|
-
|
197
|
-
|
230
|
+
if primary_lang in [
|
231
|
+
"c_cpp",
|
232
|
+
"go",
|
233
|
+
"python",
|
234
|
+
"rust",
|
235
|
+
"java",
|
236
|
+
"javascript",
|
237
|
+
"typescript",
|
238
|
+
"csharp",
|
239
|
+
"swift",
|
240
|
+
"php",
|
241
|
+
"shell",
|
242
|
+
"sql",
|
243
|
+
"ruby",
|
244
|
+
"kotlin",
|
245
|
+
"html",
|
246
|
+
"css",
|
247
|
+
"xml",
|
248
|
+
"json",
|
249
|
+
"yaml",
|
250
|
+
"docker",
|
251
|
+
"terraform",
|
252
|
+
"docs",
|
253
|
+
"markdown",
|
254
|
+
"devops",
|
255
|
+
"makefile",
|
256
|
+
]:
|
198
257
|
primary_languages.add(primary_lang)
|
199
|
-
|
258
|
+
|
200
259
|
return list(primary_languages)
|
201
|
-
|
260
|
+
|
202
261
|
def _get_language_checklist(self, language: str) -> str:
|
203
262
|
"""Get the checklist for a specific language."""
|
204
263
|
checklist = get_language_checklist(language)
|
@@ -227,104 +286,124 @@ class CodeReviewTool:
|
|
227
286
|
return {
|
228
287
|
"success": False,
|
229
288
|
"stdout": {},
|
230
|
-
"stderr": "commit_sha is required for commit review type"
|
289
|
+
"stderr": "commit_sha is required for commit review type",
|
231
290
|
}
|
232
291
|
commit_sha = args["commit_sha"].strip()
|
233
292
|
diff_cmd = f"git show {commit_sha} | cat -"
|
234
|
-
|
293
|
+
|
235
294
|
# Execute git command and get diff output
|
236
|
-
diff_output = subprocess.check_output(
|
295
|
+
diff_output = subprocess.check_output(
|
296
|
+
diff_cmd, shell=True, text=True
|
297
|
+
)
|
237
298
|
if not diff_output:
|
238
299
|
return {
|
239
300
|
"success": False,
|
240
301
|
"stdout": {},
|
241
|
-
"stderr": "No changes to review"
|
302
|
+
"stderr": "No changes to review",
|
242
303
|
}
|
243
|
-
|
304
|
+
|
244
305
|
# Extract changed files using git command
|
245
306
|
files_cmd = f"git show --name-only --pretty=format: {commit_sha} | grep -v '^$'"
|
246
307
|
try:
|
247
|
-
files_output = subprocess.check_output(
|
248
|
-
|
308
|
+
files_output = subprocess.check_output(
|
309
|
+
files_cmd, shell=True, text=True
|
310
|
+
)
|
311
|
+
file_paths = [
|
312
|
+
f.strip() for f in files_output.split("\n") if f.strip()
|
313
|
+
]
|
249
314
|
except subprocess.CalledProcessError:
|
250
315
|
# Fallback to regex extraction if git command fails
|
251
316
|
file_pattern = r"diff --git a/.*?\s+b/(.*?)(\n|$)"
|
252
317
|
files = re.findall(file_pattern, diff_output)
|
253
318
|
file_paths = [match[0] for match in files]
|
254
|
-
|
319
|
+
|
255
320
|
elif review_type == "range":
|
256
321
|
if "start_commit" not in args or "end_commit" not in args:
|
257
322
|
return {
|
258
323
|
"success": False,
|
259
324
|
"stdout": {},
|
260
|
-
"stderr": "start_commit and end_commit are required for range review type"
|
325
|
+
"stderr": "start_commit and end_commit are required for range review type",
|
261
326
|
}
|
262
327
|
start_commit = args["start_commit"].strip()
|
263
328
|
end_commit = args["end_commit"].strip()
|
264
329
|
diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
|
265
|
-
|
330
|
+
|
266
331
|
# Execute git command and get diff output
|
267
|
-
diff_output = subprocess.check_output(
|
332
|
+
diff_output = subprocess.check_output(
|
333
|
+
diff_cmd, shell=True, text=True
|
334
|
+
)
|
268
335
|
if not diff_output:
|
269
336
|
return {
|
270
337
|
"success": False,
|
271
338
|
"stdout": {},
|
272
|
-
"stderr": "No changes to review"
|
339
|
+
"stderr": "No changes to review",
|
273
340
|
}
|
274
|
-
|
341
|
+
|
275
342
|
# Extract changed files using git command
|
276
343
|
files_cmd = f"git diff --name-only {start_commit}..{end_commit}"
|
277
344
|
try:
|
278
|
-
files_output = subprocess.check_output(
|
279
|
-
|
345
|
+
files_output = subprocess.check_output(
|
346
|
+
files_cmd, shell=True, text=True
|
347
|
+
)
|
348
|
+
file_paths = [
|
349
|
+
f.strip() for f in files_output.split("\n") if f.strip()
|
350
|
+
]
|
280
351
|
except subprocess.CalledProcessError:
|
281
352
|
# Fallback to regex extraction if git command fails
|
282
353
|
file_pattern = r"diff --git a/.*?\s+b/(.*?)(\n|$)"
|
283
354
|
files = re.findall(file_pattern, diff_output)
|
284
355
|
file_paths = [match[0] for match in files]
|
285
|
-
|
356
|
+
|
286
357
|
elif review_type == "file":
|
287
358
|
if "file_path" not in args:
|
288
359
|
return {
|
289
360
|
"success": False,
|
290
361
|
"stdout": {},
|
291
|
-
"stderr": "file_path is required for file review type"
|
362
|
+
"stderr": "file_path is required for file review type",
|
292
363
|
}
|
293
364
|
file_path = args["file_path"].strip()
|
294
365
|
file_paths = [file_path]
|
295
|
-
diff_output = ReadCodeTool().execute(
|
296
|
-
|
366
|
+
diff_output = ReadCodeTool().execute(
|
367
|
+
{"files": [{"path": file_path}]}
|
368
|
+
)["stdout"]
|
369
|
+
|
297
370
|
else: # current changes
|
298
371
|
diff_cmd = "git diff HEAD | cat -"
|
299
|
-
|
372
|
+
|
300
373
|
# Execute git command and get diff output
|
301
|
-
diff_output = subprocess.check_output(
|
374
|
+
diff_output = subprocess.check_output(
|
375
|
+
diff_cmd, shell=True, text=True
|
376
|
+
)
|
302
377
|
if not diff_output:
|
303
378
|
return {
|
304
379
|
"success": False,
|
305
380
|
"stdout": {},
|
306
|
-
"stderr": "No changes to review"
|
381
|
+
"stderr": "No changes to review",
|
307
382
|
}
|
308
|
-
|
383
|
+
|
309
384
|
# Extract changed files using git command
|
310
385
|
files_cmd = "git diff --name-only HEAD"
|
311
386
|
try:
|
312
|
-
files_output = subprocess.check_output(
|
313
|
-
|
387
|
+
files_output = subprocess.check_output(
|
388
|
+
files_cmd, shell=True, text=True
|
389
|
+
)
|
390
|
+
file_paths = [
|
391
|
+
f.strip() for f in files_output.split("\n") if f.strip()
|
392
|
+
]
|
314
393
|
except subprocess.CalledProcessError:
|
315
394
|
# Fallback to regex extraction if git command fails
|
316
395
|
file_pattern = r"diff --git a/.*?\s+b/(.*?)(\n|$)"
|
317
396
|
files = re.findall(file_pattern, diff_output)
|
318
397
|
file_paths = [match[0] for match in files]
|
319
|
-
|
398
|
+
|
320
399
|
# Detect languages from the file paths
|
321
400
|
detected_languages = self._detect_languages_from_files(file_paths)
|
322
|
-
|
401
|
+
|
323
402
|
# Add review type and related information to the diff output
|
324
403
|
review_info = f"""
|
325
404
|
----- 代码审查信息 -----
|
326
405
|
审查类型: {review_type}"""
|
327
|
-
|
406
|
+
|
328
407
|
# Add specific information based on review type
|
329
408
|
if review_type == "commit":
|
330
409
|
review_info += f"\n提交SHA: {args['commit_sha']}"
|
@@ -334,29 +413,31 @@ class CodeReviewTool:
|
|
334
413
|
review_info += f"\n文件路径: {args['file_path']}"
|
335
414
|
else: # current changes
|
336
415
|
review_info += "\n当前未提交修改"
|
337
|
-
|
416
|
+
|
338
417
|
# Add file list
|
339
418
|
if file_paths:
|
340
419
|
review_info += "\n\n----- 变更文件列表 -----"
|
341
420
|
for i, path in enumerate(file_paths, 1):
|
342
421
|
review_info += f"\n{i}. {path}"
|
343
|
-
|
422
|
+
|
344
423
|
# Add language-specific checklists
|
345
424
|
if detected_languages:
|
346
425
|
review_info += "\n\n----- 检测到的编程语言 -----"
|
347
|
-
review_info +=
|
348
|
-
|
426
|
+
review_info += (
|
427
|
+
f"\n检测到的语言: {', '.join(detected_languages)}"
|
428
|
+
)
|
429
|
+
|
349
430
|
review_info += "\n\n----- 语言特定审查清单 -----"
|
350
431
|
for lang in detected_languages:
|
351
432
|
checklist = self._get_language_checklist(lang)
|
352
433
|
if checklist:
|
353
434
|
review_info += f"\n{checklist}"
|
354
|
-
|
435
|
+
|
355
436
|
review_info += "\n------------------------\n\n"
|
356
|
-
|
437
|
+
|
357
438
|
# Combine review info with diff output
|
358
439
|
diff_output = review_info + diff_output
|
359
|
-
|
440
|
+
|
360
441
|
PrettyOutput.print(diff_output, OutputType.CODE, lang="diff")
|
361
442
|
spinner.text = "代码变更获取完成"
|
362
443
|
spinner.ok("✅")
|
@@ -490,6 +571,7 @@ class CodeReviewTool:
|
|
490
571
|
我将分析上传的代码差异文件,进行全面的代码审查。
|
491
572
|
</code_review_guide>"""
|
492
573
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
574
|
+
|
493
575
|
tool_registry = ToolRegistry()
|
494
576
|
tool_registry.dont_use_tools(["code_review"])
|
495
577
|
agent = Agent(
|
@@ -561,16 +643,22 @@ class CodeReviewTool:
|
|
561
643
|
{ct("REPORT")}""",
|
562
644
|
output_handler=[tool_registry],
|
563
645
|
platform=PlatformRegistry().get_thinking_platform(),
|
564
|
-
auto_complete=False
|
646
|
+
auto_complete=False,
|
565
647
|
)
|
566
|
-
|
648
|
+
|
567
649
|
# Determine if we need to split the diff due to size
|
568
650
|
max_diff_size = 100 * 1024 * 1024 # Limit to 100MB
|
569
|
-
|
651
|
+
|
570
652
|
if len(diff_output) > max_diff_size:
|
571
|
-
PrettyOutput.print(
|
572
|
-
|
573
|
-
|
653
|
+
PrettyOutput.print(
|
654
|
+
f"代码差异内容总大小超过限制 ({len(diff_output)} > {max_diff_size} 字节),将截断内容",
|
655
|
+
OutputType.WARNING,
|
656
|
+
)
|
657
|
+
diff_output = (
|
658
|
+
diff_output[:max_diff_size]
|
659
|
+
+ "\n\n[diff content truncated due to size limitations...]"
|
660
|
+
)
|
661
|
+
|
574
662
|
# Prepare the user prompt for code review
|
575
663
|
user_prompt = f"""请对以下代码变更进行全面审查。
|
576
664
|
|
@@ -580,43 +668,50 @@ class CodeReviewTool:
|
|
580
668
|
- 检测到的编程语言: {', '.join(detected_languages) if detected_languages else '未检测到特定语言'}
|
581
669
|
|
582
670
|
请根据SCRIPPPS框架和语言特定的审查清单进行分析,提供详细的代码审查报告。"""
|
583
|
-
|
671
|
+
|
584
672
|
# Write the full diff output to a temporary file for uploading
|
585
|
-
with tempfile.NamedTemporaryFile(
|
673
|
+
with tempfile.NamedTemporaryFile(
|
674
|
+
mode="w", suffix=".diff", delete=False
|
675
|
+
) as temp_file:
|
586
676
|
temp_file_path = temp_file.name
|
587
677
|
temp_file.write(diff_output)
|
588
678
|
temp_file.flush()
|
589
|
-
|
679
|
+
|
590
680
|
try:
|
591
681
|
# Check if content is too large
|
592
682
|
is_large_content = is_context_overflow(diff_output)
|
593
|
-
|
683
|
+
|
594
684
|
# Upload the file to the agent's model
|
595
685
|
if is_large_content:
|
596
686
|
if not agent.model or not agent.model.support_upload_files():
|
597
687
|
return {
|
598
688
|
"success": False,
|
599
689
|
"stdout": "",
|
600
|
-
"stderr": "代码差异太大,无法处理"
|
690
|
+
"stderr": "代码差异太大,无法处理",
|
601
691
|
}
|
602
|
-
|
603
|
-
with yaspin(
|
692
|
+
|
693
|
+
with yaspin(
|
694
|
+
text="正在上传代码差异文件...", color="cyan"
|
695
|
+
) as spinner:
|
604
696
|
upload_success = agent.model.upload_files([temp_file_path])
|
605
697
|
if upload_success:
|
606
698
|
spinner.ok("✅")
|
607
|
-
PrettyOutput.print(
|
699
|
+
PrettyOutput.print(
|
700
|
+
f"已成功上传代码差异文件", OutputType.SUCCESS
|
701
|
+
)
|
608
702
|
else:
|
609
703
|
return {
|
610
704
|
"success": False,
|
611
705
|
"stdout": "",
|
612
|
-
"stderr": "上传代码差异文件失败"
|
706
|
+
"stderr": "上传代码差异文件失败",
|
613
707
|
}
|
614
|
-
|
615
|
-
|
708
|
+
|
616
709
|
# Prepare the prompt based on upload status
|
617
710
|
if is_large_content:
|
618
711
|
# When file is uploaded, reference it in the prompt
|
619
|
-
complete_prompt =
|
712
|
+
complete_prompt = (
|
713
|
+
user_prompt
|
714
|
+
+ f"""
|
620
715
|
|
621
716
|
我已上传了一个包含代码差异的文件。该文件包含:
|
622
717
|
- 审查类型: {review_type}
|
@@ -624,10 +719,16 @@ class CodeReviewTool:
|
|
624
719
|
- 检测到的编程语言: {', '.join(detected_languages) if detected_languages else '未检测到特定语言'}
|
625
720
|
|
626
721
|
请基于上传的代码差异文件进行全面审查,并生成详细的代码审查报告。"""
|
722
|
+
)
|
627
723
|
# Run the agent with the prompt
|
628
724
|
result = agent.run(complete_prompt)
|
629
725
|
else:
|
630
|
-
complete_prompt =
|
726
|
+
complete_prompt = (
|
727
|
+
user_prompt
|
728
|
+
+ "\n\n代码差异内容:\n```diff\n"
|
729
|
+
+ diff_output
|
730
|
+
+ "\n```"
|
731
|
+
)
|
631
732
|
result = agent.run(complete_prompt)
|
632
733
|
finally:
|
633
734
|
# Clean up the temporary file
|
@@ -635,13 +736,12 @@ class CodeReviewTool:
|
|
635
736
|
try:
|
636
737
|
os.unlink(temp_file_path)
|
637
738
|
except Exception:
|
638
|
-
PrettyOutput.print(
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
}
|
739
|
+
PrettyOutput.print(
|
740
|
+
f"临时文件 {temp_file_path} 未能删除",
|
741
|
+
OutputType.WARNING,
|
742
|
+
)
|
743
|
+
|
744
|
+
return {"success": True, "stdout": result, "stderr": ""}
|
645
745
|
finally:
|
646
746
|
# Always restore original directory
|
647
747
|
os.chdir(original_dir)
|
@@ -650,59 +750,59 @@ class CodeReviewTool:
|
|
650
750
|
return {
|
651
751
|
"success": False,
|
652
752
|
"stdout": {},
|
653
|
-
"stderr": f"Review failed: {str(e)}"
|
753
|
+
"stderr": f"Review failed: {str(e)}",
|
654
754
|
}
|
655
755
|
|
656
756
|
|
657
757
|
def extract_code_report(result: str) -> str:
|
658
|
-
sm = re.search(ot("REPORT")+r
|
758
|
+
sm = re.search(ot("REPORT") + r"\n(.*?)\n" + ct("REPORT"), result, re.DOTALL)
|
659
759
|
if sm:
|
660
760
|
return sm.group(1)
|
661
761
|
return result
|
662
762
|
|
763
|
+
|
663
764
|
def main():
|
664
765
|
"""CLI entry point"""
|
665
766
|
import argparse
|
666
767
|
|
667
768
|
init_env("欢迎使用 Jarvis-CodeReview,您的代码审查助手已准备就绪!")
|
668
769
|
|
669
|
-
parser = argparse.ArgumentParser(description=
|
670
|
-
subparsers = parser.add_subparsers(dest=
|
770
|
+
parser = argparse.ArgumentParser(description="Autonomous code review tool")
|
771
|
+
subparsers = parser.add_subparsers(dest="type")
|
671
772
|
|
672
773
|
# Commit subcommand
|
673
|
-
commit_parser = subparsers.add_parser(
|
674
|
-
commit_parser.add_argument(
|
774
|
+
commit_parser = subparsers.add_parser("commit", help="Review specific commit")
|
775
|
+
commit_parser.add_argument("commit", help="Commit SHA to review")
|
675
776
|
|
676
777
|
# Current subcommand
|
677
|
-
|
778
|
+
subparsers.add_parser("current", help="Review current changes")
|
678
779
|
|
679
780
|
# Range subcommand
|
680
|
-
range_parser = subparsers.add_parser(
|
681
|
-
range_parser.add_argument(
|
682
|
-
range_parser.add_argument(
|
781
|
+
range_parser = subparsers.add_parser("range", help="Review commit range")
|
782
|
+
range_parser.add_argument("start_commit", help="Start commit SHA")
|
783
|
+
range_parser.add_argument("end_commit", help="End commit SHA")
|
683
784
|
|
684
785
|
# File subcommand
|
685
|
-
file_parser = subparsers.add_parser(
|
686
|
-
file_parser.add_argument(
|
786
|
+
file_parser = subparsers.add_parser("file", help="Review specific file")
|
787
|
+
file_parser.add_argument("file", help="File path to review")
|
687
788
|
|
688
789
|
# Common arguments
|
689
|
-
parser.add_argument(
|
790
|
+
parser.add_argument(
|
791
|
+
"--root-dir", type=str, help="Root directory of the codebase", default="."
|
792
|
+
)
|
690
793
|
|
691
794
|
# Set default subcommand to 'current'
|
692
|
-
parser.set_defaults(type=
|
795
|
+
parser.set_defaults(type="current")
|
693
796
|
args = parser.parse_args()
|
694
797
|
|
695
798
|
tool = CodeReviewTool()
|
696
|
-
tool_args = {
|
697
|
-
|
698
|
-
"root_dir": args.root_dir
|
699
|
-
}
|
700
|
-
if args.type == 'commit':
|
799
|
+
tool_args = {"review_type": args.type, "root_dir": args.root_dir}
|
800
|
+
if args.type == "commit":
|
701
801
|
tool_args["commit_sha"] = args.commit
|
702
|
-
elif args.type ==
|
802
|
+
elif args.type == "range":
|
703
803
|
tool_args["start_commit"] = args.start_commit
|
704
804
|
tool_args["end_commit"] = args.end_commit
|
705
|
-
elif args.type ==
|
805
|
+
elif args.type == "file":
|
706
806
|
tool_args["file_path"] = args.file
|
707
807
|
|
708
808
|
result = tool.execute(tool_args)
|
@@ -715,5 +815,6 @@ def main():
|
|
715
815
|
else:
|
716
816
|
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
717
817
|
|
818
|
+
|
718
819
|
if __name__ == "__main__":
|
719
820
|
main()
|