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