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.

Files changed (49) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +205 -187
  3. jarvis/jarvis_code_agent/code_agent.py +116 -109
  4. jarvis/jarvis_code_agent/patch.py +157 -138
  5. jarvis/jarvis_code_agent/shell_input_handler.py +22 -0
  6. jarvis/jarvis_codebase/main.py +314 -288
  7. jarvis/jarvis_dev/main.py +695 -716
  8. jarvis/jarvis_lsp/base.py +0 -12
  9. jarvis/jarvis_lsp/cpp.py +0 -9
  10. jarvis/jarvis_lsp/go.py +0 -9
  11. jarvis/jarvis_lsp/python.py +0 -28
  12. jarvis/jarvis_lsp/registry.py +0 -1
  13. jarvis/jarvis_lsp/rust.py +0 -9
  14. jarvis/jarvis_multi_agent/__init__.py +52 -52
  15. jarvis/jarvis_platform/base.py +6 -5
  16. jarvis/jarvis_platform_manager/main.py +1 -1
  17. jarvis/jarvis_rag/main.py +250 -186
  18. jarvis/jarvis_smart_shell/main.py +0 -1
  19. jarvis/jarvis_tools/ask_codebase.py +10 -9
  20. jarvis/jarvis_tools/ask_user.py +2 -2
  21. jarvis/jarvis_tools/base.py +4 -4
  22. jarvis/jarvis_tools/chdir.py +28 -28
  23. jarvis/jarvis_tools/code_review.py +44 -39
  24. jarvis/jarvis_tools/create_code_agent.py +4 -4
  25. jarvis/jarvis_tools/create_sub_agent.py +7 -7
  26. jarvis/jarvis_tools/execute_shell.py +53 -23
  27. jarvis/jarvis_tools/execute_shell_script.py +3 -3
  28. jarvis/jarvis_tools/file_operation.py +70 -41
  29. jarvis/jarvis_tools/git_commiter.py +61 -51
  30. jarvis/jarvis_tools/lsp_find_definition.py +7 -7
  31. jarvis/jarvis_tools/lsp_prepare_rename.py +7 -7
  32. jarvis/jarvis_tools/methodology.py +6 -6
  33. jarvis/jarvis_tools/rag.py +5 -5
  34. jarvis/jarvis_tools/read_webpage.py +52 -32
  35. jarvis/jarvis_tools/registry.py +167 -180
  36. jarvis/jarvis_tools/search_web.py +66 -41
  37. jarvis/jarvis_tools/select_code_files.py +3 -3
  38. jarvis/jarvis_tools/tool_generator.py +68 -55
  39. jarvis/jarvis_utils/methodology.py +77 -59
  40. jarvis/jarvis_utils/output.py +1 -0
  41. {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/METADATA +31 -17
  42. jarvis_ai_assistant-0.1.128.dist-info/RECORD +74 -0
  43. {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/WHEEL +1 -1
  44. jarvis/jarvis_tools/lsp_validate_edit.py +0 -141
  45. jarvis/jarvis_tools/read_code.py +0 -192
  46. jarvis_ai_assistant-0.1.125.dist-info/RECORD +0 -75
  47. {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/LICENSE +0 -0
  48. {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/entry_points.txt +0 -0
  49. {jarvis_ai_assistant-0.1.125.dist-info → jarvis_ai_assistant-0.1.128.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ class Tool:
7
7
  """
8
8
  初始化工具对象
9
9
 
10
- Args:
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
- Args:
31
+ 参数:
32
32
  arguments (Dict): 工具执行所需的参数
33
33
 
34
- Returns:
34
+ 返回:
35
35
  Dict[str, Any]: 工具执行结果
36
36
  """
37
- return self.func(arguments)
37
+ return self.func(arguments)
@@ -3,80 +3,80 @@ import os
3
3
 
4
4
  class ChdirTool:
5
5
  name = "chdir"
6
- description = "Change current working directory"
6
+ description = "更改当前工作目录"
7
7
  parameters = {
8
8
  "type": "object",
9
9
  "properties": {
10
10
  "path": {
11
11
  "type": "string",
12
- "description": "Directory path to switch to, supports both relative and absolute paths"
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
- """Execute directory change operation with comprehensive error handling.
19
+ """执行目录切换操作,并提供全面的错误处理。
20
20
 
21
- Args:
22
- args: Dictionary containing 'path' key with target directory path
21
+ 参数:
22
+ args: 包含 'path' 键的字典,目标目录路径
23
23
 
24
- Returns:
25
- Dictionary containing:
26
- - success: Boolean indicating operation status
27
- - stdout: Success message or empty string
28
- - stderr: Error message or empty string
24
+ 返回:
25
+ 字典,包含以下内容:
26
+ - success: 布尔值,表示操作状态
27
+ - stdout: 成功消息或空字符串
28
+ - stderr: 错误消息或空字符串
29
29
 
30
- Raises:
31
- Handles and returns appropriate error messages for:
32
- - Non-existent paths
33
- - Non-directory paths
34
- - Permission errors
35
- - Generic exceptions
30
+ 异常处理:
31
+ 处理并返回适当的错误消息:
32
+ - 不存在的路径
33
+ - 非目录路径
34
+ - 权限错误
35
+ - 其他通用异常
36
36
  """
37
- # Main execution block with comprehensive error handling
37
+ # 主执行块,包含全面的错误处理
38
38
  try:
39
- # Normalize and expand the input path (handles ~ and relative paths)
39
+ # 规范化并展开输入路径(处理 ~ 和相对路径)
40
40
  path = os.path.expanduser(args["path"].strip())
41
41
  path = os.path.abspath(path)
42
42
 
43
- # Validate that the target path exists
43
+ # 验证目标路径是否存在
44
44
  if not os.path.exists(path):
45
45
  return {
46
46
  "success": False,
47
47
  "stdout": "",
48
- "stderr": f"Directory does not exist: {path}"
48
+ "stderr": f"目录不存在: {path}"
49
49
  }
50
50
 
51
- # Ensure the path points to a directory, not a file
51
+ # 确保路径指向的是目录,而不是文件
52
52
  if not os.path.isdir(path):
53
53
  return {
54
54
  "success": False,
55
55
  "stdout": "",
56
- "stderr": f"The path is not a directory: {path}"
56
+ "stderr": f"路径不是目录: {path}"
57
57
  }
58
58
 
59
- # Capture current directory and attempt to change to new path
59
+ # 获取当前目录并尝试切换到新路径
60
60
  old_path = os.getcwd()
61
61
  os.chdir(path)
62
62
 
63
63
  return {
64
64
  "success": True,
65
- "stdout": f"Changed working directory:\nFrom: {old_path}\nTo: {path}",
65
+ "stdout": f"成功切换工作目录:\n原目录: {old_path}\n新目录: {path}",
66
66
  "stderr": ""
67
67
  }
68
68
 
69
- # Handle cases where user lacks directory access permissions
69
+ # 处理用户没有目录访问权限的情况
70
70
  except PermissionError:
71
71
  return {
72
72
  "success": False,
73
73
  "stdout": "",
74
- "stderr": f"No permission to access directory: {path}"
74
+ "stderr": f"无权限访问目录: {path}"
75
75
  }
76
- # Catch-all for any other unexpected errors during directory change
76
+ # 捕获在目录切换过程中可能出现的其他意外错误
77
77
  except Exception as e:
78
78
  return {
79
79
  "success": False,
80
80
  "stdout": "",
81
- "stderr": f"Failed to switch directory: {str(e)}"
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 = "Autonomous code review agent for code changes analysis"
15
+ description = "自动代码审查工具,用于分析代码变更"
14
16
  parameters = {
15
17
  "type": "object",
16
18
  "properties": {
17
19
  "review_type": {
18
20
  "type": "string",
19
- "description": "Type of review: 'commit' for specific commit, 'current' for current changes, 'range' for commit range",
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": "Target commit SHA to analyze (required for review_type='commit')"
27
+ "description": "要分析的提交SHAreview_type='commit'时必填)"
26
28
  },
27
29
  "start_commit": {
28
30
  "type": "string",
29
- "description": "Start commit SHA (required for review_type='range')"
31
+ "description": "起始提交SHAreview_type='range'时必填)"
30
32
  },
31
33
  "end_commit": {
32
34
  "type": "string",
33
- "description": "End commit SHA (required for review_type='range')"
35
+ "description": "结束提交SHAreview_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
- if review_type == "commit":
45
- if "commit_sha" not in args:
46
- return {
47
- "success": False,
48
- "stdout": {},
49
- "stderr": "commit_sha is required for commit review type"
50
- }
51
- commit_sha = args["commit_sha"].strip()
52
- diff_cmd = f"git show {commit_sha} | cat -"
53
- elif review_type == "range":
54
- if "start_commit" not in args or "end_commit" not in args:
55
- return {
56
- "success": False,
57
- "stdout": {},
58
- "stderr": "start_commit and end_commit are required for range review type"
59
- }
60
- start_commit = args["start_commit"].strip()
61
- end_commit = args["end_commit"].strip()
62
- diff_cmd = f"git diff {start_commit}..{end_commit} | cat -"
63
- else: # current changes
64
- diff_cmd = "git diff HEAD | cat -"
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
- # Execute git diff command
67
- try:
68
- diff_output = subprocess.check_output(diff_cmd, shell=True, text=True)
69
- if not diff_output:
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": "No changes to review"
83
+ "stderr": f"Failed to get diff: {str(e)}"
74
84
  }
75
- PrettyOutput.print(diff_output, OutputType.CODE, lang="diff")
76
- except subprocess.CalledProcessError as e:
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
- """Tool for managing the code development workflow."""
9
+ """用于管理代码开发工作流的工具"""
10
10
 
11
11
  name = "create_code_agent"
12
- description = "Technical code implementation and development process management"
12
+ description = "技术代码实现和开发过程管理工具"
13
13
  parameters = {
14
- "requirement": "Technical specifications for code implementation"
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 = "Create a sub-agent to handle specific tasks, the sub-agent will generate a task summary report"
11
+ description = "创建子代理以处理特定任务,子代理将生成任务总结报告"
12
12
  parameters = {
13
13
  "type": "object",
14
14
  "properties": {
15
15
  "agent_name": {
16
16
  "type": "string",
17
- "description": "Sub-agent name"
17
+ "description": "子代理名称"
18
18
  },
19
19
  "task": {
20
20
  "type": "string",
21
- "description": "Specific task to complete"
21
+ "description": "要完成的特定任务"
22
22
  },
23
23
  "context": {
24
24
  "type": "string",
25
- "description": "Context information related to the task",
25
+ "description": "与任务相关的上下文信息",
26
26
  "default": ""
27
27
  },
28
28
  "goal": {
29
29
  "type": "string",
30
- "description": "Completion goal of the task",
30
+ "description": "任务的完成目标",
31
31
  "default": ""
32
32
  },
33
33
  "files": {
34
34
  "type": "array",
35
35
  "items": {"type": "string"},
36
- "description": "Related file path list, used for file question answering and processing",
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 = """Execute shell command and return result"""
14
-
22
+ description = "执行Shell命令并返回结果"
15
23
  parameters = {
16
24
  "type": "object",
17
25
  "properties": {
18
26
  "command": {
19
27
  "type": "string",
20
- "description": "Shell command to execute"
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
- # Modify command to use script
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
- PrettyOutput.print(f"执行命令: {command}", OutputType.INFO)
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 = """Execute shell script file and return result"""
11
+ description = "执行Shell脚本文件并返回结果"
12
12
  parameters = {
13
13
  "type": "object",
14
14
  "properties": {
15
15
  "script_content": {
16
16
  "type": "string",
17
- "description": "Content of the shell script to execute"
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 = "File operations for reading and writing multiple files"
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": "Type of file operation to perform (read or write multiple files)"
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": "List of files to operate on"
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 = "") -> Dict[str, Any]:
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
- if not os.path.exists(abs_path):
43
- PrettyOutput.print(f"文件不存在: {abs_path}", OutputType.WARNING)
44
- return {
45
- "success": False,
46
- "stdout": "",
47
- "stderr": f"文件不存在: {abs_path}"
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
- if os.path.getsize(abs_path) > 10 * 1024 * 1024: # 10MB
51
- PrettyOutput.print(f"文件太大: {abs_path}", OutputType.WARNING)
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": False,
54
- "stdout": "",
55
- "stderr": "File too large (>10MB)"
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
- os.makedirs(os.path.dirname(os.path.abspath(abs_path)), exist_ok=True)
69
- with open(abs_path, 'w', encoding='utf-8') as f:
70
- f.write(content)
71
-
72
- PrettyOutput.print(f"写入文件: {abs_path}", OutputType.INFO)
73
- return {
74
- "success": True,
75
- "stdout": f"Successfully wrote content to {abs_path}",
76
- "stderr": ""
77
- }
78
- PrettyOutput.print(f"未知操作: {operation}", OutputType.WARNING)
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(operation, file_info["path"].strip(), content)
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
+ }