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.
Files changed (91) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +69 -37
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +38 -22
  5. jarvis/jarvis_agent/main.py +20 -12
  6. jarvis/jarvis_agent/output_handler.py +7 -7
  7. jarvis/jarvis_agent/shell_input_handler.py +14 -11
  8. jarvis/jarvis_code_agent/code_agent.py +93 -90
  9. jarvis/jarvis_code_agent/lint.py +92 -105
  10. jarvis/jarvis_code_analysis/checklists/__init__.py +1 -1
  11. jarvis/jarvis_code_analysis/checklists/c_cpp.py +1 -1
  12. jarvis/jarvis_code_analysis/checklists/csharp.py +1 -1
  13. jarvis/jarvis_code_analysis/checklists/data_format.py +1 -1
  14. jarvis/jarvis_code_analysis/checklists/devops.py +1 -1
  15. jarvis/jarvis_code_analysis/checklists/docs.py +1 -1
  16. jarvis/jarvis_code_analysis/checklists/go.py +1 -1
  17. jarvis/jarvis_code_analysis/checklists/infrastructure.py +1 -1
  18. jarvis/jarvis_code_analysis/checklists/java.py +1 -1
  19. jarvis/jarvis_code_analysis/checklists/javascript.py +1 -1
  20. jarvis/jarvis_code_analysis/checklists/kotlin.py +1 -1
  21. jarvis/jarvis_code_analysis/checklists/loader.py +51 -35
  22. jarvis/jarvis_code_analysis/checklists/php.py +1 -1
  23. jarvis/jarvis_code_analysis/checklists/python.py +1 -1
  24. jarvis/jarvis_code_analysis/checklists/ruby.py +1 -1
  25. jarvis/jarvis_code_analysis/checklists/rust.py +1 -1
  26. jarvis/jarvis_code_analysis/checklists/shell.py +1 -1
  27. jarvis/jarvis_code_analysis/checklists/sql.py +1 -1
  28. jarvis/jarvis_code_analysis/checklists/swift.py +1 -1
  29. jarvis/jarvis_code_analysis/checklists/web.py +1 -1
  30. jarvis/jarvis_code_analysis/code_review.py +293 -192
  31. jarvis/jarvis_dev/main.py +73 -56
  32. jarvis/jarvis_git_details/main.py +29 -33
  33. jarvis/jarvis_git_squash/main.py +13 -11
  34. jarvis/jarvis_git_utils/git_commiter.py +12 -3
  35. jarvis/jarvis_mcp/__init__.py +8 -10
  36. jarvis/jarvis_mcp/sse_mcp_client.py +182 -205
  37. jarvis/jarvis_mcp/stdio_mcp_client.py +93 -120
  38. jarvis/jarvis_mcp/streamable_mcp_client.py +117 -142
  39. jarvis/jarvis_methodology/main.py +81 -47
  40. jarvis/jarvis_multi_agent/__init__.py +24 -16
  41. jarvis/jarvis_multi_agent/main.py +10 -4
  42. jarvis/jarvis_platform/__init__.py +1 -1
  43. jarvis/jarvis_platform/base.py +49 -21
  44. jarvis/jarvis_platform/human.py +5 -3
  45. jarvis/jarvis_platform/kimi.py +96 -72
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +50 -33
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +205 -147
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -29
  52. jarvis/jarvis_tools/ask_user.py +8 -16
  53. jarvis/jarvis_tools/base.py +3 -2
  54. jarvis/jarvis_tools/chdir.py +7 -19
  55. jarvis/jarvis_tools/cli/main.py +14 -10
  56. jarvis/jarvis_tools/code_plan.py +10 -31
  57. jarvis/jarvis_tools/create_code_agent.py +10 -13
  58. jarvis/jarvis_tools/create_sub_agent.py +10 -22
  59. jarvis/jarvis_tools/edit_file.py +98 -76
  60. jarvis/jarvis_tools/execute_script.py +46 -46
  61. jarvis/jarvis_tools/file_analyzer.py +22 -34
  62. jarvis/jarvis_tools/file_operation.py +69 -62
  63. jarvis/jarvis_tools/generate_new_tool.py +0 -2
  64. jarvis/jarvis_tools/methodology.py +19 -23
  65. jarvis/jarvis_tools/read_code.py +42 -33
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +65 -32
  68. jarvis/jarvis_tools/rewrite_file.py +26 -29
  69. jarvis/jarvis_tools/search_web.py +5 -8
  70. jarvis/jarvis_tools/virtual_tty.py +133 -122
  71. jarvis/jarvis_utils/__init__.py +0 -1
  72. jarvis/jarvis_utils/builtin_replace_map.py +96 -8
  73. jarvis/jarvis_utils/config.py +59 -32
  74. jarvis/jarvis_utils/embedding.py +17 -14
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +140 -99
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +84 -52
  79. jarvis/jarvis_utils/methodology.py +28 -21
  80. jarvis/jarvis_utils/output.py +159 -78
  81. jarvis/jarvis_utils/tag.py +2 -1
  82. jarvis/jarvis_utils/utils.py +85 -51
  83. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/METADATA +337 -204
  84. jarvis_ai_assistant-0.1.194.dist-info/RECORD +97 -0
  85. jarvis/jarvis_agent/file_input_handler.py +0 -112
  86. jarvis/jarvis_event/__init__.py +0 -0
  87. jarvis_ai_assistant-0.1.192.dist-info/RECORD +0 -99
  88. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/WHEEL +0 -0
  89. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/entry_points.txt +0 -0
  90. {jarvis_ai_assistant-0.1.192.dist-info → jarvis_ai_assistant-0.1.194.dist-info}/licenses/LICENSE +0 -0
  91. {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 = ['code', 'analysis', 'review']
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 ['.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.inl', '.ipp']:
77
- languages.add('c_cpp')
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 ['.go']:
81
- languages.add('go')
82
-
89
+ elif ext in [".go"]:
90
+ languages.add("go")
91
+
83
92
  # Python
84
- elif ext in ['.py', '.pyw', '.pyi', '.pyx', '.pxd'] or base_name in ['requirements.txt', 'setup.py', 'pyproject.toml']:
85
- languages.add('python')
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 ['.rs', '.rlib'] or base_name in ['Cargo.toml', 'Cargo.lock']:
89
- languages.add('rust')
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 ['.java', '.class', '.jar'] or base_name in ['pom.xml', 'build.gradle']:
93
- languages.add('java')
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 ['.js', '.mjs', '.cjs', '.jsx']:
97
- languages.add('javascript')
98
-
112
+ elif ext in [".js", ".mjs", ".cjs", ".jsx"]:
113
+ languages.add("javascript")
114
+
99
115
  # TypeScript
100
- elif ext in ['.ts', '.tsx', '.cts', '.mts']:
101
- languages.add('typescript')
102
-
116
+ elif ext in [".ts", ".tsx", ".cts", ".mts"]:
117
+ languages.add("typescript")
118
+
103
119
  # PHP
104
- elif ext in ['.php', '.phtml', '.php5', '.php7', '.phps']:
105
- languages.add('php')
106
-
120
+ elif ext in [".php", ".phtml", ".php5", ".php7", ".phps"]:
121
+ languages.add("php")
122
+
107
123
  # Ruby
108
- elif ext in ['.rb', '.rake', '.gemspec'] or base_name in ['Gemfile', 'Rakefile']:
109
- languages.add('ruby')
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 ['.swift']:
113
- languages.add('swift')
114
-
131
+ elif ext in [".swift"]:
132
+ languages.add("swift")
133
+
115
134
  # Kotlin
116
- elif ext in ['.kt', '.kts']:
117
- languages.add('kotlin')
118
-
135
+ elif ext in [".kt", ".kts"]:
136
+ languages.add("kotlin")
137
+
119
138
  # C#
120
- elif ext in ['.cs', '.csx']:
121
- languages.add('csharp')
122
-
139
+ elif ext in [".cs", ".csx"]:
140
+ languages.add("csharp")
141
+
123
142
  # SQL
124
- elif ext in ['.sql']:
125
- languages.add('sql')
126
-
143
+ elif ext in [".sql"]:
144
+ languages.add("sql")
145
+
127
146
  # Shell/Bash
128
- elif ext in ['.sh', '.bash'] or base_name.startswith('.bash') or base_name.startswith('.zsh'):
129
- languages.add('shell')
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 ['.html', '.htm', '.xhtml']:
133
- languages.add('html')
134
- elif ext in ['.css', '.scss', '.sass', '.less']:
135
- languages.add('css')
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 ['.xml', '.xsd', '.dtd', '.tld', '.jsp', '.jspx', '.tag', '.tagx']:
139
- languages.add('xml')
140
- elif ext in ['.json', '.jsonl', '.json5']:
141
- languages.add('json')
142
- elif ext in ['.yaml', '.yml']:
143
- languages.add('yaml')
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 ['.md', '.markdown', '.rst', '.adoc']:
147
- languages.add('markdown')
148
-
178
+ elif ext in [".md", ".markdown", ".rst", ".adoc"]:
179
+ languages.add("markdown")
180
+
149
181
  # Docker
150
- elif ext in ['.dockerfile'] or base_name in ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml']:
151
- languages.add('docker')
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 ['.tf', '.tfvars']:
155
- languages.add('terraform')
156
-
190
+ elif ext in [".tf", ".tfvars"]:
191
+ languages.add("terraform")
192
+
157
193
  # Makefile
158
- elif ext in ['.mk'] or base_name == 'Makefile':
159
- languages.add('makefile')
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
- 'c_cpp': 'c_cpp',
165
- 'go': 'go',
166
- 'python': 'python',
167
- 'rust': 'rust',
168
- 'java': 'java',
169
- 'javascript': 'javascript',
170
- 'typescript': 'typescript',
171
- 'php': 'php',
172
- 'ruby': 'ruby',
173
- 'swift': 'swift',
174
- 'kotlin': 'kotlin',
175
- 'csharp': 'csharp',
176
- 'sql': 'sql',
177
- 'shell': 'shell',
178
- 'html': 'html',
179
- 'css': 'css',
180
- 'xml': 'xml',
181
- 'json': 'json',
182
- 'yaml': 'yaml',
183
- 'markdown': 'docs',
184
- 'docker': 'docker',
185
- 'terraform': 'terraform',
186
- 'makefile': 'devops'
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 ['c_cpp', 'go', 'python', 'rust', 'java', 'javascript', 'typescript',
195
- 'csharp', 'swift', 'php', 'shell', 'sql', 'ruby', 'kotlin',
196
- 'html', 'css', 'xml', 'json', 'yaml', 'docker', 'terraform',
197
- 'docs', 'markdown', 'devops', 'makefile']:
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
248
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
279
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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({"files": [{"path": file_path}]})["stdout"]
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
313
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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 += f"\n检测到的语言: {', '.join(detected_languages)}"
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(f"代码差异内容总大小超过限制 ({len(diff_output)} > {max_diff_size} 字节),将截断内容", OutputType.WARNING)
572
- diff_output = diff_output[:max_diff_size] + "\n\n[diff content truncated due to size limitations...]"
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(mode='w', suffix='.diff', delete=False) as temp_file:
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(text="正在上传代码差异文件...", color="cyan") as spinner:
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(f"已成功上传代码差异文件", OutputType.SUCCESS)
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 = user_prompt + f"""
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 = user_prompt + "\n\n代码差异内容:\n```diff\n" + diff_output + "\n```"
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(f"临时文件 {temp_file_path} 未能删除", OutputType.WARNING)
639
-
640
- return {
641
- "success": True,
642
- "stdout": result,
643
- "stderr": ""
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'\n(.*?)\n'+ct("REPORT"), result, re.DOTALL)
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='Autonomous code review tool')
670
- subparsers = parser.add_subparsers(dest='type')
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('commit', help='Review specific commit')
674
- commit_parser.add_argument('commit', help='Commit SHA to review')
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
- current_parser = subparsers.add_parser('current', help='Review current changes')
778
+ subparsers.add_parser("current", help="Review current changes")
678
779
 
679
780
  # Range subcommand
680
- range_parser = subparsers.add_parser('range', help='Review commit range')
681
- range_parser.add_argument('start_commit', help='Start commit SHA')
682
- range_parser.add_argument('end_commit', help='End commit SHA')
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('file', help='Review specific file')
686
- file_parser.add_argument('file', help='File path to review')
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('--root-dir', type=str, help='Root directory of the codebase', default=".")
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='current')
795
+ parser.set_defaults(type="current")
693
796
  args = parser.parse_args()
694
797
 
695
798
  tool = CodeReviewTool()
696
- tool_args = {
697
- "review_type": args.type,
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 == 'range':
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 == 'file':
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()