jarvis-ai-assistant 0.1.125__py3-none-any.whl → 0.1.128__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 +205 -187
- jarvis/jarvis_code_agent/code_agent.py +116 -109
- jarvis/jarvis_code_agent/patch.py +157 -138
- jarvis/jarvis_code_agent/shell_input_handler.py +22 -0
- jarvis/jarvis_codebase/main.py +314 -288
- jarvis/jarvis_dev/main.py +695 -716
- 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_platform/base.py +6 -5
- jarvis/jarvis_platform_manager/main.py +1 -1
- jarvis/jarvis_rag/main.py +250 -186
- jarvis/jarvis_smart_shell/main.py +0 -1
- jarvis/jarvis_tools/ask_codebase.py +10 -9
- jarvis/jarvis_tools/ask_user.py +2 -2
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +28 -28
- jarvis/jarvis_tools/code_review.py +44 -39
- jarvis/jarvis_tools/create_code_agent.py +4 -4
- jarvis/jarvis_tools/create_sub_agent.py +7 -7
- jarvis/jarvis_tools/execute_shell.py +53 -23
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_operation.py +70 -41
- jarvis/jarvis_tools/git_commiter.py +61 -51
- 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 +52 -32
- jarvis/jarvis_tools/registry.py +167 -180
- jarvis/jarvis_tools/search_web.py +66 -41
- jarvis/jarvis_tools/select_code_files.py +3 -3
- jarvis/jarvis_tools/tool_generator.py +68 -55
- jarvis/jarvis_utils/methodology.py +77 -59
- jarvis/jarvis_utils/output.py +1 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/METADATA +31 -17
- jarvis_ai_assistant-0.1.128.dist-info/RECORD +74 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.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.128.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/top_level.txt +0 -0
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,80 +3,80 @@ 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"]
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
19
|
-
"""
|
|
19
|
+
"""执行目录切换操作,并提供全面的错误处理。
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
args:
|
|
21
|
+
参数:
|
|
22
|
+
args: 包含 'path' 键的字典,目标目录路径
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- success:
|
|
27
|
-
- stdout:
|
|
28
|
-
- stderr:
|
|
24
|
+
返回:
|
|
25
|
+
字典,包含以下内容:
|
|
26
|
+
- success: 布尔值,表示操作状态
|
|
27
|
+
- stdout: 成功消息或空字符串
|
|
28
|
+
- stderr: 错误消息或空字符串
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
30
|
+
异常处理:
|
|
31
|
+
处理并返回适当的错误消息:
|
|
32
|
+
- 不存在的路径
|
|
33
|
+
- 非目录路径
|
|
34
|
+
- 权限错误
|
|
35
|
+
- 其他通用异常
|
|
36
36
|
"""
|
|
37
|
-
#
|
|
37
|
+
# 主执行块,包含全面的错误处理
|
|
38
38
|
try:
|
|
39
|
-
#
|
|
39
|
+
# 规范化并展开输入路径(处理 ~ 和相对路径)
|
|
40
40
|
path = os.path.expanduser(args["path"].strip())
|
|
41
41
|
path = os.path.abspath(path)
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# 验证目标路径是否存在
|
|
44
44
|
if not os.path.exists(path):
|
|
45
45
|
return {
|
|
46
46
|
"success": False,
|
|
47
47
|
"stdout": "",
|
|
48
|
-
"stderr": f"
|
|
48
|
+
"stderr": f"目录不存在: {path}"
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
#
|
|
51
|
+
# 确保路径指向的是目录,而不是文件
|
|
52
52
|
if not os.path.isdir(path):
|
|
53
53
|
return {
|
|
54
54
|
"success": False,
|
|
55
55
|
"stdout": "",
|
|
56
|
-
"stderr": f"
|
|
56
|
+
"stderr": f"路径不是目录: {path}"
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
#
|
|
59
|
+
# 获取当前目录并尝试切换到新路径
|
|
60
60
|
old_path = os.getcwd()
|
|
61
61
|
os.chdir(path)
|
|
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
|
|
|
69
|
-
#
|
|
69
|
+
# 处理用户没有目录访问权限的情况
|
|
70
70
|
except PermissionError:
|
|
71
71
|
return {
|
|
72
72
|
"success": False,
|
|
73
73
|
"stdout": "",
|
|
74
|
-
"stderr": f"
|
|
74
|
+
"stderr": f"无权限访问目录: {path}"
|
|
75
75
|
}
|
|
76
|
-
#
|
|
76
|
+
# 捕获在目录切换过程中可能出现的其他意外错误
|
|
77
77
|
except Exception as e:
|
|
78
78
|
return {
|
|
79
79
|
"success": False,
|
|
80
80
|
"stdout": "",
|
|
81
|
-
"stderr": f"
|
|
81
|
+
"stderr": f"切换目录失败: {str(e)}"
|
|
82
82
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
import subprocess
|
|
3
|
+
|
|
4
|
+
from yaspin import yaspin
|
|
3
5
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
4
6
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
5
7
|
from jarvis.jarvis_agent import Agent
|
|
@@ -10,27 +12,27 @@ from jarvis.jarvis_utils.utils import init_env
|
|
|
10
12
|
|
|
11
13
|
class CodeReviewTool:
|
|
12
14
|
name = "code_review"
|
|
13
|
-
description = "
|
|
15
|
+
description = "自动代码审查工具,用于分析代码变更"
|
|
14
16
|
parameters = {
|
|
15
17
|
"type": "object",
|
|
16
18
|
"properties": {
|
|
17
19
|
"review_type": {
|
|
18
20
|
"type": "string",
|
|
19
|
-
"description": "
|
|
21
|
+
"description": "审查类型:'commit' 审查特定提交,'current' 审查当前变更,'range' 审查提交范围",
|
|
20
22
|
"enum": ["commit", "current", "range"],
|
|
21
23
|
"default": "current"
|
|
22
24
|
},
|
|
23
25
|
"commit_sha": {
|
|
24
26
|
"type": "string",
|
|
25
|
-
"description": "
|
|
27
|
+
"description": "要分析的提交SHA(review_type='commit'时必填)"
|
|
26
28
|
},
|
|
27
29
|
"start_commit": {
|
|
28
30
|
"type": "string",
|
|
29
|
-
"description": "
|
|
31
|
+
"description": "起始提交SHA(review_type='range'时必填)"
|
|
30
32
|
},
|
|
31
33
|
"end_commit": {
|
|
32
34
|
"type": "string",
|
|
33
|
-
"description": "
|
|
35
|
+
"description": "结束提交SHA(review_type='range'时必填)"
|
|
34
36
|
}
|
|
35
37
|
},
|
|
36
38
|
"required": []
|
|
@@ -41,44 +43,47 @@ class CodeReviewTool:
|
|
|
41
43
|
review_type = args.get("review_type", "current").strip()
|
|
42
44
|
|
|
43
45
|
# Build git diff command based on review type
|
|
44
|
-
|
|
45
|
-
if "
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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 -"
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
70
80
|
return {
|
|
71
81
|
"success": False,
|
|
72
82
|
"stdout": {},
|
|
73
|
-
"stderr": "
|
|
83
|
+
"stderr": f"Failed to get diff: {str(e)}"
|
|
74
84
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
"success": False,
|
|
79
|
-
"stdout": {},
|
|
80
|
-
"stderr": f"Failed to get diff: {str(e)}"
|
|
81
|
-
}
|
|
85
|
+
spinner.text = "代码变更获取完成"
|
|
86
|
+
spinner.ok("✅")
|
|
82
87
|
|
|
83
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:
|
|
84
89
|
|
|
@@ -243,4 +248,4 @@ def main():
|
|
|
243
248
|
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
244
249
|
|
|
245
250
|
if __name__ == "__main__":
|
|
246
|
-
main()
|
|
251
|
+
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,86 @@
|
|
|
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
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# Execute command
|
|
76
|
+
# Execute command and capture return code
|
|
48
77
|
return_code = os.system(tee_command)
|
|
49
78
|
|
|
50
|
-
# Read output file
|
|
79
|
+
# Read and process output file
|
|
51
80
|
try:
|
|
52
81
|
with open(output_file, 'r', encoding='utf-8', errors='replace') as f:
|
|
53
82
|
output = f.read()
|
|
54
|
-
# Remove header and footer added by script
|
|
83
|
+
# Remove header and footer added by script command
|
|
55
84
|
if output:
|
|
56
85
|
lines = output.splitlines()
|
|
57
86
|
if len(lines) > 2:
|
|
@@ -62,6 +91,7 @@ class ShellTool:
|
|
|
62
91
|
# Clean up temporary file
|
|
63
92
|
Path(output_file).unlink(missing_ok=True)
|
|
64
93
|
|
|
94
|
+
# Return successful result
|
|
65
95
|
return {
|
|
66
96
|
"success": True,
|
|
67
97
|
"stdout": output,
|
|
@@ -69,7 +99,7 @@ class ShellTool:
|
|
|
69
99
|
}
|
|
70
100
|
|
|
71
101
|
except Exception as e:
|
|
72
|
-
# Ensure temporary file is cleaned up
|
|
102
|
+
# Ensure temporary file is cleaned up even if error occurs
|
|
73
103
|
if 'output_file' in locals():
|
|
74
104
|
Path(output_file).unlink(missing_ok=True)
|
|
75
105
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
@@ -77,4 +107,4 @@ class ShellTool:
|
|
|
77
107
|
"success": False,
|
|
78
108
|
"stdout": "",
|
|
79
109
|
"stderr": str(e)
|
|
80
|
-
}
|
|
110
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
import os
|
|
3
3
|
|
|
4
|
+
from yaspin import yaspin
|
|
5
|
+
|
|
4
6
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class FileOperationTool:
|
|
9
11
|
name = "file_operation"
|
|
10
|
-
description = "
|
|
12
|
+
description = "用于读写多个文件的操作工具"
|
|
11
13
|
parameters = {
|
|
12
14
|
"type": "object",
|
|
13
15
|
"properties": {
|
|
14
16
|
"operation": {
|
|
15
17
|
"type": "string",
|
|
16
18
|
"enum": ["read", "write"],
|
|
17
|
-
"description": "
|
|
19
|
+
"description": "要执行的文件操作类型(读取或写入多个文件)"
|
|
18
20
|
},
|
|
19
21
|
"files": {
|
|
20
22
|
"type": "array",
|
|
@@ -26,56 +28,77 @@ class FileOperationTool:
|
|
|
26
28
|
},
|
|
27
29
|
"required": ["path"]
|
|
28
30
|
},
|
|
29
|
-
"description": "
|
|
31
|
+
"description": "要操作的文件列表"
|
|
30
32
|
}
|
|
31
33
|
},
|
|
32
34
|
"required": ["operation", "files"]
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
def _handle_single_file(self, operation: str, filepath: str, content: str = ""
|
|
37
|
+
def _handle_single_file(self, operation: str, filepath: str, content: str = "",
|
|
38
|
+
start_line: int = 1, end_line: int = -1) -> Dict[str, Any]:
|
|
36
39
|
"""Handle operations for a single file"""
|
|
37
40
|
try:
|
|
38
41
|
abs_path = os.path.abspath(filepath)
|
|
39
|
-
PrettyOutput.print(f"文件操作: {operation} - {abs_path}", OutputType.INFO)
|
|
40
|
-
|
|
41
42
|
if operation == "read":
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
43
|
+
with yaspin(text=f"正在读取文件: {abs_path}...", color="cyan") as spinner:
|
|
44
|
+
if not os.path.exists(abs_path):
|
|
45
|
+
return {
|
|
46
|
+
"success": False,
|
|
47
|
+
"stdout": "",
|
|
48
|
+
"stderr": f"文件不存在: {abs_path}"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if os.path.getsize(abs_path) > 10 * 1024 * 1024: # 10MB
|
|
52
|
+
return {
|
|
53
|
+
"success": False,
|
|
54
|
+
"stdout": "",
|
|
55
|
+
"stderr": "File too large (>10MB)"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
with open(abs_path, 'r', encoding='utf-8') as f:
|
|
59
|
+
lines = f.readlines()
|
|
60
|
+
|
|
49
61
|
|
|
50
|
-
|
|
51
|
-
|
|
62
|
+
total_lines = len(lines)
|
|
63
|
+
start_line = start_line if start_line >= 0 else total_lines + start_line + 1
|
|
64
|
+
end_line = end_line if end_line >= 0 else total_lines + end_line + 1
|
|
65
|
+
start_line = max(1, min(start_line, total_lines))
|
|
66
|
+
end_line = max(1, min(end_line, total_lines))
|
|
67
|
+
if end_line == -1:
|
|
68
|
+
end_line = total_lines
|
|
69
|
+
|
|
70
|
+
if start_line > end_line:
|
|
71
|
+
spinner.text = "无效的行范围"
|
|
72
|
+
spinner.fail("❌")
|
|
73
|
+
error_msg = f"无效的行范围 [{start_line, end_line}] (文件总行数: {total_lines})"
|
|
74
|
+
return {
|
|
75
|
+
"success": False,
|
|
76
|
+
"stdout": "",
|
|
77
|
+
"stderr": error_msg
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
content = "".join(lines[start_line - 1:end_line])
|
|
81
|
+
output = f"\n文件: {abs_path}\n行: [{start_line}-{end_line}]\n{content}" + "\n\n" + "="*80 + "\n\n"
|
|
82
|
+
|
|
83
|
+
spinner.text = f"文件读取完成: {abs_path}"
|
|
84
|
+
spinner.ok("✅")
|
|
52
85
|
return {
|
|
53
|
-
"success":
|
|
54
|
-
"stdout":
|
|
55
|
-
"stderr": "
|
|
86
|
+
"success": True,
|
|
87
|
+
"stdout": output,
|
|
88
|
+
"stderr": ""
|
|
56
89
|
}
|
|
57
|
-
|
|
58
|
-
content = open(abs_path, 'r', encoding='utf-8').read()
|
|
59
|
-
output = f"File: {abs_path}\n{content}"
|
|
60
|
-
|
|
61
|
-
return {
|
|
62
|
-
"success": True,
|
|
63
|
-
"stdout": output,
|
|
64
|
-
"stderr": ""
|
|
65
|
-
}
|
|
66
|
-
|
|
67
90
|
elif operation == "write":
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
with yaspin(text=f"正在写入文件: {abs_path}...", color="cyan") as spinner:
|
|
92
|
+
os.makedirs(os.path.dirname(os.path.abspath(abs_path)), exist_ok=True)
|
|
93
|
+
with open(abs_path, 'w', encoding='utf-8') as f:
|
|
94
|
+
f.write(content)
|
|
95
|
+
spinner.text = f"文件写入完成: {abs_path}"
|
|
96
|
+
spinner.ok("✅")
|
|
97
|
+
return {
|
|
98
|
+
"success": True,
|
|
99
|
+
"stdout": f"Successfully wrote content to {abs_path}",
|
|
100
|
+
"stderr": ""
|
|
101
|
+
}
|
|
79
102
|
return {
|
|
80
103
|
"success": False,
|
|
81
104
|
"stdout": "",
|
|
@@ -120,7 +143,13 @@ class FileOperationTool:
|
|
|
120
143
|
continue
|
|
121
144
|
|
|
122
145
|
content = file_info.get("content", "") if operation == "write" else ""
|
|
123
|
-
result = self._handle_single_file(
|
|
146
|
+
result = self._handle_single_file(
|
|
147
|
+
operation,
|
|
148
|
+
file_info["path"].strip(),
|
|
149
|
+
content,
|
|
150
|
+
file_info.get("start_line", 1),
|
|
151
|
+
file_info.get("end_line", -1)
|
|
152
|
+
)
|
|
124
153
|
|
|
125
154
|
if result["success"]:
|
|
126
155
|
all_outputs.append(result["stdout"])
|
|
@@ -143,4 +172,4 @@ class FileOperationTool:
|
|
|
143
172
|
"success": False,
|
|
144
173
|
"stdout": "",
|
|
145
174
|
"stderr": f"File operation failed: {str(e)}"
|
|
146
|
-
}
|
|
175
|
+
}
|