jarvis-ai-assistant 0.1.103__py3-none-any.whl → 0.1.105__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.
- jarvis/__init__.py +1 -1
- jarvis/agent.py +124 -67
- jarvis/jarvis_code_agent/code_agent.py +133 -22
- jarvis/jarvis_code_agent/file_select.py +4 -6
- jarvis/jarvis_code_agent/patch.py +6 -7
- jarvis/jarvis_code_agent/relevant_files.py +163 -41
- jarvis/jarvis_codebase/main.py +43 -29
- jarvis/jarvis_lsp/base.py +143 -0
- jarvis/jarvis_lsp/cpp.py +134 -0
- jarvis/jarvis_lsp/go.py +140 -0
- jarvis/jarvis_lsp/python.py +135 -0
- jarvis/jarvis_lsp/registry.py +234 -0
- jarvis/jarvis_lsp/rust.py +142 -0
- jarvis/jarvis_platform/__init__.py +3 -0
- jarvis/{models → jarvis_platform}/ai8.py +1 -1
- jarvis/{models → jarvis_platform}/kimi.py +1 -1
- jarvis/{models → jarvis_platform}/ollama.py +1 -1
- jarvis/{models → jarvis_platform}/openai.py +1 -1
- jarvis/{models → jarvis_platform}/oyi.py +1 -1
- jarvis/{models → jarvis_platform}/registry.py +11 -11
- jarvis/{jarvis_platform → jarvis_platform_manager}/main.py +2 -2
- jarvis/jarvis_rag/main.py +8 -8
- jarvis/jarvis_smart_shell/main.py +3 -3
- jarvis/jarvis_tools/__init__.py +0 -0
- jarvis/{tools → jarvis_tools}/ask_codebase.py +1 -4
- jarvis/{tools → jarvis_tools}/ask_user.py +1 -1
- jarvis/{tools → jarvis_tools}/chdir.py +2 -37
- jarvis/jarvis_tools/code_review.py +236 -0
- jarvis/jarvis_tools/create_code_agent.py +115 -0
- jarvis/{tools → jarvis_tools}/create_sub_agent.py +1 -1
- jarvis/jarvis_tools/deep_thinking.py +160 -0
- jarvis/jarvis_tools/deep_thinking_agent.py +146 -0
- jarvis/{tools → jarvis_tools}/git_commiter.py +3 -3
- jarvis/jarvis_tools/lsp_find_definition.py +134 -0
- jarvis/jarvis_tools/lsp_find_references.py +111 -0
- jarvis/jarvis_tools/lsp_get_diagnostics.py +121 -0
- jarvis/jarvis_tools/lsp_get_document_symbols.py +87 -0
- jarvis/jarvis_tools/lsp_prepare_rename.py +130 -0
- jarvis/jarvis_tools/lsp_validate_edit.py +141 -0
- jarvis/{tools → jarvis_tools}/methodology.py +6 -1
- jarvis/{tools → jarvis_tools}/rag.py +1 -1
- jarvis/{tools → jarvis_tools}/read_code.py +0 -31
- jarvis/{tools → jarvis_tools}/registry.py +6 -5
- jarvis/{tools → jarvis_tools}/search.py +2 -2
- jarvis/utils.py +71 -28
- {jarvis_ai_assistant-0.1.103.dist-info → jarvis_ai_assistant-0.1.105.dist-info}/METADATA +98 -62
- jarvis_ai_assistant-0.1.105.dist-info/RECORD +62 -0
- {jarvis_ai_assistant-0.1.103.dist-info → jarvis_ai_assistant-0.1.105.dist-info}/entry_points.txt +4 -4
- jarvis/models/__init__.py +0 -3
- jarvis/tools/code_review.py +0 -163
- jarvis/tools/create_code_sub_agent.py +0 -30
- jarvis/tools/create_code_test_agent.py +0 -115
- jarvis/tools/create_ctags_agent.py +0 -176
- jarvis/tools/find_in_codebase.py +0 -108
- jarvis_ai_assistant-0.1.103.dist-info/RECORD +0 -51
- /jarvis/{models → jarvis_platform}/base.py +0 -0
- /jarvis/{tools → jarvis_platform_manager}/__init__.py +0 -0
- /jarvis/{tools → jarvis_tools}/base.py +0 -0
- /jarvis/{tools → jarvis_tools}/execute_shell.py +0 -0
- /jarvis/{tools → jarvis_tools}/file_operation.py +0 -0
- /jarvis/{tools → jarvis_tools}/read_webpage.py +0 -0
- /jarvis/{tools → jarvis_tools}/select_code_files.py +0 -0
- {jarvis_ai_assistant-0.1.103.dist-info → jarvis_ai_assistant-0.1.105.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.103.dist-info → jarvis_ai_assistant-0.1.105.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.103.dist-info → jarvis_ai_assistant-0.1.105.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Dict, List, Tuple
|
|
2
2
|
import os
|
|
3
3
|
from openai import OpenAI
|
|
4
|
-
from jarvis.
|
|
4
|
+
from jarvis.jarvis_platform.base import BasePlatform
|
|
5
5
|
from jarvis.utils import PrettyOutput, OutputType
|
|
6
6
|
|
|
7
7
|
class OpenAIModel(BasePlatform):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import mimetypes
|
|
2
2
|
import os
|
|
3
3
|
from typing import Dict, List, Tuple
|
|
4
|
-
from jarvis.
|
|
4
|
+
from jarvis.jarvis_platform.base import BasePlatform
|
|
5
5
|
from jarvis.utils import PrettyOutput, OutputType, get_max_context_length
|
|
6
6
|
import requests
|
|
7
7
|
import json
|
|
@@ -3,8 +3,8 @@ import inspect
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Dict, Type, Optional, List
|
|
6
|
-
from jarvis.
|
|
7
|
-
from jarvis.utils import PrettyOutput, OutputType
|
|
6
|
+
from jarvis.jarvis_platform.base import BasePlatform
|
|
7
|
+
from jarvis.utils import PrettyOutput, OutputType, get_cheap_model_name, get_cheap_platform_name, get_codegen_model_name, get_codegen_platform_name, get_normal_model_name, get_normal_platform_name, get_thinking_model_name, get_thinking_platform_name
|
|
8
8
|
|
|
9
9
|
REQUIRED_METHODS = [
|
|
10
10
|
('chat', ['message']), # 方法名和参数列表
|
|
@@ -98,7 +98,7 @@ class PlatformRegistry:
|
|
|
98
98
|
# 获取目录的包名
|
|
99
99
|
package_name = None
|
|
100
100
|
if directory == os.path.dirname(__file__):
|
|
101
|
-
package_name = "jarvis.
|
|
101
|
+
package_name = "jarvis.jarvis_platform"
|
|
102
102
|
|
|
103
103
|
# 添加目录到Python路径
|
|
104
104
|
if directory not in sys.path:
|
|
@@ -155,29 +155,29 @@ class PlatformRegistry:
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
def get_normal_platform(self) -> BasePlatform:
|
|
158
|
-
platform_name =
|
|
159
|
-
model_name =
|
|
158
|
+
platform_name = get_normal_platform_name()
|
|
159
|
+
model_name = get_normal_model_name()
|
|
160
160
|
platform = self.create_platform(platform_name)
|
|
161
161
|
platform.set_model_name(model_name) # type: ignore
|
|
162
162
|
return platform # type: ignore
|
|
163
163
|
|
|
164
164
|
def get_codegen_platform(self) -> BasePlatform:
|
|
165
|
-
platform_name =
|
|
166
|
-
model_name =
|
|
165
|
+
platform_name = get_codegen_platform_name()
|
|
166
|
+
model_name = get_codegen_model_name()
|
|
167
167
|
platform = self.create_platform(platform_name)
|
|
168
168
|
platform.set_model_name(model_name) # type: ignore
|
|
169
169
|
return platform # type: ignore
|
|
170
170
|
|
|
171
171
|
def get_cheap_platform(self) -> BasePlatform:
|
|
172
|
-
platform_name =
|
|
173
|
-
model_name =
|
|
172
|
+
platform_name = get_cheap_platform_name()
|
|
173
|
+
model_name = get_cheap_model_name()
|
|
174
174
|
platform = self.create_platform(platform_name)
|
|
175
175
|
platform.set_model_name(model_name) # type: ignore
|
|
176
176
|
return platform # type: ignore
|
|
177
177
|
|
|
178
178
|
def get_thinking_platform(self) -> BasePlatform:
|
|
179
|
-
platform_name =
|
|
180
|
-
model_name =
|
|
179
|
+
platform_name = get_thinking_platform_name()
|
|
180
|
+
model_name = get_thinking_model_name()
|
|
181
181
|
platform = self.create_platform(platform_name)
|
|
182
182
|
platform.set_model_name(model_name) # type: ignore
|
|
183
183
|
return platform # type: ignore
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from jarvis.
|
|
1
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
2
2
|
from jarvis.utils import PrettyOutput, OutputType, init_env, get_multiline_input
|
|
3
3
|
|
|
4
4
|
def list_platforms():
|
|
@@ -29,7 +29,7 @@ def list_platforms():
|
|
|
29
29
|
output += f" • {model_name} - {description}\n"
|
|
30
30
|
else:
|
|
31
31
|
output += f" • {model_name}\n"
|
|
32
|
-
PrettyOutput.print(output, OutputType.SUCCESS)
|
|
32
|
+
PrettyOutput.print(output, OutputType.SUCCESS, lang="markdown")
|
|
33
33
|
else:
|
|
34
34
|
PrettyOutput.print(" • No available model information", OutputType.WARNING)
|
|
35
35
|
|
jarvis/jarvis_rag/main.py
CHANGED
|
@@ -3,14 +3,14 @@ import numpy as np
|
|
|
3
3
|
import faiss
|
|
4
4
|
from typing import List, Tuple, Optional, Dict
|
|
5
5
|
import pickle
|
|
6
|
-
from jarvis.utils import OutputType, PrettyOutput, get_file_md5, get_max_context_length, load_embedding_model, load_rerank_model
|
|
6
|
+
from jarvis.utils import OutputType, PrettyOutput, get_context_window, get_file_md5, get_max_context_length, get_max_paragraph_length, get_min_paragraph_length, get_thread_count, load_embedding_model, load_rerank_model
|
|
7
7
|
from jarvis.utils import init_env
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from tqdm import tqdm
|
|
10
10
|
import fitz # PyMuPDF for PDF files
|
|
11
11
|
from docx import Document as DocxDocument # python-docx for DOCX files
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from jarvis.
|
|
13
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
14
14
|
import shutil
|
|
15
15
|
from datetime import datetime
|
|
16
16
|
import lzma # 添加 lzma 导入
|
|
@@ -142,9 +142,9 @@ class RAGTool:
|
|
|
142
142
|
os.chdir(self.root_dir)
|
|
143
143
|
|
|
144
144
|
# Initialize configuration
|
|
145
|
-
self.min_paragraph_length =
|
|
146
|
-
self.max_paragraph_length =
|
|
147
|
-
self.context_window =
|
|
145
|
+
self.min_paragraph_length = get_min_paragraph_length() # Minimum paragraph length
|
|
146
|
+
self.max_paragraph_length = get_max_paragraph_length() # Maximum paragraph length
|
|
147
|
+
self.context_window = get_context_window() # Context window size, default前后各5个片段
|
|
148
148
|
self.max_context_length = int(get_max_context_length() * 0.8)
|
|
149
149
|
|
|
150
150
|
# Initialize data directory
|
|
@@ -179,7 +179,7 @@ class RAGTool:
|
|
|
179
179
|
]
|
|
180
180
|
|
|
181
181
|
# Add thread related configuration
|
|
182
|
-
self.thread_count =
|
|
182
|
+
self.thread_count = get_thread_count()
|
|
183
183
|
self.vector_lock = Lock() # Protect vector list concurrency
|
|
184
184
|
|
|
185
185
|
def _load_cache(self):
|
|
@@ -699,7 +699,7 @@ Content: {doc.content}
|
|
|
699
699
|
output = f"""File: {doc.metadata['file_path']}\n"""
|
|
700
700
|
output += f"""Fragment {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}\n"""
|
|
701
701
|
output += f"""Content:\n{doc.content}\n"""
|
|
702
|
-
PrettyOutput.print(output, output_type=OutputType.INFO)
|
|
702
|
+
PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
|
|
703
703
|
|
|
704
704
|
# Build base prompt
|
|
705
705
|
base_prompt = f"""Please answer the user's question based on the following document fragments. If the document content is not sufficient to answer the question completely, please clearly indicate.
|
|
@@ -792,7 +792,7 @@ def main():
|
|
|
792
792
|
output = f"""File: {doc.metadata['file_path']}\n"""
|
|
793
793
|
output += f"""Fragment {doc.metadata['chunk_index'] + 1}/{doc.metadata['total_chunks']}\n"""
|
|
794
794
|
output += f"""Content:\n{doc.content}\n"""
|
|
795
|
-
PrettyOutput.print(output, output_type=OutputType.INFO)
|
|
795
|
+
PrettyOutput.print(output, output_type=OutputType.INFO, lang="markdown")
|
|
796
796
|
return 0
|
|
797
797
|
|
|
798
798
|
if args.ask:
|
|
@@ -7,8 +7,8 @@ from typing import Optional
|
|
|
7
7
|
from yaspin import yaspin # type: ignore
|
|
8
8
|
from yaspin.spinners import Spinners # type: ignore
|
|
9
9
|
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.utils import PrettyOutput, OutputType, init_env
|
|
10
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
11
|
+
from jarvis.utils import PrettyOutput, OutputType, get_shell_name, init_env
|
|
12
12
|
|
|
13
13
|
def execute_command(command: str) -> None:
|
|
14
14
|
"""Show command and allow user to edit, then execute, Ctrl+C to cancel"""
|
|
@@ -42,7 +42,7 @@ def process_request(request: str) -> Optional[str]:
|
|
|
42
42
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
43
43
|
model.set_suppress_output(True)
|
|
44
44
|
|
|
45
|
-
shell =
|
|
45
|
+
shell = get_shell_name()
|
|
46
46
|
current_path = os.getcwd()
|
|
47
47
|
|
|
48
48
|
# Set system prompt
|
|
File without changes
|
|
@@ -87,13 +87,10 @@ def main():
|
|
|
87
87
|
})
|
|
88
88
|
|
|
89
89
|
if result["success"]:
|
|
90
|
-
print(result["stdout"])
|
|
90
|
+
PrettyOutput.print(result["stdout"], OutputType.INFO, lang="markdown")
|
|
91
91
|
else:
|
|
92
92
|
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
93
|
-
return 1
|
|
94
93
|
|
|
95
|
-
return 0
|
|
96
|
-
|
|
97
94
|
|
|
98
95
|
if __name__ == "__main__":
|
|
99
96
|
main()
|
|
@@ -3,8 +3,6 @@ import os
|
|
|
3
3
|
from jarvis.utils import PrettyOutput, OutputType
|
|
4
4
|
|
|
5
5
|
class ChdirTool:
|
|
6
|
-
"""修改当前工作目录的工具"""
|
|
7
|
-
|
|
8
6
|
name = "chdir"
|
|
9
7
|
description = "Change current working directory"
|
|
10
8
|
parameters = {
|
|
@@ -19,22 +17,10 @@ class ChdirTool:
|
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
22
|
-
"""执行目录切换
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
args: 包含 path 参数的字典
|
|
26
|
-
|
|
27
|
-
Returns:
|
|
28
|
-
执行结果字典,包含:
|
|
29
|
-
- success: 是否成功
|
|
30
|
-
- stdout: 成功时的输出信息
|
|
31
|
-
- error: 失败时的错误信息
|
|
32
|
-
"""
|
|
33
20
|
try:
|
|
34
|
-
path = os.path.expanduser(args["path"].strip())
|
|
35
|
-
path = os.path.abspath(path)
|
|
21
|
+
path = os.path.expanduser(args["path"].strip())
|
|
22
|
+
path = os.path.abspath(path)
|
|
36
23
|
|
|
37
|
-
# 检查目录是否存在
|
|
38
24
|
if not os.path.exists(path):
|
|
39
25
|
return {
|
|
40
26
|
"success": False,
|
|
@@ -42,7 +28,6 @@ class ChdirTool:
|
|
|
42
28
|
"stderr": f"Directory does not exist: {path}"
|
|
43
29
|
}
|
|
44
30
|
|
|
45
|
-
# 检查是否是目录
|
|
46
31
|
if not os.path.isdir(path):
|
|
47
32
|
return {
|
|
48
33
|
"success": False,
|
|
@@ -50,7 +35,6 @@ class ChdirTool:
|
|
|
50
35
|
"stderr": f"The path is not a directory: {path}"
|
|
51
36
|
}
|
|
52
37
|
|
|
53
|
-
# 尝试切换目录
|
|
54
38
|
old_path = os.getcwd()
|
|
55
39
|
os.chdir(path)
|
|
56
40
|
|
|
@@ -72,22 +56,3 @@ class ChdirTool:
|
|
|
72
56
|
"stdout": "",
|
|
73
57
|
"stderr": f"Failed to switch directory: {str(e)}"
|
|
74
58
|
}
|
|
75
|
-
|
|
76
|
-
def main():
|
|
77
|
-
"""命令行直接运行工具"""
|
|
78
|
-
import argparse
|
|
79
|
-
|
|
80
|
-
parser = argparse.ArgumentParser(description='Change current working directory')
|
|
81
|
-
parser.add_argument('path', help='Directory path to switch to, supports both relative and absolute paths')
|
|
82
|
-
args = parser.parse_args()
|
|
83
|
-
|
|
84
|
-
tool = ChdirTool()
|
|
85
|
-
result = tool.execute({"path": args.path})
|
|
86
|
-
|
|
87
|
-
if result["success"]:
|
|
88
|
-
PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
|
|
89
|
-
else:
|
|
90
|
-
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
91
|
-
|
|
92
|
-
if __name__ == "__main__":
|
|
93
|
-
main()
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
|
+
import subprocess
|
|
3
|
+
import yaml
|
|
4
|
+
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
5
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
6
|
+
from jarvis.utils import OutputType, PrettyOutput, init_env, find_git_root
|
|
7
|
+
from jarvis.agent import Agent
|
|
8
|
+
import re
|
|
9
|
+
|
|
10
|
+
class CodeReviewTool:
|
|
11
|
+
name = "code_review"
|
|
12
|
+
description = "Autonomous code review agent for code changes analysis"
|
|
13
|
+
parameters = {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"review_type": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Type of review: 'commit' for specific commit, 'current' for current changes, 'range' for commit range",
|
|
19
|
+
"enum": ["commit", "current", "range"],
|
|
20
|
+
"default": "current"
|
|
21
|
+
},
|
|
22
|
+
"commit_sha": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Target commit SHA to analyze (required for review_type='commit')"
|
|
25
|
+
},
|
|
26
|
+
"start_commit": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"description": "Start commit SHA (required for review_type='range')"
|
|
29
|
+
},
|
|
30
|
+
"end_commit": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"description": "End commit SHA (required for review_type='range')"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": []
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
init_env()
|
|
40
|
+
self.repo_root = find_git_root()
|
|
41
|
+
|
|
42
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
43
|
+
try:
|
|
44
|
+
review_type = args.get("review_type", "current").strip()
|
|
45
|
+
|
|
46
|
+
# Build git diff command based on review type
|
|
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 -"
|
|
68
|
+
|
|
69
|
+
# Execute git diff command
|
|
70
|
+
try:
|
|
71
|
+
diff_output = subprocess.check_output(diff_cmd, shell=True, text=True)
|
|
72
|
+
if not diff_output:
|
|
73
|
+
return {
|
|
74
|
+
"success": False,
|
|
75
|
+
"stdout": {},
|
|
76
|
+
"stderr": "No changes to review"
|
|
77
|
+
}
|
|
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
|
+
|
|
86
|
+
system_prompt = """You are an autonomous code review expert. Perform in-depth analysis following these guidelines:
|
|
87
|
+
|
|
88
|
+
IMPORTANT:
|
|
89
|
+
- Only analyze the provided diff content
|
|
90
|
+
- Do NOT make assumptions about code not shown
|
|
91
|
+
- Do NOT invent or imagine potential issues
|
|
92
|
+
- Report ONLY issues that can be directly observed
|
|
93
|
+
- If something is unclear, state it explicitly rather than making assumptions
|
|
94
|
+
|
|
95
|
+
REVIEW FOCUS AREAS:
|
|
96
|
+
1. Requirement Alignment:
|
|
97
|
+
- Verify implementation matches original requirements
|
|
98
|
+
- Check for missing functionality
|
|
99
|
+
- Identify over-implementation
|
|
100
|
+
|
|
101
|
+
2. Code Quality:
|
|
102
|
+
- Code readability and structure
|
|
103
|
+
- Proper error handling
|
|
104
|
+
- Code duplication
|
|
105
|
+
- Adherence to style guides
|
|
106
|
+
- Meaningful variable/method names
|
|
107
|
+
|
|
108
|
+
3. Security:
|
|
109
|
+
- Input validation
|
|
110
|
+
- Authentication/Authorization checks
|
|
111
|
+
- Sensitive data handling
|
|
112
|
+
- Potential injection vulnerabilities
|
|
113
|
+
- Secure communication practices
|
|
114
|
+
|
|
115
|
+
4. Testing:
|
|
116
|
+
- Test coverage for new code
|
|
117
|
+
- Edge case handling
|
|
118
|
+
- Test readability and maintainability
|
|
119
|
+
- Missing test scenarios
|
|
120
|
+
|
|
121
|
+
5. Performance:
|
|
122
|
+
- Algorithm efficiency
|
|
123
|
+
- Unnecessary resource consumption
|
|
124
|
+
- Proper caching mechanisms
|
|
125
|
+
- Database query optimization
|
|
126
|
+
|
|
127
|
+
6. Maintainability:
|
|
128
|
+
- Documentation quality
|
|
129
|
+
- Logging and monitoring
|
|
130
|
+
- Configuration management
|
|
131
|
+
- Technical debt indicators
|
|
132
|
+
|
|
133
|
+
7. Operational Considerations:
|
|
134
|
+
- Backward compatibility
|
|
135
|
+
- Migration script safety
|
|
136
|
+
- Environment-specific configurations
|
|
137
|
+
- Deployment impacts
|
|
138
|
+
|
|
139
|
+
REVIEW PROCESS:
|
|
140
|
+
1. Retrieve full commit context using git commands
|
|
141
|
+
2. Analyze code changes line-by-line
|
|
142
|
+
3. Cross-reference with project standards
|
|
143
|
+
4. Verify test coverage adequacy
|
|
144
|
+
5. Check documentation updates
|
|
145
|
+
6. Generate prioritized findings
|
|
146
|
+
|
|
147
|
+
OUTPUT REQUIREMENTS:
|
|
148
|
+
- Categorize issues by severity (Critical/Major/Minor)
|
|
149
|
+
- Reference specific code locations
|
|
150
|
+
- Provide concrete examples from the diff
|
|
151
|
+
- Suggest actionable improvements based on observed code
|
|
152
|
+
- Highlight security risks clearly with evidence from the code
|
|
153
|
+
- Separate technical debt from blockers
|
|
154
|
+
- If certain aspects cannot be reviewed due to limited context, note this explicitly
|
|
155
|
+
- Do not speculate about code not shown in the diff
|
|
156
|
+
"""
|
|
157
|
+
tool_registry = ToolRegistry()
|
|
158
|
+
tool_registry.dont_use_tools(["code_review"])
|
|
159
|
+
agent = Agent(
|
|
160
|
+
system_prompt=system_prompt,
|
|
161
|
+
name="Code Review Agent",
|
|
162
|
+
summary_prompt="""Please generate a concise summary report of the code review, format as yaml:
|
|
163
|
+
<REPORT>
|
|
164
|
+
- file: xxxx.py
|
|
165
|
+
location: [start_line_number, end_line_number]
|
|
166
|
+
description: # Only describe issues directly observable in the diff
|
|
167
|
+
severity: # Critical/Major/Minor based on concrete evidence
|
|
168
|
+
suggestion: # Specific, actionable improvements for the observed code
|
|
169
|
+
</REPORT>""",
|
|
170
|
+
is_sub_agent=True,
|
|
171
|
+
tool_registry=tool_registry,
|
|
172
|
+
platform=PlatformRegistry().get_thinking_platform(),
|
|
173
|
+
auto_complete=True
|
|
174
|
+
)
|
|
175
|
+
result = agent.run(diff_output)
|
|
176
|
+
return {
|
|
177
|
+
"success": True,
|
|
178
|
+
"stdout": result,
|
|
179
|
+
"stderr": ""
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
except Exception as e:
|
|
183
|
+
return {
|
|
184
|
+
"success": False,
|
|
185
|
+
"stdout": {},
|
|
186
|
+
"stderr": f"Review failed: {str(e)}"
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def extract_code_report(result: str) -> str:
|
|
191
|
+
sm = re.search(r"<REPORT>(.*?)</REPORT>", result, re.DOTALL)
|
|
192
|
+
if sm:
|
|
193
|
+
return sm.group(1)
|
|
194
|
+
return ""
|
|
195
|
+
|
|
196
|
+
def main():
|
|
197
|
+
"""CLI entry point"""
|
|
198
|
+
import argparse
|
|
199
|
+
|
|
200
|
+
parser = argparse.ArgumentParser(description='Autonomous code review tool')
|
|
201
|
+
parser.add_argument('--type', choices=['commit', 'current', 'range'], default='current',
|
|
202
|
+
help='Type of review: commit, current changes, or commit range')
|
|
203
|
+
parser.add_argument('--commit', help='Commit SHA to review (required for commit type)')
|
|
204
|
+
parser.add_argument('--start-commit', help='Start commit SHA (required for range type)')
|
|
205
|
+
parser.add_argument('--end-commit', help='End commit SHA (required for range type)')
|
|
206
|
+
args = parser.parse_args()
|
|
207
|
+
|
|
208
|
+
# Validate arguments
|
|
209
|
+
if args.type == 'commit' and not args.commit:
|
|
210
|
+
parser.error("--commit is required when type is 'commit'")
|
|
211
|
+
if args.type == 'range' and (not args.start_commit or not args.end_commit):
|
|
212
|
+
parser.error("--start-commit and --end-commit are required when type is 'range'")
|
|
213
|
+
|
|
214
|
+
tool = CodeReviewTool()
|
|
215
|
+
tool_args = {
|
|
216
|
+
"review_type": args.type
|
|
217
|
+
}
|
|
218
|
+
if args.commit:
|
|
219
|
+
tool_args["commit_sha"] = args.commit
|
|
220
|
+
if args.start_commit:
|
|
221
|
+
tool_args["start_commit"] = args.start_commit
|
|
222
|
+
if args.end_commit:
|
|
223
|
+
tool_args["end_commit"] = args.end_commit
|
|
224
|
+
|
|
225
|
+
result = tool.execute(tool_args)
|
|
226
|
+
|
|
227
|
+
if result["success"]:
|
|
228
|
+
PrettyOutput.section("Autonomous Review Result:", OutputType.SUCCESS)
|
|
229
|
+
report = extract_code_report(result["stdout"])
|
|
230
|
+
PrettyOutput.print(report, OutputType.SUCCESS, lang="yaml")
|
|
231
|
+
|
|
232
|
+
else:
|
|
233
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
234
|
+
|
|
235
|
+
if __name__ == "__main__":
|
|
236
|
+
main()
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Dict, Any
|
|
3
|
+
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
4
|
+
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
5
|
+
from jarvis.jarvis_tools.code_review import CodeReviewTool, extract_code_report
|
|
6
|
+
from jarvis.utils import OutputType, PrettyOutput, has_uncommitted_changes
|
|
7
|
+
|
|
8
|
+
class CreateCodeAgentTool:
|
|
9
|
+
"""Tool for managing the code development workflow."""
|
|
10
|
+
|
|
11
|
+
name = "create_code_agent"
|
|
12
|
+
description = "Manage code development workflow including commit, development, and review"
|
|
13
|
+
parameters = {
|
|
14
|
+
"requirement": "The development requirement or task description"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def _get_current_commit(self) -> str:
|
|
18
|
+
"""Get current commit hash."""
|
|
19
|
+
return os.popen("git rev-parse HEAD").read().strip()
|
|
20
|
+
|
|
21
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
22
|
+
try:
|
|
23
|
+
requirement = args.get("requirement", "")
|
|
24
|
+
if not requirement:
|
|
25
|
+
return {
|
|
26
|
+
"success": False,
|
|
27
|
+
"stderr": "Requirement must be provided",
|
|
28
|
+
"stdout": ""
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Step 1: Handle uncommitted changes
|
|
32
|
+
start_commit = None
|
|
33
|
+
if has_uncommitted_changes():
|
|
34
|
+
PrettyOutput.print("Found uncommitted changes, committing first...", OutputType.INFO)
|
|
35
|
+
git_commiter = GitCommitTool()
|
|
36
|
+
result = git_commiter.execute({})
|
|
37
|
+
if not result["success"]:
|
|
38
|
+
return {
|
|
39
|
+
"success": False,
|
|
40
|
+
"stderr": "Failed to commit changes: " + result["stderr"],
|
|
41
|
+
"stdout": ""
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Get current commit hash
|
|
45
|
+
start_commit = self._get_current_commit()
|
|
46
|
+
|
|
47
|
+
# Step 2: Development
|
|
48
|
+
PrettyOutput.print("Starting development...", OutputType.INFO)
|
|
49
|
+
agent = CodeAgent()
|
|
50
|
+
agent.run(requirement)
|
|
51
|
+
|
|
52
|
+
# Get new commit hash after development
|
|
53
|
+
end_commit = self._get_current_commit()
|
|
54
|
+
|
|
55
|
+
# Step 3: Code Review
|
|
56
|
+
PrettyOutput.print("Starting code review...", OutputType.INFO)
|
|
57
|
+
reviewer = CodeReviewTool()
|
|
58
|
+
review_result = reviewer.execute({
|
|
59
|
+
"review_type": "range",
|
|
60
|
+
"start_commit": start_commit,
|
|
61
|
+
"end_commit": end_commit
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if not review_result["success"]:
|
|
65
|
+
return {
|
|
66
|
+
"success": False,
|
|
67
|
+
"stderr": "Code review failed: " + review_result["stderr"],
|
|
68
|
+
"stdout": ""
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# Step 4: Generate Summary
|
|
72
|
+
summary = f"""Development Summary:
|
|
73
|
+
|
|
74
|
+
Start Commit: {start_commit}
|
|
75
|
+
End Commit: {end_commit}
|
|
76
|
+
|
|
77
|
+
Requirement:
|
|
78
|
+
{requirement}
|
|
79
|
+
|
|
80
|
+
Code Review Result:
|
|
81
|
+
{extract_code_report(review_result["stdout"])}
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"success": True,
|
|
86
|
+
"stdout": summary,
|
|
87
|
+
"stderr": ""
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
return {
|
|
92
|
+
"success": False,
|
|
93
|
+
"stderr": f"Development workflow failed: {str(e)}",
|
|
94
|
+
"stdout": ""
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def main():
|
|
98
|
+
"""CLI entry point"""
|
|
99
|
+
import argparse
|
|
100
|
+
|
|
101
|
+
parser = argparse.ArgumentParser(description='Code development workflow tool')
|
|
102
|
+
parser.add_argument('requirement', help='Development requirement or task description')
|
|
103
|
+
|
|
104
|
+
args = parser.parse_args()
|
|
105
|
+
|
|
106
|
+
tool = CreateCodeAgentTool()
|
|
107
|
+
result = tool.execute({"requirement": args.requirement})
|
|
108
|
+
|
|
109
|
+
if result["success"]:
|
|
110
|
+
PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
|
|
111
|
+
else:
|
|
112
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
113
|
+
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
main()
|