jarvis-ai-assistant 0.1.102__py3-none-any.whl → 0.1.103__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 +138 -117
- jarvis/jarvis_code_agent/code_agent.py +234 -0
- jarvis/{jarvis_coder → jarvis_code_agent}/file_select.py +16 -17
- jarvis/jarvis_code_agent/patch.py +118 -0
- jarvis/jarvis_code_agent/relevant_files.py +66 -0
- jarvis/jarvis_codebase/main.py +878 -0
- jarvis/jarvis_platform/main.py +5 -3
- jarvis/jarvis_rag/main.py +818 -0
- jarvis/jarvis_smart_shell/main.py +2 -2
- jarvis/models/ai8.py +3 -1
- jarvis/models/kimi.py +36 -30
- jarvis/models/ollama.py +17 -11
- jarvis/models/openai.py +15 -12
- jarvis/models/oyi.py +24 -7
- jarvis/models/registry.py +1 -25
- jarvis/tools/__init__.py +0 -6
- jarvis/tools/ask_codebase.py +99 -0
- jarvis/tools/ask_user.py +1 -9
- jarvis/tools/chdir.py +1 -1
- jarvis/tools/code_review.py +163 -0
- jarvis/tools/create_code_sub_agent.py +19 -45
- jarvis/tools/create_code_test_agent.py +115 -0
- jarvis/tools/create_ctags_agent.py +176 -0
- jarvis/tools/create_sub_agent.py +2 -2
- jarvis/tools/execute_shell.py +2 -2
- jarvis/tools/file_operation.py +2 -2
- jarvis/tools/find_in_codebase.py +108 -0
- jarvis/tools/git_commiter.py +68 -0
- jarvis/tools/methodology.py +3 -3
- jarvis/tools/rag.py +141 -0
- jarvis/tools/read_code.py +147 -0
- jarvis/tools/read_webpage.py +1 -1
- jarvis/tools/registry.py +47 -31
- jarvis/tools/search.py +8 -6
- jarvis/tools/select_code_files.py +4 -4
- jarvis/utils.py +374 -84
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/METADATA +52 -2
- jarvis_ai_assistant-0.1.103.dist-info/RECORD +51 -0
- jarvis_ai_assistant-0.1.103.dist-info/entry_points.txt +11 -0
- jarvis/jarvis_code_agent/main.py +0 -200
- jarvis/jarvis_coder/git_utils.py +0 -123
- jarvis/jarvis_coder/patch_handler.py +0 -340
- jarvis/jarvis_github/main.py +0 -232
- jarvis/tools/execute_code_modification.py +0 -70
- jarvis/tools/find_files.py +0 -119
- jarvis/tools/generate_tool.py +0 -174
- jarvis/tools/thinker.py +0 -151
- jarvis_ai_assistant-0.1.102.dist-info/RECORD +0 -46
- jarvis_ai_assistant-0.1.102.dist-info/entry_points.txt +0 -6
- /jarvis/{jarvis_coder → jarvis_codebase}/__init__.py +0 -0
- /jarvis/{jarvis_github → jarvis_rag}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.102.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/top_level.txt +0 -0
|
@@ -13,7 +13,7 @@ from jarvis.utils import PrettyOutput, OutputType, init_env
|
|
|
13
13
|
def execute_command(command: str) -> None:
|
|
14
14
|
"""Show command and allow user to edit, then execute, Ctrl+C to cancel"""
|
|
15
15
|
try:
|
|
16
|
-
print("
|
|
16
|
+
print("Generated command (can be edited, press Enter to execute, Ctrl+C to cancel):")
|
|
17
17
|
# Pre-fill input line
|
|
18
18
|
readline.set_startup_hook(lambda: readline.insert_text(command))
|
|
19
19
|
try:
|
|
@@ -21,7 +21,7 @@ def execute_command(command: str) -> None:
|
|
|
21
21
|
if edited_command.strip(): # Ensure command is not empty
|
|
22
22
|
os.system(edited_command)
|
|
23
23
|
except KeyboardInterrupt:
|
|
24
|
-
print("
|
|
24
|
+
print("Execution cancelled")
|
|
25
25
|
finally:
|
|
26
26
|
readline.set_startup_hook() # Clear pre-filled
|
|
27
27
|
except Exception as e:
|
jarvis/models/ai8.py
CHANGED
|
@@ -31,7 +31,9 @@ class AI8Model(BasePlatform):
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
34
|
-
|
|
34
|
+
if self.model_name not in self.get_available_models():
|
|
35
|
+
PrettyOutput.print(f"Warning: The selected model {self.model_name} is not in the available list", OutputType.WARNING)
|
|
36
|
+
|
|
35
37
|
|
|
36
38
|
def set_model_name(self, model_name: str):
|
|
37
39
|
"""Set model name"""
|
jarvis/models/kimi.py
CHANGED
|
@@ -25,21 +25,24 @@ class KimiModel(BasePlatform):
|
|
|
25
25
|
self.chat_id = ""
|
|
26
26
|
self.api_key = os.getenv("KIMI_API_KEY")
|
|
27
27
|
if not self.api_key:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
message = (
|
|
29
|
+
"Need to set KIMI_API_KEY to use Jarvis. Please follow the steps below:\n"
|
|
30
|
+
"1. Get Kimi API Key:\n"
|
|
31
|
+
" • Visit Kimi AI platform: https://kimi.moonshot.cn\n"
|
|
32
|
+
" • Login to your account\n"
|
|
33
|
+
" • Open browser developer tools (F12 or right-click -> Inspect)\n"
|
|
34
|
+
" • Switch to the Network tab\n"
|
|
35
|
+
" • Send any message\n"
|
|
36
|
+
" • Find the Authorization header in the request\n"
|
|
37
|
+
" • Copy the token value (remove the 'Bearer ' prefix)\n"
|
|
38
|
+
"2. Set environment variable:\n"
|
|
39
|
+
" • Method 1: Create or edit ~/.jarvis/env file:\n"
|
|
40
|
+
" echo 'KIMI_API_KEY=your_key_here' > ~/.jarvis/env\n"
|
|
41
|
+
" • Method 2: Set environment variable directly:\n"
|
|
42
|
+
" export KIMI_API_KEY=your_key_here\n"
|
|
43
|
+
"After setting, run Jarvis again."
|
|
44
|
+
)
|
|
45
|
+
PrettyOutput.print(message, OutputType.INFO)
|
|
43
46
|
PrettyOutput.print("KIMI_API_KEY is not set", OutputType.WARNING)
|
|
44
47
|
self.auth_header = f"Bearer {self.api_key}"
|
|
45
48
|
self.chat_id = ""
|
|
@@ -308,42 +311,45 @@ class KimiModel(BasePlatform):
|
|
|
308
311
|
|
|
309
312
|
# 显示搜索结果摘要
|
|
310
313
|
if search_results and not self.suppress_output:
|
|
311
|
-
|
|
314
|
+
output = ["搜索结果:"]
|
|
312
315
|
for result in search_results:
|
|
313
|
-
|
|
316
|
+
output.append(f"- {result['title']}")
|
|
314
317
|
if result['date']:
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
output.append(f" 日期: {result['date']}")
|
|
319
|
+
output.append(f" 来源: {result['site_name']}")
|
|
317
320
|
if result['snippet']:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
+
output.append(f" 摘要: {result['snippet']}")
|
|
322
|
+
output.append(f" 链接: {result['url']}")
|
|
323
|
+
output.append("")
|
|
324
|
+
PrettyOutput.print("\n".join(output), OutputType.PROGRESS)
|
|
321
325
|
|
|
322
326
|
# 显示引用来源
|
|
323
327
|
if ref_sources and not self.suppress_output:
|
|
324
|
-
|
|
328
|
+
output = ["引用来源:"]
|
|
325
329
|
for source in ref_sources:
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
output.append(f"- [{source['ref_id']}] {source['title']} ({source['source']})")
|
|
331
|
+
output.append(f" 链接: {source['url']}")
|
|
328
332
|
if source['abstract']:
|
|
329
|
-
|
|
333
|
+
output.append(f" 摘要: {source['abstract']}")
|
|
330
334
|
|
|
331
335
|
# 显示相关段落
|
|
332
336
|
if source['rag_segments']:
|
|
333
|
-
|
|
337
|
+
output.append(" 相关段落:")
|
|
334
338
|
for segment in source['rag_segments']:
|
|
335
339
|
text = segment.get('text', '').replace('\n', ' ').strip()
|
|
336
340
|
if text:
|
|
337
|
-
|
|
341
|
+
output.append(f" - {text}")
|
|
338
342
|
|
|
339
343
|
# 显示原文引用
|
|
340
344
|
origin = source['origin']
|
|
341
345
|
if origin:
|
|
342
346
|
text = origin.get('text', '')
|
|
343
347
|
if text:
|
|
344
|
-
|
|
348
|
+
output.append(f" 原文: {text}")
|
|
345
349
|
|
|
346
|
-
|
|
350
|
+
output.append("")
|
|
351
|
+
|
|
352
|
+
PrettyOutput.print("\n".join(output), OutputType.PROGRESS)
|
|
347
353
|
|
|
348
354
|
PrettyOutput.print(full_response, OutputType.RESULT)
|
|
349
355
|
|
jarvis/models/ollama.py
CHANGED
|
@@ -25,18 +25,24 @@ class OllamaPlatform(BasePlatform):
|
|
|
25
25
|
available_models = [model["name"] for model in response.json().get("models", [])]
|
|
26
26
|
|
|
27
27
|
if not available_models:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
message = (
|
|
29
|
+
"Need to download Ollama model first to use:\n"
|
|
30
|
+
"1. Install Ollama: https://ollama.ai\n"
|
|
31
|
+
"2. Download model:\n"
|
|
32
|
+
f" ollama pull {self.model_name}"
|
|
33
|
+
)
|
|
34
|
+
PrettyOutput.print(message, OutputType.INFO)
|
|
32
35
|
PrettyOutput.print("Ollama has no available models", OutputType.WARNING)
|
|
33
36
|
|
|
34
37
|
except requests.exceptions.ConnectionError:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
message = (
|
|
39
|
+
"Ollama service is not started or cannot be connected\n"
|
|
40
|
+
"Please ensure that you have:\n"
|
|
41
|
+
"1. Installed Ollama: https://ollama.ai\n"
|
|
42
|
+
"2. Started Ollama service\n"
|
|
43
|
+
"3. Service address configured correctly (default: http://localhost:11434)"
|
|
44
|
+
)
|
|
45
|
+
PrettyOutput.print(message, OutputType.WARNING)
|
|
40
46
|
|
|
41
47
|
|
|
42
48
|
self.messages = []
|
|
@@ -136,10 +142,10 @@ if __name__ == "__main__":
|
|
|
136
142
|
ollama = OllamaPlatform()
|
|
137
143
|
while True:
|
|
138
144
|
try:
|
|
139
|
-
message = get_single_line_input("
|
|
145
|
+
message = get_single_line_input("Input question (Ctrl+C to exit)")
|
|
140
146
|
ollama.chat_until_success(message)
|
|
141
147
|
except KeyboardInterrupt:
|
|
142
|
-
print("
|
|
148
|
+
print("Goodbye!")
|
|
143
149
|
break
|
|
144
150
|
except Exception as e:
|
|
145
151
|
PrettyOutput.print(f"Program exited with an exception: {str(e)}", OutputType.ERROR)
|
jarvis/models/openai.py
CHANGED
|
@@ -19,18 +19,21 @@ class OpenAIModel(BasePlatform):
|
|
|
19
19
|
self.system_message = ""
|
|
20
20
|
self.api_key = os.getenv("OPENAI_API_KEY")
|
|
21
21
|
if not self.api_key:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
message = (
|
|
23
|
+
"Need to set the following environment variables to use OpenAI model:\n"
|
|
24
|
+
" • OPENAI_API_KEY: API key\n"
|
|
25
|
+
" • OPENAI_API_BASE: (optional) API base address, default using https://api.openai.com/v1\n"
|
|
26
|
+
"You can set them in the following ways:\n"
|
|
27
|
+
"1. Create or edit ~/.jarvis/env file:\n"
|
|
28
|
+
" OPENAI_API_KEY=your_api_key\n"
|
|
29
|
+
" OPENAI_API_BASE=your_api_base\n"
|
|
30
|
+
" OPENAI_MODEL_NAME=your_model_name\n"
|
|
31
|
+
"2. Or set the environment variables directly:\n"
|
|
32
|
+
" export OPENAI_API_KEY=your_api_key\n"
|
|
33
|
+
" export OPENAI_API_BASE=your_api_base\n"
|
|
34
|
+
" export OPENAI_MODEL_NAME=your_model_name"
|
|
35
|
+
)
|
|
36
|
+
PrettyOutput.print(message, OutputType.INFO)
|
|
34
37
|
PrettyOutput.print("OPENAI_API_KEY is not set", OutputType.WARNING)
|
|
35
38
|
|
|
36
39
|
self.base_url = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1")
|
jarvis/models/oyi.py
CHANGED
|
@@ -32,6 +32,9 @@ class OyiModel(BasePlatform):
|
|
|
32
32
|
PrettyOutput.print("OYI_API_KEY is not set", OutputType.WARNING)
|
|
33
33
|
|
|
34
34
|
self.model_name = os.getenv("JARVIS_MODEL") or "deepseek-chat"
|
|
35
|
+
if self.model_name not in [m.split()[0] for m in self.get_available_models()]:
|
|
36
|
+
PrettyOutput.print(f"Warning: The selected model {self.model_name} is not in the available list", OutputType.WARNING)
|
|
37
|
+
|
|
35
38
|
|
|
36
39
|
def set_model_name(self, model_name: str):
|
|
37
40
|
"""Set model name"""
|
|
@@ -63,7 +66,7 @@ class OyiModel(BasePlatform):
|
|
|
63
66
|
"requestMsgCount": 65536,
|
|
64
67
|
"temperature": 0.8,
|
|
65
68
|
"speechVoice": "Alloy",
|
|
66
|
-
"max_tokens":
|
|
69
|
+
"max_tokens": 8192,
|
|
67
70
|
"chatPluginIds": []
|
|
68
71
|
})
|
|
69
72
|
}
|
|
@@ -168,19 +171,33 @@ class OyiModel(BasePlatform):
|
|
|
168
171
|
# 获取响应内容
|
|
169
172
|
response = requests.post(
|
|
170
173
|
f"{self.BASE_URL}/chatapi/chat/message/{message_id}",
|
|
171
|
-
headers=headers
|
|
174
|
+
headers=headers,
|
|
175
|
+
stream=True
|
|
172
176
|
)
|
|
173
177
|
|
|
174
178
|
if response.status_code == 200:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
full_response = ""
|
|
180
|
+
bin = b""
|
|
181
|
+
for chunk in response.iter_content(decode_unicode=True):
|
|
182
|
+
if chunk:
|
|
183
|
+
bin += chunk
|
|
184
|
+
try:
|
|
185
|
+
text = bin.decode('utf-8')
|
|
186
|
+
except UnicodeDecodeError:
|
|
187
|
+
continue
|
|
188
|
+
if not self.suppress_output:
|
|
189
|
+
PrettyOutput.print_stream(text)
|
|
190
|
+
full_response += text
|
|
191
|
+
bin = b""
|
|
192
|
+
|
|
193
|
+
PrettyOutput.print_stream_end()
|
|
194
|
+
|
|
195
|
+
self.messages.append({"role": "assistant", "content": full_response})
|
|
196
|
+
return full_response
|
|
179
197
|
else:
|
|
180
198
|
error_msg = f"Get response failed: {response.status_code}"
|
|
181
199
|
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
182
200
|
raise Exception(error_msg)
|
|
183
|
-
|
|
184
201
|
except Exception as e:
|
|
185
202
|
PrettyOutput.print(f"Chat failed: {str(e)}", OutputType.ERROR)
|
|
186
203
|
raise e
|
jarvis/models/registry.py
CHANGED
|
@@ -211,31 +211,7 @@ class PlatformRegistry:
|
|
|
211
211
|
except Exception as e:
|
|
212
212
|
PrettyOutput.print(f"Create platform failed: {str(e)}", OutputType.ERROR)
|
|
213
213
|
return None
|
|
214
|
-
|
|
215
|
-
def use_platforms(self, platform_names: List[str]):
|
|
216
|
-
"""Restrict available platforms to the specified list
|
|
217
|
-
|
|
218
|
-
Args:
|
|
219
|
-
platform_names: List of platform names to use
|
|
220
|
-
"""
|
|
221
|
-
self.platforms = {
|
|
222
|
-
name: cls
|
|
223
|
-
for name, cls in self.platforms.items()
|
|
224
|
-
if name in platform_names
|
|
225
|
-
}
|
|
226
214
|
|
|
227
|
-
def dont_use_platforms(self, platform_names: List[str]):
|
|
228
|
-
"""Restrict available platforms by excluding the specified list
|
|
229
|
-
|
|
230
|
-
Args:
|
|
231
|
-
platform_names: List of platform names to exclude
|
|
232
|
-
"""
|
|
233
|
-
self.platforms = {
|
|
234
|
-
name: cls
|
|
235
|
-
for name, cls in self.platforms.items()
|
|
236
|
-
if name not in platform_names
|
|
237
|
-
}
|
|
238
215
|
def get_available_platforms(self) -> List[str]:
|
|
239
216
|
"""Get available platform list"""
|
|
240
|
-
return list(self.platforms.keys())
|
|
241
|
-
|
|
217
|
+
return list(self.platforms.keys())
|
jarvis/tools/__init__.py
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
from jarvis.utils import OutputType, PrettyOutput, dont_use_local_model, find_git_root
|
|
3
|
+
from jarvis.jarvis_codebase.main import CodeBase
|
|
4
|
+
|
|
5
|
+
class AskCodebaseTool:
|
|
6
|
+
"""Tool for intelligent codebase querying and analysis using CodeBase"""
|
|
7
|
+
|
|
8
|
+
name = "ask_codebase"
|
|
9
|
+
description = "Ask questions about the codebase and get detailed analysis"
|
|
10
|
+
parameters = {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"question": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Question about the codebase"
|
|
16
|
+
},
|
|
17
|
+
"top_k": {
|
|
18
|
+
"type": "integer",
|
|
19
|
+
"description": "Number of most relevant files to analyze (optional)",
|
|
20
|
+
"default": 20
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"required": ["question"]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def check() -> bool:
|
|
28
|
+
return not dont_use_local_model()
|
|
29
|
+
|
|
30
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
31
|
+
"""Execute codebase analysis using CodeBase
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
args: Dictionary containing:
|
|
35
|
+
- question: The question to answer
|
|
36
|
+
- top_k: Optional number of files to analyze
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Dict containing:
|
|
40
|
+
- success: Boolean indicating success
|
|
41
|
+
- stdout: Analysis result
|
|
42
|
+
- stderr: Error message if any
|
|
43
|
+
"""
|
|
44
|
+
try:
|
|
45
|
+
question = args["question"]
|
|
46
|
+
top_k = args.get("top_k", 20)
|
|
47
|
+
|
|
48
|
+
PrettyOutput.print(f"Analyzing codebase for question: {question}", OutputType.INFO)
|
|
49
|
+
|
|
50
|
+
# Create new CodeBase instance
|
|
51
|
+
git_root = find_git_root()
|
|
52
|
+
codebase = CodeBase(git_root)
|
|
53
|
+
|
|
54
|
+
# Use ask_codebase method
|
|
55
|
+
response = codebase.ask_codebase(question, top_k)
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"success": True,
|
|
59
|
+
"stdout": response,
|
|
60
|
+
"stderr": ""
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
error_msg = f"Failed to analyze codebase: {str(e)}"
|
|
65
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
66
|
+
return {
|
|
67
|
+
"success": False,
|
|
68
|
+
"stdout": "",
|
|
69
|
+
"stderr": error_msg
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def main():
|
|
74
|
+
"""Command line interface for the tool"""
|
|
75
|
+
import argparse
|
|
76
|
+
|
|
77
|
+
parser = argparse.ArgumentParser(description='Ask questions about the codebase')
|
|
78
|
+
parser.add_argument('question', help='Question about the codebase')
|
|
79
|
+
parser.add_argument('--top-k', type=int, help='Number of files to analyze', default=20)
|
|
80
|
+
|
|
81
|
+
args = parser.parse_args()
|
|
82
|
+
|
|
83
|
+
tool = AskCodebaseTool()
|
|
84
|
+
result = tool.execute({
|
|
85
|
+
"question": args.question,
|
|
86
|
+
"top_k": args.top_k
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
if result["success"]:
|
|
90
|
+
print(result["stdout"])
|
|
91
|
+
else:
|
|
92
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
93
|
+
return 1
|
|
94
|
+
|
|
95
|
+
return 0
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
main()
|
jarvis/tools/ask_user.py
CHANGED
|
@@ -30,19 +30,11 @@ class AskUserTool:
|
|
|
30
30
|
question = args["question"]
|
|
31
31
|
|
|
32
32
|
# Display the question
|
|
33
|
-
PrettyOutput.print("
|
|
34
|
-
PrettyOutput.print(question, OutputType.SYSTEM)
|
|
33
|
+
PrettyOutput.print(f"Question: {question}", OutputType.SYSTEM)
|
|
35
34
|
|
|
36
35
|
# Get user input
|
|
37
36
|
user_response = get_multiline_input("Please enter your answer (input empty line to end)")
|
|
38
37
|
|
|
39
|
-
if user_response == "__interrupt__":
|
|
40
|
-
return {
|
|
41
|
-
"success": False,
|
|
42
|
-
"stdout": "",
|
|
43
|
-
"stderr": "User canceled input"
|
|
44
|
-
}
|
|
45
|
-
|
|
46
38
|
return {
|
|
47
39
|
"success": True,
|
|
48
40
|
"stdout": user_response,
|
jarvis/tools/chdir.py
CHANGED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
import subprocess
|
|
3
|
+
import yaml
|
|
4
|
+
from jarvis.models.registry import PlatformRegistry
|
|
5
|
+
from jarvis.tools.registry import ToolRegistry
|
|
6
|
+
from jarvis.utils import OutputType, PrettyOutput, init_env, find_git_root
|
|
7
|
+
from jarvis.agent import Agent
|
|
8
|
+
|
|
9
|
+
class CodeReviewTool:
|
|
10
|
+
name = "code_review"
|
|
11
|
+
description = "Autonomous code review agent for commit analysis"
|
|
12
|
+
parameters = {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"commit_sha": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Target commit SHA to analyze"
|
|
18
|
+
},
|
|
19
|
+
"requirement_desc": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Development goal to verify"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": ["commit_sha", "requirement_desc"]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
def __init__(self):
|
|
28
|
+
init_env()
|
|
29
|
+
self.repo_root = find_git_root()
|
|
30
|
+
|
|
31
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
32
|
+
try:
|
|
33
|
+
commit_sha = args["commit_sha"].strip()
|
|
34
|
+
requirement = args["requirement_desc"].strip()
|
|
35
|
+
|
|
36
|
+
system_prompt = """You are an autonomous code review expert. Perform in-depth analysis following these guidelines:
|
|
37
|
+
|
|
38
|
+
REVIEW FOCUS AREAS:
|
|
39
|
+
1. Requirement Alignment:
|
|
40
|
+
- Verify implementation matches original requirements
|
|
41
|
+
- Check for missing functionality
|
|
42
|
+
- Identify over-implementation
|
|
43
|
+
|
|
44
|
+
2. Code Quality:
|
|
45
|
+
- Code readability and structure
|
|
46
|
+
- Proper error handling
|
|
47
|
+
- Code duplication
|
|
48
|
+
- Adherence to style guides
|
|
49
|
+
- Meaningful variable/method names
|
|
50
|
+
|
|
51
|
+
3. Security:
|
|
52
|
+
- Input validation
|
|
53
|
+
- Authentication/Authorization checks
|
|
54
|
+
- Sensitive data handling
|
|
55
|
+
- Potential injection vulnerabilities
|
|
56
|
+
- Secure communication practices
|
|
57
|
+
|
|
58
|
+
4. Testing:
|
|
59
|
+
- Test coverage for new code
|
|
60
|
+
- Edge case handling
|
|
61
|
+
- Test readability and maintainability
|
|
62
|
+
- Missing test scenarios
|
|
63
|
+
|
|
64
|
+
5. Performance:
|
|
65
|
+
- Algorithm efficiency
|
|
66
|
+
- Unnecessary resource consumption
|
|
67
|
+
- Proper caching mechanisms
|
|
68
|
+
- Database query optimization
|
|
69
|
+
|
|
70
|
+
6. Maintainability:
|
|
71
|
+
- Documentation quality
|
|
72
|
+
- Logging and monitoring
|
|
73
|
+
- Configuration management
|
|
74
|
+
- Technical debt indicators
|
|
75
|
+
|
|
76
|
+
7. Operational Considerations:
|
|
77
|
+
- Backward compatibility
|
|
78
|
+
- Migration script safety
|
|
79
|
+
- Environment-specific configurations
|
|
80
|
+
- Deployment impacts
|
|
81
|
+
|
|
82
|
+
REVIEW PROCESS:
|
|
83
|
+
1. Retrieve full commit context using git commands
|
|
84
|
+
2. Analyze code changes line-by-line
|
|
85
|
+
3. Cross-reference with project standards
|
|
86
|
+
4. Verify test coverage adequacy
|
|
87
|
+
5. Check documentation updates
|
|
88
|
+
6. Generate prioritized findings
|
|
89
|
+
|
|
90
|
+
OUTPUT REQUIREMENTS:
|
|
91
|
+
- Categorize issues by severity (Critical/Major/Minor)
|
|
92
|
+
- Reference specific code locations
|
|
93
|
+
- Provide concrete examples
|
|
94
|
+
- Suggest actionable improvements
|
|
95
|
+
- Highlight security risks clearly
|
|
96
|
+
- Separate technical debt from blockers"""
|
|
97
|
+
|
|
98
|
+
summary_prompt = """Please generate a concise summary report of the code review, format as yaml:
|
|
99
|
+
<REPORT>
|
|
100
|
+
- file: xxxx.py
|
|
101
|
+
location: [start_line_number, end_line_number]
|
|
102
|
+
description:
|
|
103
|
+
severity:
|
|
104
|
+
suggestion:
|
|
105
|
+
</REPORT>
|
|
106
|
+
|
|
107
|
+
Please describe in concise bullet points, highlighting important information.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
tool_registry = ToolRegistry()
|
|
111
|
+
tool_registry.use_tools(["execute_shell", "read_code", "ask_user", "ask_codebase", "find_in_codebase", "create_ctags_agent"])
|
|
112
|
+
tool_registry.dont_use_tools(["code_review"])
|
|
113
|
+
|
|
114
|
+
review_agent = Agent(
|
|
115
|
+
name="Code Review Agent",
|
|
116
|
+
platform=PlatformRegistry().get_thinking_platform(),
|
|
117
|
+
system_prompt=system_prompt,
|
|
118
|
+
is_sub_agent=True,
|
|
119
|
+
tool_registry=tool_registry,
|
|
120
|
+
summary_prompt=summary_prompt,
|
|
121
|
+
auto_complete=True
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
result = review_agent.run(
|
|
125
|
+
f"Analyze commit {commit_sha} for requirement: {requirement}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
"success": True,
|
|
130
|
+
"stdout": {"report": result},
|
|
131
|
+
"stderr": ""
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
except Exception as e:
|
|
135
|
+
return {
|
|
136
|
+
"success": False,
|
|
137
|
+
"stdout": {},
|
|
138
|
+
"stderr": f"Review failed: {str(e)}"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
def main():
|
|
142
|
+
"""CLI entry point"""
|
|
143
|
+
import argparse
|
|
144
|
+
|
|
145
|
+
parser = argparse.ArgumentParser(description='Autonomous code review tool')
|
|
146
|
+
parser.add_argument('--commit', required=True)
|
|
147
|
+
parser.add_argument('--requirement', required=True)
|
|
148
|
+
args = parser.parse_args()
|
|
149
|
+
|
|
150
|
+
tool = CodeReviewTool()
|
|
151
|
+
result = tool.execute({
|
|
152
|
+
"commit_sha": args.commit,
|
|
153
|
+
"requirement_desc": args.requirement
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
if result["success"]:
|
|
157
|
+
PrettyOutput.print("Autonomous Review Result:", OutputType.INFO)
|
|
158
|
+
print(yaml.dump(result["stdout"], allow_unicode=True))
|
|
159
|
+
else:
|
|
160
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
main()
|