jarvis-ai-assistant 0.1.125__py3-none-any.whl → 0.1.126__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/jarvis_agent/__init__.py +116 -116
- jarvis/jarvis_code_agent/code_agent.py +96 -100
- jarvis/jarvis_code_agent/patch.py +39 -47
- jarvis/jarvis_code_agent/shell_input_handler.py +22 -0
- jarvis/jarvis_codebase/main.py +83 -84
- jarvis/jarvis_dev/main.py +691 -713
- jarvis/jarvis_lsp/base.py +0 -12
- jarvis/jarvis_lsp/cpp.py +0 -9
- jarvis/jarvis_lsp/go.py +0 -9
- jarvis/jarvis_lsp/python.py +0 -28
- jarvis/jarvis_lsp/registry.py +0 -1
- jarvis/jarvis_lsp/rust.py +0 -9
- jarvis/jarvis_multi_agent/__init__.py +52 -52
- jarvis/jarvis_tools/ask_codebase.py +6 -6
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +8 -8
- jarvis/jarvis_tools/code_review.py +6 -6
- jarvis/jarvis_tools/create_code_agent.py +4 -4
- jarvis/jarvis_tools/create_sub_agent.py +7 -7
- jarvis/jarvis_tools/execute_shell.py +54 -21
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_operation.py +36 -8
- jarvis/jarvis_tools/git_commiter.py +16 -16
- jarvis/jarvis_tools/lsp_find_definition.py +7 -7
- jarvis/jarvis_tools/lsp_prepare_rename.py +7 -7
- jarvis/jarvis_tools/methodology.py +6 -6
- jarvis/jarvis_tools/rag.py +5 -5
- jarvis/jarvis_tools/read_webpage.py +2 -2
- jarvis/jarvis_tools/registry.py +139 -139
- jarvis/jarvis_tools/search_web.py +5 -5
- jarvis/jarvis_tools/select_code_files.py +3 -3
- jarvis/jarvis_tools/tool_generator.py +33 -34
- jarvis/jarvis_utils/methodology.py +5 -5
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.126.dist-info}/METADATA +31 -17
- jarvis_ai_assistant-0.1.126.dist-info/RECORD +74 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.126.dist-info}/WHEEL +1 -1
- jarvis/jarvis_tools/lsp_validate_edit.py +0 -141
- jarvis/jarvis_tools/read_code.py +0 -192
- jarvis_ai_assistant-0.1.125.dist-info/RECORD +0 -75
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.126.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.126.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.126.dist-info}/top_level.txt +0 -0
jarvis/jarvis_lsp/base.py
CHANGED
|
@@ -125,18 +125,6 @@ class BaseLSP(ABC):
|
|
|
125
125
|
"""
|
|
126
126
|
return None
|
|
127
127
|
|
|
128
|
-
@abstractmethod
|
|
129
|
-
def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
|
|
130
|
-
"""Validate if proposed edit is syntactically correct.
|
|
131
|
-
|
|
132
|
-
Args:
|
|
133
|
-
file_path: Path to the file
|
|
134
|
-
edit: Edit operation in LSP format
|
|
135
|
-
|
|
136
|
-
Returns:
|
|
137
|
-
bool: True if edit is valid
|
|
138
|
-
"""
|
|
139
|
-
return False
|
|
140
128
|
|
|
141
129
|
def shutdown(self):
|
|
142
130
|
"""Shutdown LSP server cleanly."""
|
jarvis/jarvis_lsp/cpp.py
CHANGED
|
@@ -114,15 +114,6 @@ class CPPLSP(BaseLSP):
|
|
|
114
114
|
})
|
|
115
115
|
return result
|
|
116
116
|
|
|
117
|
-
def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
|
|
118
|
-
# Send workspace/willRenameFiles request to check validity
|
|
119
|
-
result = self._send_request("workspace/willRenameFiles", {
|
|
120
|
-
"files": [{
|
|
121
|
-
"oldUri": f"file://{file_path}",
|
|
122
|
-
"newUri": f"file://{file_path}.tmp"
|
|
123
|
-
}]
|
|
124
|
-
})
|
|
125
|
-
return bool(result)
|
|
126
117
|
|
|
127
118
|
def shutdown(self):
|
|
128
119
|
if self.clangd_process:
|
jarvis/jarvis_lsp/go.py
CHANGED
|
@@ -120,15 +120,6 @@ class GoLSP(BaseLSP):
|
|
|
120
120
|
})
|
|
121
121
|
return result
|
|
122
122
|
|
|
123
|
-
def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
|
|
124
|
-
# Send workspace/willRenameFiles request to check validity
|
|
125
|
-
result = self._send_request("workspace/willRenameFiles", {
|
|
126
|
-
"files": [{
|
|
127
|
-
"oldUri": f"file://{file_path}",
|
|
128
|
-
"newUri": f"file://{file_path}.tmp"
|
|
129
|
-
}]
|
|
130
|
-
})
|
|
131
|
-
return bool(result)
|
|
132
123
|
|
|
133
124
|
def shutdown(self):
|
|
134
125
|
if self.gopls_process:
|
jarvis/jarvis_lsp/python.py
CHANGED
|
@@ -100,34 +100,6 @@ class PythonLSP(BaseLSP):
|
|
|
100
100
|
return None
|
|
101
101
|
return None
|
|
102
102
|
|
|
103
|
-
def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
|
|
104
|
-
try:
|
|
105
|
-
# Simple syntax check of the edited content
|
|
106
|
-
content = ""
|
|
107
|
-
with open(file_path, 'r') as f:
|
|
108
|
-
content = f.read()
|
|
109
|
-
|
|
110
|
-
# Apply edit
|
|
111
|
-
start = edit["range"]["start"]
|
|
112
|
-
end = edit["range"]["end"]
|
|
113
|
-
new_text = edit["newText"]
|
|
114
|
-
|
|
115
|
-
lines = content.splitlines(True)
|
|
116
|
-
before = "".join(lines[:start["line"]])
|
|
117
|
-
after = "".join(lines[end["line"] + 1:])
|
|
118
|
-
current_line = lines[start["line"]]
|
|
119
|
-
|
|
120
|
-
edited_line = (current_line[:start["character"]] +
|
|
121
|
-
new_text +
|
|
122
|
-
current_line[end["character"]:])
|
|
123
|
-
|
|
124
|
-
new_content = before + edited_line + after
|
|
125
|
-
|
|
126
|
-
# Check if new content is valid Python
|
|
127
|
-
jedi.Script(code=new_content)
|
|
128
|
-
return True
|
|
129
|
-
except Exception:
|
|
130
|
-
return False
|
|
131
103
|
|
|
132
104
|
def shutdown(self):
|
|
133
105
|
self.script_cache.clear()
|
jarvis/jarvis_lsp/registry.py
CHANGED
jarvis/jarvis_lsp/rust.py
CHANGED
|
@@ -122,15 +122,6 @@ class RustLSP(BaseLSP):
|
|
|
122
122
|
})
|
|
123
123
|
return result
|
|
124
124
|
|
|
125
|
-
def validate_edit(self, file_path: str, edit: Dict[str, Any]) -> bool:
|
|
126
|
-
# Send workspace/willRenameFiles request to check validity
|
|
127
|
-
result = self._send_request("workspace/willRenameFiles", {
|
|
128
|
-
"files": [{
|
|
129
|
-
"oldUri": f"file://{file_path}",
|
|
130
|
-
"newUri": f"file://{file_path}.tmp"
|
|
131
|
-
}]
|
|
132
|
-
})
|
|
133
|
-
return bool(result)
|
|
134
125
|
|
|
135
126
|
def shutdown(self):
|
|
136
127
|
if self.analyzer_process:
|
|
@@ -32,65 +32,65 @@ class MultiAgent(OutputHandler):
|
|
|
32
32
|
|
|
33
33
|
def prompt(self) -> str:
|
|
34
34
|
return f"""
|
|
35
|
-
# 🤖
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# 🎯
|
|
39
|
-
##
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
##
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
# 📝
|
|
35
|
+
# 🤖 多智能体消息处理系统
|
|
36
|
+
您是多智能体系统的一部分,通过结构化消息进行通信。
|
|
37
|
+
|
|
38
|
+
# 🎯 核心规则
|
|
39
|
+
## 关键操作规则
|
|
40
|
+
- 每轮只能执行一个操作:
|
|
41
|
+
- 要么使用一个工具(文件操作、询问用户等)
|
|
42
|
+
- 要么发送一条消息给其他智能体
|
|
43
|
+
- 切勿在同一轮中同时进行这两种操作
|
|
44
|
+
|
|
45
|
+
## 消息流控制
|
|
46
|
+
- 发送消息后等待响应
|
|
47
|
+
- 处理响应后再进行下一步操作
|
|
48
|
+
- 切勿同时发送多条消息
|
|
49
|
+
- 切勿将消息与工具调用混合使用
|
|
50
|
+
|
|
51
|
+
# 📝 消息格式
|
|
52
52
|
```
|
|
53
53
|
<SEND_MESSAGE>
|
|
54
|
-
to:
|
|
54
|
+
to: 智能体名称 # 目标智能体名称
|
|
55
55
|
content: |
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
消息内容 # 消息内容
|
|
57
|
+
可使用多行 # 如果需要
|
|
58
|
+
保持正确的缩进
|
|
59
59
|
</SEND_MESSAGE>
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
# 🔄
|
|
63
|
-
1.
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
62
|
+
# 🔄 操作顺序
|
|
63
|
+
1. 选择最重要的操作
|
|
64
|
+
- 评估优先级
|
|
65
|
+
- 选择一个操作
|
|
66
|
+
- 执行该操作
|
|
67
67
|
|
|
68
|
-
2.
|
|
69
|
-
-
|
|
70
|
-
-
|
|
71
|
-
-
|
|
68
|
+
2. 等待响应
|
|
69
|
+
- 处理结果/响应
|
|
70
|
+
- 计划下一步操作
|
|
71
|
+
- 等待下一轮
|
|
72
72
|
|
|
73
|
-
3.
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
73
|
+
3. 处理响应
|
|
74
|
+
- 处理收到的消息
|
|
75
|
+
- 需要时回复发送者
|
|
76
|
+
- 根据响应继续任务
|
|
77
77
|
|
|
78
|
-
# 👥
|
|
78
|
+
# 👥 可用智能体
|
|
79
79
|
{chr(10).join([f"- {c.name}: {c.description}" for c in self.agents_config])}
|
|
80
80
|
|
|
81
|
-
# ❗
|
|
82
|
-
1.
|
|
83
|
-
2.
|
|
84
|
-
3.
|
|
85
|
-
4.
|
|
86
|
-
5.
|
|
87
|
-
|
|
88
|
-
# 💡
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
81
|
+
# ❗ 重要规则
|
|
82
|
+
1. 每轮只能执行一个操作
|
|
83
|
+
2. 等待响应
|
|
84
|
+
3. 处理后再进行下一步
|
|
85
|
+
4. 回复消息
|
|
86
|
+
5. 需要时转发任务
|
|
87
|
+
|
|
88
|
+
# 💡 提示
|
|
89
|
+
- 第一个操作将被执行
|
|
90
|
+
- 额外的操作将被忽略
|
|
91
|
+
- 总是先处理响应
|
|
92
|
+
- 需要时发送消息以继续任务
|
|
93
|
+
- 处理并回复收到的消息
|
|
94
94
|
"""
|
|
95
95
|
|
|
96
96
|
def can_handle(self, response: str) -> bool:
|
|
@@ -161,10 +161,10 @@ from: {last_agent}
|
|
|
161
161
|
content: {msg['content']}
|
|
162
162
|
"""
|
|
163
163
|
if msg['to'] not in self.agents:
|
|
164
|
-
PrettyOutput.print(f"
|
|
165
|
-
msg = self.agents[last_agent].run(f"
|
|
164
|
+
PrettyOutput.print(f"未找到智能体 {msg['to']},正在重试...", OutputType.WARNING)
|
|
165
|
+
msg = self.agents[last_agent].run(f"未找到智能体 {msg['to']},可用智能体列表: {self.agents.keys()}")
|
|
166
166
|
continue
|
|
167
|
-
PrettyOutput.print(f"{last_agent}
|
|
167
|
+
PrettyOutput.print(f"{last_agent} 正在向 {msg['to']} 发送消息...", OutputType.INFO)
|
|
168
168
|
last_agent = self.agents[msg['to']].name
|
|
169
169
|
msg = self.agents[msg['to']].run(prompt)
|
|
170
|
-
return ""
|
|
170
|
+
return ""
|
|
@@ -5,20 +5,20 @@ from jarvis.jarvis_utils.git_utils import find_git_root
|
|
|
5
5
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
6
6
|
|
|
7
7
|
class AskCodebaseTool:
|
|
8
|
-
"""
|
|
9
|
-
|
|
8
|
+
"""用于智能代码库查询和分析的工具"""
|
|
9
|
+
|
|
10
10
|
name = "ask_codebase"
|
|
11
|
-
description = "
|
|
11
|
+
description = "查询代码库问题并获取详细分析"
|
|
12
12
|
parameters = {
|
|
13
13
|
"type": "object",
|
|
14
14
|
"properties": {
|
|
15
15
|
"question": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"description": "
|
|
17
|
+
"description": "关于代码库的问题"
|
|
18
18
|
},
|
|
19
19
|
"top_k": {
|
|
20
20
|
"type": "integer",
|
|
21
|
-
"description": "
|
|
21
|
+
"description": "要分析的最相关文件数量(可选)",
|
|
22
22
|
"default": 20
|
|
23
23
|
}
|
|
24
24
|
},
|
|
@@ -98,4 +98,4 @@ def main():
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
if __name__ == "__main__":
|
|
101
|
-
main()
|
|
101
|
+
main()
|
jarvis/jarvis_tools/ask_user.py
CHANGED
|
@@ -5,13 +5,13 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
5
5
|
|
|
6
6
|
class AskUserTool:
|
|
7
7
|
name="ask_user"
|
|
8
|
-
description="""
|
|
8
|
+
description="""当完成任务所需的信息缺失或关键决策信息不足时,向用户提问。用户可以输入多行文本,以空行结束。使用场景:1. 需要用户提供更多信息以完成任务;2. 需要用户做出关键决策;3. 需要用户确认重要操作;4. 需要用户提供额外信息"""
|
|
9
9
|
parameters={
|
|
10
10
|
"type": "object",
|
|
11
11
|
"properties": {
|
|
12
12
|
"question": {
|
|
13
13
|
"type": "string",
|
|
14
|
-
"description": "
|
|
14
|
+
"description": "要向用户提出的问题"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"required": ["question"]
|
jarvis/jarvis_tools/base.py
CHANGED
|
@@ -7,7 +7,7 @@ class Tool:
|
|
|
7
7
|
"""
|
|
8
8
|
初始化工具对象
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
参数:
|
|
11
11
|
name (str): 工具名称
|
|
12
12
|
description (str): 工具描述
|
|
13
13
|
parameters (Dict): 工具参数定义
|
|
@@ -28,10 +28,10 @@ class Tool:
|
|
|
28
28
|
"""
|
|
29
29
|
执行工具函数
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
参数:
|
|
32
32
|
arguments (Dict): 工具执行所需的参数
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
返回:
|
|
35
35
|
Dict[str, Any]: 工具执行结果
|
|
36
36
|
"""
|
|
37
|
-
return self.func(arguments)
|
|
37
|
+
return self.func(arguments)
|
jarvis/jarvis_tools/chdir.py
CHANGED
|
@@ -3,13 +3,13 @@ import os
|
|
|
3
3
|
|
|
4
4
|
class ChdirTool:
|
|
5
5
|
name = "chdir"
|
|
6
|
-
description = "
|
|
6
|
+
description = "更改当前工作目录"
|
|
7
7
|
parameters = {
|
|
8
8
|
"type": "object",
|
|
9
9
|
"properties": {
|
|
10
10
|
"path": {
|
|
11
11
|
"type": "string",
|
|
12
|
-
"description": "
|
|
12
|
+
"description": "要切换到的目录路径,支持相对路径和绝对路径"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"required": ["path"]
|
|
@@ -45,7 +45,7 @@ class ChdirTool:
|
|
|
45
45
|
return {
|
|
46
46
|
"success": False,
|
|
47
47
|
"stdout": "",
|
|
48
|
-
"stderr": f"
|
|
48
|
+
"stderr": f"目录不存在: {path}"
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
# Ensure the path points to a directory, not a file
|
|
@@ -53,7 +53,7 @@ class ChdirTool:
|
|
|
53
53
|
return {
|
|
54
54
|
"success": False,
|
|
55
55
|
"stdout": "",
|
|
56
|
-
"stderr": f"
|
|
56
|
+
"stderr": f"路径不是目录: {path}"
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
# Capture current directory and attempt to change to new path
|
|
@@ -62,7 +62,7 @@ class ChdirTool:
|
|
|
62
62
|
|
|
63
63
|
return {
|
|
64
64
|
"success": True,
|
|
65
|
-
"stdout": f"
|
|
65
|
+
"stdout": f"成功切换工作目录:\n原目录: {old_path}\n新目录: {path}",
|
|
66
66
|
"stderr": ""
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -71,12 +71,12 @@ class ChdirTool:
|
|
|
71
71
|
return {
|
|
72
72
|
"success": False,
|
|
73
73
|
"stdout": "",
|
|
74
|
-
"stderr": f"
|
|
74
|
+
"stderr": f"无权限访问目录: {path}"
|
|
75
75
|
}
|
|
76
76
|
# Catch-all for any other unexpected errors during directory change
|
|
77
77
|
except Exception as e:
|
|
78
78
|
return {
|
|
79
79
|
"success": False,
|
|
80
80
|
"stdout": "",
|
|
81
|
-
"stderr": f"
|
|
82
|
-
}
|
|
81
|
+
"stderr": f"切换目录失败: {str(e)}"
|
|
82
|
+
}
|
|
@@ -10,27 +10,27 @@ from jarvis.jarvis_utils.utils import init_env
|
|
|
10
10
|
|
|
11
11
|
class CodeReviewTool:
|
|
12
12
|
name = "code_review"
|
|
13
|
-
description = "
|
|
13
|
+
description = "自动代码审查工具,用于分析代码变更"
|
|
14
14
|
parameters = {
|
|
15
15
|
"type": "object",
|
|
16
16
|
"properties": {
|
|
17
17
|
"review_type": {
|
|
18
18
|
"type": "string",
|
|
19
|
-
"description": "
|
|
19
|
+
"description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围",
|
|
20
20
|
"enum": ["commit", "current", "range"],
|
|
21
21
|
"default": "current"
|
|
22
22
|
},
|
|
23
23
|
"commit_sha": {
|
|
24
24
|
"type": "string",
|
|
25
|
-
"description": "
|
|
25
|
+
"description": "要分析的提交SHA(review_type='commit'时必填)"
|
|
26
26
|
},
|
|
27
27
|
"start_commit": {
|
|
28
28
|
"type": "string",
|
|
29
|
-
"description": "
|
|
29
|
+
"description": "起始提交SHA(review_type='range'时必填)"
|
|
30
30
|
},
|
|
31
31
|
"end_commit": {
|
|
32
32
|
"type": "string",
|
|
33
|
-
"description": "
|
|
33
|
+
"description": "结束提交SHA(review_type='range'时必填)"
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"required": []
|
|
@@ -243,4 +243,4 @@ def main():
|
|
|
243
243
|
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
244
244
|
|
|
245
245
|
if __name__ == "__main__":
|
|
246
|
-
main()
|
|
246
|
+
main()
|
|
@@ -6,12 +6,12 @@ from jarvis.jarvis_utils.git_utils import get_latest_commit_hash, has_uncommitte
|
|
|
6
6
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
7
|
|
|
8
8
|
class CreateCodeAgentTool:
|
|
9
|
-
"""
|
|
9
|
+
"""用于管理代码开发工作流的工具"""
|
|
10
10
|
|
|
11
11
|
name = "create_code_agent"
|
|
12
|
-
description = "
|
|
12
|
+
description = "技术代码实现和开发过程管理工具"
|
|
13
13
|
parameters = {
|
|
14
|
-
"requirement": "
|
|
14
|
+
"requirement": "代码实现的技术规范"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
|
|
@@ -109,4 +109,4 @@ def main():
|
|
|
109
109
|
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
110
110
|
|
|
111
111
|
if __name__ == "__main__":
|
|
112
|
-
main()
|
|
112
|
+
main()
|
|
@@ -8,32 +8,32 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class SubAgentTool:
|
|
10
10
|
name = "create_sub_agent"
|
|
11
|
-
description = "
|
|
11
|
+
description = "创建子代理以处理特定任务,子代理将生成任务总结报告"
|
|
12
12
|
parameters = {
|
|
13
13
|
"type": "object",
|
|
14
14
|
"properties": {
|
|
15
15
|
"agent_name": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"description": "
|
|
17
|
+
"description": "子代理名称"
|
|
18
18
|
},
|
|
19
19
|
"task": {
|
|
20
20
|
"type": "string",
|
|
21
|
-
"description": "
|
|
21
|
+
"description": "要完成的特定任务"
|
|
22
22
|
},
|
|
23
23
|
"context": {
|
|
24
24
|
"type": "string",
|
|
25
|
-
"description": "
|
|
25
|
+
"description": "与任务相关的上下文信息",
|
|
26
26
|
"default": ""
|
|
27
27
|
},
|
|
28
28
|
"goal": {
|
|
29
29
|
"type": "string",
|
|
30
|
-
"description": "
|
|
30
|
+
"description": "任务的完成目标",
|
|
31
31
|
"default": ""
|
|
32
32
|
},
|
|
33
33
|
"files": {
|
|
34
34
|
"type": "array",
|
|
35
35
|
"items": {"type": "string"},
|
|
36
|
-
"description": "
|
|
36
|
+
"description": "相关文件路径列表,用于文件问答和处理",
|
|
37
37
|
"default": []
|
|
38
38
|
}
|
|
39
39
|
},
|
|
@@ -83,4 +83,4 @@ class SubAgentTool:
|
|
|
83
83
|
"success": False,
|
|
84
84
|
"stdout": "",
|
|
85
85
|
"stderr": f"Sub-agent execution failed: {str(e)}"
|
|
86
|
-
}
|
|
86
|
+
}
|
|
@@ -1,57 +1,89 @@
|
|
|
1
|
+
# Shell command execution module
|
|
2
|
+
#
|
|
3
|
+
# Provides functionality to execute shell commands safely with:
|
|
4
|
+
# - Command escaping
|
|
5
|
+
# - Output capturing
|
|
6
|
+
# - Temporary file management
|
|
7
|
+
# - Error handling
|
|
1
8
|
from typing import Dict, Any
|
|
2
9
|
import os
|
|
3
10
|
import tempfile
|
|
4
11
|
from pathlib import Path
|
|
5
|
-
|
|
6
12
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
13
|
class ShellTool:
|
|
14
|
+
"""Shell command execution tool
|
|
15
|
+
|
|
16
|
+
Attributes:
|
|
17
|
+
name: Tool identifier used in API
|
|
18
|
+
description: Tool description for API documentation
|
|
19
|
+
parameters: JSON schema for command parameters
|
|
20
|
+
"""
|
|
12
21
|
name = "execute_shell"
|
|
13
|
-
description = ""
|
|
14
|
-
|
|
22
|
+
description = "执行Shell命令并返回结果"
|
|
15
23
|
parameters = {
|
|
16
24
|
"type": "object",
|
|
17
25
|
"properties": {
|
|
18
26
|
"command": {
|
|
19
27
|
"type": "string",
|
|
20
|
-
"description": "Shell
|
|
28
|
+
"description": "要执行的Shell命令"
|
|
21
29
|
}
|
|
22
30
|
},
|
|
23
31
|
"required": ["command"]
|
|
24
32
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
33
|
def _escape_command(self, cmd: str) -> str:
|
|
28
|
-
"""Escape special characters in command
|
|
34
|
+
"""Escape special characters in command to prevent shell injection
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
cmd: Raw command string
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Escaped command string with single quotes properly handled
|
|
41
|
+
"""
|
|
29
42
|
return cmd.replace("'", "'\"'\"'")
|
|
30
|
-
|
|
31
43
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
32
|
-
"""Execute shell command
|
|
44
|
+
"""Execute shell command and capture output
|
|
45
|
+
|
|
46
|
+
Steps:
|
|
47
|
+
1. Validate and clean input command
|
|
48
|
+
2. Create temporary file for output capture
|
|
49
|
+
3. Execute command with output redirection
|
|
50
|
+
4. Read and process output file
|
|
51
|
+
5. Clean up temporary resources
|
|
52
|
+
6. Return structured results
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
args: Dictionary containing 'command' parameter
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary with:
|
|
59
|
+
- success: Boolean indicating command execution status
|
|
60
|
+
- stdout: Command output
|
|
61
|
+
- stderr: Error message if execution failed
|
|
62
|
+
"""
|
|
33
63
|
try:
|
|
64
|
+
# Get and clean command input
|
|
34
65
|
command = args["command"].strip()
|
|
35
66
|
|
|
36
|
-
# Generate temporary file name
|
|
67
|
+
# Generate temporary file name using process ID for uniqueness
|
|
37
68
|
output_file = os.path.join(tempfile.gettempdir(), f"jarvis_shell_{os.getpid()}.log")
|
|
38
69
|
|
|
39
|
-
# Escape special characters in command
|
|
70
|
+
# Escape special characters in command to prevent injection
|
|
40
71
|
escaped_command = self._escape_command(command)
|
|
41
72
|
|
|
42
|
-
#
|
|
73
|
+
# Use script command to capture both stdout and stderr
|
|
43
74
|
tee_command = f"script -q -c '{escaped_command}' {output_file}"
|
|
44
75
|
|
|
76
|
+
# Log command execution
|
|
45
77
|
PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
|
|
46
78
|
|
|
47
|
-
# Execute command
|
|
79
|
+
# Execute command and capture return code
|
|
48
80
|
return_code = os.system(tee_command)
|
|
49
81
|
|
|
50
|
-
# Read output file
|
|
82
|
+
# Read and process output file
|
|
51
83
|
try:
|
|
52
84
|
with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
|
|
53
85
|
output = f.read()
|
|
54
|
-
# Remove header and footer added by script
|
|
86
|
+
# Remove header and footer added by script command
|
|
55
87
|
if output:
|
|
56
88
|
lines = output.splitlines()
|
|
57
89
|
if len(lines) > 2:
|
|
@@ -62,6 +94,7 @@ class ShellTool:
|
|
|
62
94
|
# Clean up temporary file
|
|
63
95
|
Path(output_file).unlink(missing_ok=True)
|
|
64
96
|
|
|
97
|
+
# Return successful result
|
|
65
98
|
return {
|
|
66
99
|
"success": True,
|
|
67
100
|
"stdout": output,
|
|
@@ -69,7 +102,7 @@ class ShellTool:
|
|
|
69
102
|
}
|
|
70
103
|
|
|
71
104
|
except Exception as e:
|
|
72
|
-
# Ensure temporary file is cleaned up
|
|
105
|
+
# Ensure temporary file is cleaned up even if error occurs
|
|
73
106
|
if 'output_file' in locals():
|
|
74
107
|
Path(output_file).unlink(missing_ok=True)
|
|
75
108
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
@@ -77,4 +110,4 @@ class ShellTool:
|
|
|
77
110
|
"success": False,
|
|
78
111
|
"stdout": "",
|
|
79
112
|
"stderr": str(e)
|
|
80
|
-
}
|
|
113
|
+
}
|
|
@@ -8,13 +8,13 @@ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
|
8
8
|
|
|
9
9
|
class ShellScriptTool:
|
|
10
10
|
name = "execute_shell_script"
|
|
11
|
-
description = ""
|
|
11
|
+
description = "执行Shell脚本文件并返回结果"
|
|
12
12
|
parameters = {
|
|
13
13
|
"type": "object",
|
|
14
14
|
"properties": {
|
|
15
15
|
"script_content": {
|
|
16
16
|
"type": "string",
|
|
17
|
-
"description": "
|
|
17
|
+
"description": "要执行的Shell脚本内容"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"required": ["script_content"]
|
|
@@ -55,4 +55,4 @@ class ShellScriptTool:
|
|
|
55
55
|
"success": False,
|
|
56
56
|
"stdout": "",
|
|
57
57
|
"stderr": str(e)
|
|
58
|
-
}
|
|
58
|
+
}
|