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.
Files changed (92) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +45 -41
  3. jarvis/jarvis_agent/builtin_input_handler.py +26 -4
  4. jarvis/jarvis_agent/jarvis.py +30 -19
  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 +81 -79
  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 +31 -29
  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 +292 -190
  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 +15 -5
  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 +71 -39
  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 +44 -18
  44. jarvis/jarvis_platform/human.py +15 -3
  45. jarvis/jarvis_platform/kimi.py +117 -81
  46. jarvis/jarvis_platform/openai.py +23 -28
  47. jarvis/jarvis_platform/registry.py +43 -29
  48. jarvis/jarvis_platform/tongyi.py +16 -10
  49. jarvis/jarvis_platform/yuanbao.py +197 -144
  50. jarvis/jarvis_platform_manager/main.py +4 -2
  51. jarvis/jarvis_smart_shell/main.py +35 -30
  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 +6 -11
  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 +35 -35
  66. jarvis/jarvis_tools/read_webpage.py +7 -16
  67. jarvis/jarvis_tools/registry.py +63 -30
  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 +9 -9
  73. jarvis/jarvis_utils/config.py +60 -37
  74. jarvis/jarvis_utils/embedding.py +24 -19
  75. jarvis/jarvis_utils/file_processors.py +16 -9
  76. jarvis/jarvis_utils/git_utils.py +157 -107
  77. jarvis/jarvis_utils/globals.py +1 -1
  78. jarvis/jarvis_utils/input.py +85 -52
  79. jarvis/jarvis_utils/jarvis_history.py +43 -0
  80. jarvis/jarvis_utils/methodology.py +31 -24
  81. jarvis/jarvis_utils/output.py +164 -80
  82. jarvis/jarvis_utils/tag.py +2 -1
  83. jarvis/jarvis_utils/utils.py +84 -52
  84. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/METADATA +362 -230
  85. jarvis_ai_assistant-0.1.195.dist-info/RECORD +98 -0
  86. jarvis/jarvis_agent/file_input_handler.py +0 -112
  87. jarvis/jarvis_event/__init__.py +0 -0
  88. jarvis_ai_assistant-0.1.193.dist-info/RECORD +0 -99
  89. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/WHEEL +0 -0
  90. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/entry_points.txt +0 -0
  91. {jarvis_ai_assistant-0.1.193.dist-info → jarvis_ai_assistant-0.1.195.dist-info}/licenses/LICENSE +0 -0
  92. {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 = ['code', 'analysis', 'review']
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 ['.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.inl', '.ipp']:
77
- languages.add('c_cpp')
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 ['.go']:
81
- languages.add('go')
82
-
90
+ elif ext in [".go"]:
91
+ languages.add("go")
92
+
83
93
  # 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
-
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 ['.rs', '.rlib'] or base_name in ['Cargo.toml', 'Cargo.lock']:
89
- languages.add('rust')
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 ['.java', '.class', '.jar'] or base_name in ['pom.xml', 'build.gradle']:
93
- languages.add('java')
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 ['.js', '.mjs', '.cjs', '.jsx']:
97
- languages.add('javascript')
98
-
113
+ elif ext in [".js", ".mjs", ".cjs", ".jsx"]:
114
+ languages.add("javascript")
115
+
99
116
  # TypeScript
100
- elif ext in ['.ts', '.tsx', '.cts', '.mts']:
101
- languages.add('typescript')
102
-
117
+ elif ext in [".ts", ".tsx", ".cts", ".mts"]:
118
+ languages.add("typescript")
119
+
103
120
  # PHP
104
- elif ext in ['.php', '.phtml', '.php5', '.php7', '.phps']:
105
- languages.add('php')
106
-
121
+ elif ext in [".php", ".phtml", ".php5", ".php7", ".phps"]:
122
+ languages.add("php")
123
+
107
124
  # Ruby
108
- elif ext in ['.rb', '.rake', '.gemspec'] or base_name in ['Gemfile', 'Rakefile']:
109
- languages.add('ruby')
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 ['.swift']:
113
- languages.add('swift')
114
-
132
+ elif ext in [".swift"]:
133
+ languages.add("swift")
134
+
115
135
  # Kotlin
116
- elif ext in ['.kt', '.kts']:
117
- languages.add('kotlin')
118
-
136
+ elif ext in [".kt", ".kts"]:
137
+ languages.add("kotlin")
138
+
119
139
  # C#
120
- elif ext in ['.cs', '.csx']:
121
- languages.add('csharp')
122
-
140
+ elif ext in [".cs", ".csx"]:
141
+ languages.add("csharp")
142
+
123
143
  # SQL
124
- elif ext in ['.sql']:
125
- languages.add('sql')
126
-
144
+ elif ext in [".sql"]:
145
+ languages.add("sql")
146
+
127
147
  # Shell/Bash
128
- elif ext in ['.sh', '.bash'] or base_name.startswith('.bash') or base_name.startswith('.zsh'):
129
- languages.add('shell')
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 ['.html', '.htm', '.xhtml']:
133
- languages.add('html')
134
- elif ext in ['.css', '.scss', '.sass', '.less']:
135
- languages.add('css')
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 ['.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
-
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 ['.md', '.markdown', '.rst', '.adoc']:
147
- languages.add('markdown')
148
-
179
+ elif ext in [".md", ".markdown", ".rst", ".adoc"]:
180
+ languages.add("markdown")
181
+
149
182
  # Docker
150
- elif ext in ['.dockerfile'] or base_name in ['Dockerfile', 'docker-compose.yml', 'docker-compose.yaml']:
151
- languages.add('docker')
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 ['.tf', '.tfvars']:
155
- languages.add('terraform')
156
-
191
+ elif ext in [".tf", ".tfvars"]:
192
+ languages.add("terraform")
193
+
157
194
  # Makefile
158
- elif ext in ['.mk'] or base_name == 'Makefile':
159
- languages.add('makefile')
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
- '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'
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 ['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']:
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
248
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
279
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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({"files": [{"path": file_path}]})["stdout"]
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(diff_cmd, shell=True, text=True)
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(files_cmd, shell=True, text=True)
313
- file_paths = [f.strip() for f in files_output.split("\n") if f.strip()]
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 += f"\n检测到的语言: {', '.join(detected_languages)}"
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(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
-
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(mode='w', suffix='.diff', delete=False) as temp_file:
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(text="正在上传代码差异文件...", color="cyan") as spinner:
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(f"已成功上传代码差异文件", OutputType.SUCCESS)
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 = user_prompt + f"""
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 = user_prompt + "\n\n代码差异内容:\n```diff\n" + diff_output + "\n```"
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(f"临时文件 {temp_file_path} 未能删除", OutputType.WARNING)
639
-
640
- return {
641
- "success": True,
642
- "stdout": result,
643
- "stderr": ""
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'\n(.*?)\n'+ct("REPORT"), result, re.DOTALL)
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='Autonomous code review tool')
670
- subparsers = parser.add_subparsers(dest='type')
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('commit', help='Review specific commit')
674
- commit_parser.add_argument('commit', help='Commit SHA to review')
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
- current_parser = subparsers.add_parser('current', help='Review current changes')
779
+ subparsers.add_parser("current", help="Review current changes")
678
780
 
679
781
  # 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')
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('file', help='Review specific file')
686
- file_parser.add_argument('file', help='File path to review')
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('--root-dir', type=str, help='Root directory of the codebase', default=".")
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='current')
796
+ parser.set_defaults(type="current")
693
797
  args = parser.parse_args()
694
798
 
695
799
  tool = CodeReviewTool()
696
- tool_args = {
697
- "review_type": args.type,
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 == 'range':
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 == 'file':
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()