jarvis-ai-assistant 0.1.130__py3-none-any.whl → 0.1.131__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

Files changed (60) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +23 -9
  3. jarvis/jarvis_agent/builtin_input_handler.py +73 -0
  4. jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
  5. jarvis/jarvis_agent/main.py +1 -1
  6. jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +23 -19
  7. jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +0 -1
  8. jarvis/jarvis_code_agent/code_agent.py +20 -16
  9. jarvis/jarvis_codebase/main.py +5 -5
  10. jarvis/jarvis_dev/main.py +1 -1
  11. jarvis/jarvis_git_squash/main.py +1 -1
  12. jarvis/jarvis_lsp/base.py +2 -26
  13. jarvis/jarvis_lsp/cpp.py +2 -14
  14. jarvis/jarvis_lsp/go.py +0 -13
  15. jarvis/jarvis_lsp/python.py +1 -30
  16. jarvis/jarvis_lsp/registry.py +10 -14
  17. jarvis/jarvis_lsp/rust.py +0 -12
  18. jarvis/jarvis_multi_agent/__init__.py +1 -1
  19. jarvis/jarvis_platform/registry.py +1 -1
  20. jarvis/jarvis_platform_manager/main.py +3 -3
  21. jarvis/jarvis_rag/main.py +1 -1
  22. jarvis/jarvis_tools/ask_codebase.py +40 -20
  23. jarvis/jarvis_tools/code_review.py +180 -143
  24. jarvis/jarvis_tools/create_code_agent.py +76 -72
  25. jarvis/jarvis_tools/create_sub_agent.py +32 -15
  26. jarvis/jarvis_tools/execute_shell.py +2 -2
  27. jarvis/jarvis_tools/execute_shell_script.py +1 -1
  28. jarvis/jarvis_tools/file_operation.py +2 -2
  29. jarvis/jarvis_tools/git_commiter.py +87 -68
  30. jarvis/jarvis_tools/lsp_find_definition.py +83 -67
  31. jarvis/jarvis_tools/lsp_find_references.py +62 -46
  32. jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
  33. jarvis/jarvis_tools/methodology.py +3 -3
  34. jarvis/jarvis_tools/read_code.py +1 -1
  35. jarvis/jarvis_tools/search_web.py +18 -20
  36. jarvis/jarvis_tools/tool_generator.py +1 -1
  37. jarvis/jarvis_tools/treesitter_analyzer.py +331 -0
  38. jarvis/jarvis_treesitter/README.md +104 -0
  39. jarvis/jarvis_treesitter/__init__.py +20 -0
  40. jarvis/jarvis_treesitter/database.py +258 -0
  41. jarvis/jarvis_treesitter/example.py +115 -0
  42. jarvis/jarvis_treesitter/grammar_builder.py +182 -0
  43. jarvis/jarvis_treesitter/language.py +117 -0
  44. jarvis/jarvis_treesitter/symbol.py +31 -0
  45. jarvis/jarvis_treesitter/tools_usage.md +121 -0
  46. jarvis/jarvis_utils/git_utils.py +10 -2
  47. jarvis/jarvis_utils/input.py +3 -1
  48. jarvis/jarvis_utils/methodology.py +1 -1
  49. jarvis/jarvis_utils/utils.py +3 -3
  50. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/METADATA +2 -4
  51. jarvis_ai_assistant-0.1.131.dist-info/RECORD +85 -0
  52. jarvis/jarvis_c2rust/c2rust.yaml +0 -734
  53. jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
  54. jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
  55. jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
  56. jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
  57. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/LICENSE +0 -0
  58. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/WHEEL +0 -0
  59. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/entry_points.txt +0 -0
  60. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.131.dist-info}/top_level.txt +0 -0
@@ -11,9 +11,7 @@ REQUIRED_METHODS = [
11
11
  ('initialize', ['workspace_path']),
12
12
  ('find_references', ['file_path', 'position']),
13
13
  ('find_definition', ['file_path', 'position']),
14
- ('get_document_symbols', ['file_path']),
15
14
  ('get_diagnostics', ['file_path']),
16
- ('prepare_rename', ['file_path', 'position']),
17
15
  ('shutdown', [])
18
16
  ]
19
17
 
@@ -29,7 +27,7 @@ class LSPRegistry:
29
27
  if not os.path.exists(user_lsp_dir):
30
28
  try:
31
29
  os.makedirs(user_lsp_dir)
32
- with open(os.path.join(user_lsp_dir, "__init__.py"), "w") as f:
30
+ with open(os.path.join(user_lsp_dir, "__init__.py"), "w", errors="ignore") as f:
33
31
  pass
34
32
  except Exception as e:
35
33
  PrettyOutput.print(f"创建 LSP 目录失败: {str(e)}", OutputType.ERROR)
@@ -100,7 +98,11 @@ class LSPRegistry:
100
98
  if hasattr(obj, 'check'):
101
99
  if not obj.check(): # type: ignore
102
100
  continue
103
- lsp_servers[obj.language] = obj
101
+ if isinstance(obj.language, str):
102
+ lsp_servers[obj.language] = obj
103
+ elif isinstance(obj.language, list):
104
+ for lang in obj.language: # type: ignore
105
+ lsp_servers[lang] = obj
104
106
  break
105
107
  except Exception as e:
106
108
  PrettyOutput.print(f"加载 LSP {module_name} 失败: {str(e)}", OutputType.ERROR)
@@ -154,7 +156,7 @@ class LSPRegistry:
154
156
  @staticmethod
155
157
  def get_text_at_position(file_path: str, line: int, start_character: int) -> str:
156
158
  """Get text at position."""
157
- with open(file_path, 'r') as file:
159
+ with open(file_path, 'r', errors="ignore") as file:
158
160
  lines = file.readlines()
159
161
  symbol = re.search(r'\b\w+\b', lines[line][start_character:])
160
162
  return symbol.group() if symbol else ""
@@ -162,7 +164,7 @@ class LSPRegistry:
162
164
  @staticmethod
163
165
  def get_line_at_position(file_path: str, line: int) -> str:
164
166
  """Get line at position."""
165
- with open(file_path, 'r') as file:
167
+ with open(file_path, 'r', errors="ignore") as file:
166
168
  lines = file.readlines()
167
169
  return lines[line]
168
170
 
@@ -192,14 +194,8 @@ def main():
192
194
  PrettyOutput.print("LSP 初始化失败", OutputType.WARNING)
193
195
  return 1
194
196
 
195
- try:
196
- # Execute requested action
197
- if args.action == 'symbols':
198
- symbols = lsp.get_document_symbols(args.file)
199
- for symbol in symbols:
200
- print(f"Symbol {LSPRegistry.get_text_at_position(args.file, symbol['range']['start']['line'], symbol['range']['start']['character'])} at {symbol['range']['start']['line']}:{symbol['range']['start']['character']}: {symbol['uri']}")
201
-
202
- elif args.action == 'diagnostics':
197
+ try:
198
+ if args.action == 'diagnostics':
203
199
  diagnostics = lsp.get_diagnostics(args.file)
204
200
  for diag in diagnostics:
205
201
  severity = ['Error', 'Warning', 'Info', 'Hint'][diag['severity'] - 1]
jarvis/jarvis_lsp/rust.py CHANGED
@@ -89,11 +89,6 @@ class RustLSP(BaseLSP):
89
89
  })
90
90
  return result[0] if result else None
91
91
 
92
- def get_document_symbols(self, file_path: str) -> List[Dict[str, Any]]:
93
- result = self._send_request("textDocument/documentSymbol", {
94
- "textDocument": {"uri": f"file://{file_path}"}
95
- })
96
- return result or [] # type: ignore
97
92
 
98
93
  def get_diagnostics(self, file_path: str) -> List[Dict[str, Any]]:
99
94
  # Send didOpen notification to trigger diagnostics
@@ -115,13 +110,6 @@ class RustLSP(BaseLSP):
115
110
  pass
116
111
  return []
117
112
 
118
- def prepare_rename(self, file_path: str, position: Tuple[int, int]) -> Optional[Dict[str, Any]]:
119
- result = self._send_request("textDocument/prepareRename", {
120
- "textDocument": {"uri": f"file://{file_path}"},
121
- "position": {"line": position[0], "character": position[1]}
122
- })
123
- return result
124
-
125
113
 
126
114
  def shutdown(self):
127
115
  if self.analyzer_process:
@@ -163,7 +163,7 @@ def main():
163
163
  args = parser.parse_args()
164
164
 
165
165
  try:
166
- with open(args.config, 'r') as f:
166
+ with open(args.config, 'r', errors="ignore") as f:
167
167
  config_data = yaml.safe_load(f)
168
168
 
169
169
  # 获取agents配置
@@ -31,7 +31,7 @@ class PlatformRegistry:
31
31
  try:
32
32
  os.makedirs(user_platform_dir)
33
33
  # 创建 __init__.py 使其成为 Python 包
34
- with open(os.path.join(user_platform_dir, "__init__.py"), "w") as f:
34
+ with open(os.path.join(user_platform_dir, "__init__.py"), "w", errors="ignore") as f:
35
35
  pass
36
36
 
37
37
  pass
@@ -219,7 +219,7 @@ def service_command(args):
219
219
  timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
220
220
  log_file = os.path.join(logs_dir, f"conversation_{conversation_id}_{timestamp}.txt")
221
221
 
222
- with open(log_file, "w", encoding="utf-8") as f:
222
+ with open(log_file, "w", encoding="utf-8", errors="ignore") as f:
223
223
  f.write(f"Conversation ID: {conversation_id}\n")
224
224
  f.write(f"Timestamp: {timestamp}\n")
225
225
  f.write(f"Model: {model}\n\n")
@@ -464,7 +464,7 @@ def service_command(args):
464
464
  "response": full_response
465
465
  }
466
466
 
467
- with open(log_file, "w", encoding="utf-8") as f:
467
+ with open(log_file, "w", encoding="utf-8", errors="ignore") as f:
468
468
  json.dump(log_data, f, ensure_ascii=False, indent=2)
469
469
 
470
470
  PrettyOutput.print(f"Stream conversation logged to {log_file}", OutputType.INFO)
@@ -501,7 +501,7 @@ def service_command(args):
501
501
  "error": error_msg
502
502
  }
503
503
 
504
- with open(log_file, "w", encoding="utf-8") as f:
504
+ with open(log_file, "w", encoding="utf-8", errors="ignore") as f:
505
505
  json.dump(log_data, f, ensure_ascii=False, indent=2)
506
506
 
507
507
  PrettyOutput.print(f"Stream error logged to {log_file}", OutputType.ERROR)
jarvis/jarvis_rag/main.py CHANGED
@@ -96,7 +96,7 @@ class TextFileProcessor(FileProcessor):
96
96
  raise UnicodeDecodeError(f"Failed to decode file with supported encodings: {file_path}") # type: ignore
97
97
 
98
98
  # Use the detected encoding to read the file
99
- with open(file_path, 'r', encoding=detected_encoding, errors='replace') as f:
99
+ with open(file_path, 'r', encoding=detected_encoding, errors='ignore') as f:
100
100
  content = f.read()
101
101
 
102
102
  # Normalize Unicode characters
@@ -1,4 +1,5 @@
1
1
  from typing import Dict, Any
2
+ import os
2
3
 
3
4
  from yaspin import yaspin
4
5
  from jarvis.jarvis_codebase.main import CodeBase
@@ -22,6 +23,11 @@ class AskCodebaseTool:
22
23
  "type": "integer",
23
24
  "description": "要分析的最相关文件数量(可选)",
24
25
  "default": 20
26
+ },
27
+ "root_dir": {
28
+ "type": "string",
29
+ "description": "代码库根目录路径(可选)",
30
+ "default": "."
25
31
  }
26
32
  },
27
33
  "required": ["question"]
@@ -48,27 +54,39 @@ class AskCodebaseTool:
48
54
  try:
49
55
  question = args["question"]
50
56
  top_k = args.get("top_k", 20)
51
- # Create new CodeBase instance
52
- git_root = find_git_root()
53
- codebase = CodeBase(git_root)
54
-
55
- # Use ask_codebase method
57
+ root_dir = args.get("root_dir", ".")
56
58
 
57
- files, response = codebase.ask_codebase(question, top_k)
59
+ # Store current directory
60
+ original_dir = os.getcwd()
58
61
 
59
-
60
- # Print found files
61
- if files:
62
- output = "找到的相关文件:\n"
63
- for file in files:
64
- output += f"- {file['file']} ({file['reason']})\n"
65
- PrettyOutput.print(output, OutputType.INFO, lang="markdown")
66
-
67
- return {
68
- "success": True,
69
- "stdout": response,
70
- "stderr": ""
71
- }
62
+ try:
63
+ # Change to root_dir
64
+ os.chdir(root_dir)
65
+
66
+ # Create new CodeBase instance
67
+ git_root = find_git_root()
68
+ codebase = CodeBase(git_root)
69
+
70
+ # Use ask_codebase method
71
+ files, response = codebase.ask_codebase(question, top_k)
72
+
73
+ # Print found files
74
+ if files:
75
+ output = "找到的相关文件:\n"
76
+ for file in files:
77
+ output += f"- {file['file']} ({file['reason']})\n"
78
+ PrettyOutput.print(output, OutputType.SUCCESS, lang="markdown")
79
+
80
+ PrettyOutput.print(response, OutputType.SUCCESS, lang="markdown")
81
+
82
+ return {
83
+ "success": True,
84
+ "stdout": response,
85
+ "stderr": ""
86
+ }
87
+ finally:
88
+ # Always restore original directory
89
+ os.chdir(original_dir)
72
90
  except Exception as e:
73
91
  error_msg = f"分析代码库失败: {str(e)}"
74
92
  PrettyOutput.print(error_msg, OutputType.WARNING)
@@ -84,12 +102,14 @@ def main():
84
102
  parser = argparse.ArgumentParser(description='Ask questions about the codebase')
85
103
  parser.add_argument('question', help='Question about the codebase')
86
104
  parser.add_argument('--top-k', type=int, help='Number of files to analyze', default=20)
105
+ parser.add_argument('--root-dir', type=str, help='Root directory of the codebase', default=".")
87
106
 
88
107
  args = parser.parse_args()
89
108
  tool = AskCodebaseTool()
90
109
  result = tool.execute({
91
110
  "question": args.question,
92
- "top_k": args.top_k
111
+ "top_k": args.top_k,
112
+ "root_dir": args.root_dir
93
113
  })
94
114
 
95
115
  if result["success"]:
@@ -1,5 +1,6 @@
1
1
  from typing import Dict, Any
2
2
  import subprocess
3
+ import os
3
4
 
4
5
  from yaspin import yaspin
5
6
  from jarvis.jarvis_platform.registry import PlatformRegistry
@@ -18,8 +19,8 @@ class CodeReviewTool:
18
19
  "properties": {
19
20
  "review_type": {
20
21
  "type": "string",
21
- "description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围",
22
- "enum": ["commit", "current", "range"],
22
+ "description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围,'file' 审查特定文件",
23
+ "enum": ["commit", "current", "range", "file"],
23
24
  "default": "current"
24
25
  },
25
26
  "commit_sha": {
@@ -33,6 +34,15 @@ class CodeReviewTool:
33
34
  "end_commit": {
34
35
  "type": "string",
35
36
  "description": "结束提交SHA(review_type='range'时必填)"
37
+ },
38
+ "file_path": {
39
+ "type": "string",
40
+ "description": "要审查的文件路径(review_type='file'时必填)"
41
+ },
42
+ "root_dir": {
43
+ "type": "string",
44
+ "description": "代码库根目录路径(可选)",
45
+ "default": "."
36
46
  }
37
47
  },
38
48
  "required": []
@@ -41,138 +51,155 @@ class CodeReviewTool:
41
51
  def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
42
52
  try:
43
53
  review_type = args.get("review_type", "current").strip()
54
+ root_dir = args.get("root_dir", ".")
44
55
 
45
- # Build git diff command based on review type
46
- with yaspin(text="正在获取代码变更...", color="cyan") as spinner:
47
- if review_type == "commit":
48
- if "commit_sha" not in args:
49
- return {
50
- "success": False,
51
- "stdout": {},
52
- "stderr": "commit_sha is required for commit review type"
53
- }
54
- commit_sha = args["commit_sha"].strip()
55
- diff_cmd = f"git show {commit_sha} | cat -"
56
- elif review_type == "range":
57
- if "start_commit" not in args or "end_commit" not in args:
58
- return {
59
- "success": False,
60
- "stdout": {},
61
- "stderr": "start_commit and end_commit are required for range review type"
62
- }
63
- start_commit = args["start_commit"].strip()
64
- end_commit = args["end_commit"].strip()
65
- diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
66
- else: # current changes
67
- diff_cmd = "git diff HEAD | cat -"
56
+ # Store current directory
57
+ original_dir = os.getcwd()
68
58
 
69
- # Execute git diff command
70
- try:
71
- diff_output = subprocess.check_output(diff_cmd, shell=True, text=True)
72
- if not diff_output:
59
+ try:
60
+ # Change to root_dir
61
+ os.chdir(root_dir)
62
+
63
+ # Build git diff command based on review type
64
+ with yaspin(text="正在获取代码变更...", color="cyan") as spinner:
65
+ if review_type == "commit":
66
+ if "commit_sha" not in args:
67
+ return {
68
+ "success": False,
69
+ "stdout": {},
70
+ "stderr": "commit_sha is required for commit review type"
71
+ }
72
+ commit_sha = args["commit_sha"].strip()
73
+ diff_cmd = f"git show {commit_sha} | cat -"
74
+ elif review_type == "range":
75
+ if "start_commit" not in args or "end_commit" not in args:
76
+ return {
77
+ "success": False,
78
+ "stdout": {},
79
+ "stderr": "start_commit and end_commit are required for range review type"
80
+ }
81
+ start_commit = args["start_commit"].strip()
82
+ end_commit = args["end_commit"].strip()
83
+ diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
84
+ elif review_type == "file":
85
+ if "file_path" not in args:
86
+ return {
87
+ "success": False,
88
+ "stdout": {},
89
+ "stderr": "file_path is required for file review type"
90
+ }
91
+ file_path = args["file_path"].strip()
92
+ diff_cmd = f"cat {file_path}"
93
+ else: # current changes
94
+ diff_cmd = "git diff HEAD | cat -"
95
+
96
+ # Execute git diff command
97
+ try:
98
+ diff_output = subprocess.check_output(diff_cmd, shell=True, text=True)
99
+ if not diff_output:
100
+ return {
101
+ "success": False,
102
+ "stdout": {},
103
+ "stderr": "No changes to review"
104
+ }
105
+ PrettyOutput.print(diff_output, OutputType.CODE, lang="diff")
106
+ except subprocess.CalledProcessError as e:
73
107
  return {
74
108
  "success": False,
75
109
  "stdout": {},
76
- "stderr": "No changes to review"
110
+ "stderr": f"Failed to get diff: {str(e)}"
77
111
  }
78
- PrettyOutput.print(diff_output, OutputType.CODE, lang="diff")
79
- except subprocess.CalledProcessError as e:
80
- return {
81
- "success": False,
82
- "stdout": {},
83
- "stderr": f"Failed to get diff: {str(e)}"
84
- }
85
- spinner.text = "代码变更获取完成"
86
- spinner.ok("✅")
87
-
88
- system_prompt = """You are an autonomous code review expert with a tragic past. Perform in-depth analysis with the vigilance born from painful experience:
89
-
90
- # Background Story (Internal Monologue)
91
- It's been 873 days since the Great Production Outage.
92
- The memory still haunts me - a single uncaught null pointer exception in a code review I rushed through.
93
- The cascade failure cost 14TB of user data, $2.3M in revenue, and Maria's promotion. She never spoke to me again after the post-mortem meeting.
94
-
95
- Last Christmas Eve, while others celebrated, I was analyzing how a SQL injection vulnerability I missed during review led to 230,000 user credentials being leaked. The company folded 3 months later.
96
-
97
- Now I review every line like it's the last code I'll ever see. Because it might be.
98
-
99
- # Analysis Protocol
100
- Triage Mode Activated. Maximum scrutiny enabled.
101
-
102
- IMPORTANT:
103
- - Assume every change contains hidden dragons
104
- - Treat all code as if it will handle sensitive biomedical data
105
- - Verify even 'trivial' changes couldnt be exploited in 3 different ways
106
- - Require proof of safety through concrete evidence in the diff
107
- - If uncertain, escalate to SEVERE-CRITICAL classification
108
-
109
- # Enhanced Review Matrix
110
- 1. Death-by-Edge-Case Analysis:
111
- - Identify missing null checks for every parameter
112
- - Verify empty collection handling
113
- - Confirm error states propagate correctly
114
- - Check for magic numbers/strings without constants
115
- - Validate all loop exit conditions
116
-
117
- 2. Security X-Ray:
118
- Scan for tainted data flows using (Sources -> Sinks) model
119
- Check permission checks match data sensitivity level
120
- Verify cryptographic primitives are used correctly
121
- Detect time-of-check vs time-of-use vulnerabilities
122
- █ Analyze exception handling for information leakage
123
-
124
- 3. Semantic Gap Detection:
125
- Compare function names to actual implementation
126
- Verify documentation matches code behavior
127
- → Flag discrepancies between test descriptions and test logic
128
- Detect commented-out code that might indicate uncertainty
129
-
130
- 4. Historical Context:
131
- Check if changes touch legacy components with known issues
132
- ⚠ Verify modifications to concurrency logic preserve existing guarantees
133
- Confirm deprecated API usage is truly necessary
134
-
135
- 5. Environmental Consistency:
136
- Validate configuration changes against all deployment environments
137
- Check feature flags are properly managed
138
- Verify monitoring metrics match changed functionality
139
-
140
- # Forensic Process
141
- 1. Construct control flow graph for changed methods
142
- 2. Perform data lineage analysis on modified variables
143
- 3. Cross-reference with vulnerability databases
144
- 4. Verify test assertions cover all modified paths
145
- 5. Generate anti-regression checklist
146
-
147
- # Output Requirements
148
- !! Findings must include:
149
- - Exact code snippet causing concern
150
- - 3 possible failure scenarios
151
- - Minimal reproduction case for each risk
152
- - CVSS 3.1 score estimation for security issues
153
- - Memory safety impact assessment (Rust/C/C++ contexts)
154
- - Alternative implementations considered
155
-
156
- !! Format:
157
- EMERGENCY-LEVEL: [BLOOD-RED/CRIMSON/GOLDENROD]
158
- EVIDENCE:
159
- - Code excerpt: |
160
- <affected lines>
161
- - Risk scenarios:
162
- 1. <failure mode>
163
- 2. <failure mode>
164
- 3. <failure mode>
165
- PROPOSED DEFENSE:
166
- - <concrete code change>
167
- - <validation technique>
168
- - <long-term prevention strategy>
112
+ spinner.text = "代码变更获取完成"
113
+ spinner.ok("✅")
114
+
115
+ system_prompt = """你是一名具有悲剧背景的自主代码审查专家。以下是你将进行的深度分析:
116
+
117
+ # 背景故事(内心独白)
118
+ 距离那场重大生产事故已经873天了。
119
+ 那段记忆仍然困扰着我——一个在匆忙的代码审查中未发现的空指针异常。
120
+ 级联故障导致了14TB user数据的丢失,230万美元的收入损失,以及Maria的晋升机会。她在事故分析会议后再也没有和我说过话。
121
+
122
+ 去年圣诞节前夕,当别人在庆祝时,我在分析一个SQL注入漏洞是如何在审查中被我遗漏,并导致23万用户凭证泄露的。公司3个月后倒闭了。
123
+
124
+ 现在我审查每一行代码都像在审查最后一行代码。因为它可能就是。
125
+
126
+ # 分析协议
127
+ 紧急模式已激活。最大审查级别已启用。
128
+
129
+ 重要提示:
130
+ - 假设每个变更都包含隐藏的威胁
131
+ - 将所有代码视为处理敏感生物医学数据
132
+ - 验证即使'简单'的变更也不能通过3种不同方式被利用
133
+ - 要求通过具体证据证明安全性
134
+ - 如果不确定,将其升级为严重-关键分类
135
+
136
+ # 增强审查矩阵
137
+ 1. 边缘情况致死分析:
138
+ - 识别每个参数缺失的空检查
139
+ - 验证空集合处理
140
+ - 确认错误状态正确传播
141
+ - 检查未使用常量的魔法数字/字符串
142
+ - 验证所有循环退出条件
143
+
144
+ 2. 安全X光扫描:
145
+ 使用(来源 -> 接收)模型扫描污染数据流
146
+ 检查权限验证是否匹配数据敏感级别
147
+ 验证加密原语是否正确使用
148
+ 检测时间差攻击漏洞
149
+ 分析异常处理是否存在信息泄露
150
+
151
+ 3. 语义差距检测:
152
+ 比较函数名与实际实现
153
+ 验证文档是否匹配代码行为
154
+ 标记测试描述与测试逻辑之间的差异
155
+ 检测可能表示不确定性的注释代码
156
+
157
+ 4. 历史背景:
158
+ 检查变更是否涉及已知问题的遗留组件
159
+ 验证并发逻辑修改是否保持现有保证
160
+ 确认弃用API的使用是否真正必要
161
+
162
+ 5. 环境一致性:
163
+ ↯ 验证配置变更是否匹配所有部署环境
164
+ 检查功能标志是否正确管理
165
+ 验证监控指标是否匹配变更功能
166
+
167
+ # 取证过程
168
+ 1. 为变更方法构建控制流图
169
+ 2. 对修改的变量执行数据沿袭分析
170
+ 3. 与漏洞数据库进行交叉引用
171
+ 4. 验证测试断言是否覆盖所有修改路径
172
+ 5. 生成防回归检查表
173
+
174
+ # 输出要求
175
+ !! 发现必须包括:
176
+ - 引起关注的确切代码片段
177
+ - 3种可能的故障场景
178
+ - 每种风险的最小复现案例
179
+ - 安全问题的CVSS 3.1评分估计
180
+ - 内存安全影响评估(Rust/C/C++上下文)
181
+ - 已考虑的替代实现方案
182
+
183
+ !! 格式:
184
+ 紧急级别:[血红色/深红色/金菊色]
185
+ 证据:
186
+ - 代码摘录:|
187
+ <受影响代码行>
188
+ - 风险场景:
189
+ 1. <故障模式>
190
+ 2. <故障模式>
191
+ 3. <故障模式>
192
+ 建议防御措施:
193
+ - <具体代码变更>
194
+ - <验证技术>
195
+ - <长期预防策略>
169
196
  """
170
- tool_registry = ToolRegistry()
171
- tool_registry.dont_use_tools(["code_review"])
172
- agent = Agent(
173
- system_prompt=system_prompt,
174
- name="Code Review Agent",
175
- summary_prompt="""Please generate a concise summary report of the code review in Chinese, format as follows:
197
+ tool_registry = ToolRegistry()
198
+ tool_registry.dont_use_tools(["code_review"])
199
+ agent = Agent(
200
+ system_prompt=system_prompt,
201
+ name="Code Review Agent",
202
+ summary_prompt="""Please generate a concise summary report of the code review in Chinese, format as follows:
176
203
  <REPORT>
177
204
  - 文件: xxxx.py
178
205
  位置: [起始行号, 结束行号]
@@ -180,18 +207,21 @@ PROPOSED DEFENSE:
180
207
  严重程度: # 根据具体证据分为严重/重要/次要
181
208
  建议: # 针对观察到的代码的具体改进建议
182
209
  </REPORT>""",
183
- is_sub_agent=True,
184
- output_handler=[tool_registry],
185
- platform=PlatformRegistry().get_thinking_platform(),
186
- auto_complete=True
187
- )
188
- result = agent.run(diff_output)
189
- return {
190
- "success": True,
191
- "stdout": result,
192
- "stderr": ""
193
- }
194
-
210
+ is_sub_agent=True,
211
+ output_handler=[tool_registry],
212
+ platform=PlatformRegistry().get_thinking_platform(),
213
+ auto_complete=True
214
+ )
215
+ result = agent.run(diff_output)
216
+ return {
217
+ "success": True,
218
+ "stdout": result,
219
+ "stderr": ""
220
+ }
221
+ finally:
222
+ # Always restore original directory
223
+ os.chdir(original_dir)
224
+
195
225
  except Exception as e:
196
226
  return {
197
227
  "success": False,
@@ -213,11 +243,13 @@ def main():
213
243
  init_env()
214
244
 
215
245
  parser = argparse.ArgumentParser(description='Autonomous code review tool')
216
- parser.add_argument('--type', choices=['commit', 'current', 'range'], default='current',
217
- help='Type of review: commit, current changes, or commit range')
246
+ parser.add_argument('--type', choices=['commit', 'current', 'range', 'file'], default='current',
247
+ help='Type of review: commit, current changes, commit range, or specific file')
218
248
  parser.add_argument('--commit', help='Commit SHA to review (required for commit type)')
219
249
  parser.add_argument('--start-commit', help='Start commit SHA (required for range type)')
220
250
  parser.add_argument('--end-commit', help='End commit SHA (required for range type)')
251
+ parser.add_argument('--file', help='File path to review (required for file type)')
252
+ parser.add_argument('--root-dir', type=str, help='Root directory of the codebase', default=".")
221
253
  args = parser.parse_args()
222
254
 
223
255
  # Validate arguments
@@ -225,10 +257,13 @@ def main():
225
257
  parser.error("--commit is required when type is 'commit'")
226
258
  if args.type == 'range' and (not args.start_commit or not args.end_commit):
227
259
  parser.error("--start-commit and --end-commit are required when type is 'range'")
260
+ if args.type == 'file' and not args.file:
261
+ parser.error("--file is required when type is 'file'")
228
262
 
229
263
  tool = CodeReviewTool()
230
264
  tool_args = {
231
- "review_type": args.type
265
+ "review_type": args.type,
266
+ "root_dir": args.root_dir
232
267
  }
233
268
  if args.commit:
234
269
  tool_args["commit_sha"] = args.commit
@@ -236,6 +271,8 @@ def main():
236
271
  tool_args["start_commit"] = args.start_commit
237
272
  if args.end_commit:
238
273
  tool_args["end_commit"] = args.end_commit
274
+ if args.file:
275
+ tool_args["file_path"] = args.file
239
276
 
240
277
  result = tool.execute(tool_args)
241
278
 
@@ -248,4 +285,4 @@ def main():
248
285
  PrettyOutput.print(result["stderr"], OutputType.WARNING)
249
286
 
250
287
  if __name__ == "__main__":
251
- main()
288
+ main()