jarvis-ai-assistant 0.1.98__py3-none-any.whl → 0.1.100__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 +199 -157
- jarvis/jarvis_code_agent/__init__.py +0 -0
- jarvis/jarvis_code_agent/main.py +202 -0
- jarvis/jarvis_codebase/main.py +415 -287
- jarvis/jarvis_coder/file_select.py +209 -0
- jarvis/jarvis_coder/git_utils.py +64 -2
- jarvis/jarvis_coder/main.py +13 -397
- jarvis/jarvis_coder/patch_handler.py +229 -81
- jarvis/jarvis_coder/plan_generator.py +49 -7
- jarvis/jarvis_platform/main.py +2 -2
- jarvis/jarvis_rag/main.py +11 -11
- jarvis/jarvis_smart_shell/main.py +5 -5
- jarvis/models/base.py +6 -1
- jarvis/models/kimi.py +2 -2
- jarvis/models/ollama.py +2 -2
- jarvis/models/openai.py +1 -1
- jarvis/models/registry.py +38 -18
- jarvis/tools/ask_user.py +12 -9
- jarvis/tools/chdir.py +9 -5
- jarvis/tools/create_code_sub_agent.py +56 -0
- jarvis/tools/{sub_agent.py → create_sub_agent.py} +6 -2
- jarvis/tools/execute_code_modification.py +70 -0
- jarvis/tools/{shell.py → execute_shell.py} +2 -2
- jarvis/tools/{file_ops.py → file_operation.py} +19 -15
- jarvis/tools/find_files.py +119 -0
- jarvis/tools/{generator.py → generate_tool.py} +27 -25
- jarvis/tools/methodology.py +32 -26
- jarvis/tools/rag.py +37 -33
- jarvis/tools/{webpage.py → read_webpage.py} +4 -2
- jarvis/tools/registry.py +94 -48
- jarvis/tools/search.py +19 -16
- jarvis/tools/select_code_files.py +61 -0
- jarvis/tools/thinker.py +7 -5
- jarvis/utils.py +155 -32
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.100.dist-info}/METADATA +9 -8
- jarvis_ai_assistant-0.1.100.dist-info/RECORD +51 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.100.dist-info}/entry_points.txt +2 -1
- jarvis/main.py +0 -155
- jarvis/tools/codebase_qa.py +0 -74
- jarvis/tools/coder.py +0 -69
- jarvis_ai_assistant-0.1.98.dist-info/RECORD +0 -47
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.100.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.100.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.98.dist-info → jarvis_ai_assistant-0.1.100.dist-info}/top_level.txt +0 -0
jarvis/models/kimi.py
CHANGED
|
@@ -35,8 +35,8 @@ class KimiModel(BasePlatform):
|
|
|
35
35
|
PrettyOutput.print(" • Find the Authorization header in the request", OutputType.INFO)
|
|
36
36
|
PrettyOutput.print(" • Copy the token value (remove the 'Bearer ' prefix)", OutputType.INFO)
|
|
37
37
|
PrettyOutput.print("\n2. Set environment variable:", OutputType.INFO)
|
|
38
|
-
PrettyOutput.print(" • Method 1: Create or edit ~/.
|
|
39
|
-
PrettyOutput.print(" echo 'KIMI_API_KEY=your_key_here' > ~/.
|
|
38
|
+
PrettyOutput.print(" • Method 1: Create or edit ~/.jarvis/env file:", OutputType.INFO)
|
|
39
|
+
PrettyOutput.print(" echo 'KIMI_API_KEY=your_key_here' > ~/.jarvis/env", OutputType.INFO)
|
|
40
40
|
PrettyOutput.print("\n • Method 2: Set environment variable directly:", OutputType.INFO)
|
|
41
41
|
PrettyOutput.print(" export KIMI_API_KEY=your_key_here", OutputType.INFO)
|
|
42
42
|
PrettyOutput.print("\nAfter setting, run Jarvis again.", OutputType.INFO)
|
jarvis/models/ollama.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
from typing import List, Dict, Tuple
|
|
3
3
|
from jarvis.models.base import BasePlatform
|
|
4
|
-
from jarvis.utils import OutputType, PrettyOutput
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput, get_single_line_input
|
|
5
5
|
import os
|
|
6
6
|
import json
|
|
7
7
|
|
|
@@ -136,7 +136,7 @@ if __name__ == "__main__":
|
|
|
136
136
|
ollama = OllamaPlatform()
|
|
137
137
|
while True:
|
|
138
138
|
try:
|
|
139
|
-
message =
|
|
139
|
+
message = get_single_line_input("\nInput question (Ctrl+C to exit)")
|
|
140
140
|
ollama.chat_until_success(message)
|
|
141
141
|
except KeyboardInterrupt:
|
|
142
142
|
print("\nGoodbye!")
|
jarvis/models/openai.py
CHANGED
|
@@ -23,7 +23,7 @@ class OpenAIModel(BasePlatform):
|
|
|
23
23
|
PrettyOutput.print(" • OPENAI_API_KEY: API key", OutputType.INFO)
|
|
24
24
|
PrettyOutput.print(" • OPENAI_API_BASE: (optional) API base address, default using https://api.openai.com/v1", OutputType.INFO)
|
|
25
25
|
PrettyOutput.print("\nYou can set them in the following ways:", OutputType.INFO)
|
|
26
|
-
PrettyOutput.print("1. Create or edit ~/.
|
|
26
|
+
PrettyOutput.print("1. Create or edit ~/.jarvis/env file:", OutputType.INFO)
|
|
27
27
|
PrettyOutput.print(" OPENAI_API_KEY=your_api_key", OutputType.INFO)
|
|
28
28
|
PrettyOutput.print(" OPENAI_API_BASE=your_api_base", OutputType.INFO)
|
|
29
29
|
PrettyOutput.print(" OPENAI_MODEL_NAME=your_model_name", OutputType.INFO)
|
jarvis/models/registry.py
CHANGED
|
@@ -3,8 +3,8 @@ import inspect
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
from typing import Dict, Type, Optional, List
|
|
6
|
-
from .base import BasePlatform
|
|
7
|
-
from
|
|
6
|
+
from jarvis.models.base import BasePlatform
|
|
7
|
+
from jarvis.utils import PrettyOutput, OutputType
|
|
8
8
|
|
|
9
9
|
REQUIRED_METHODS = [
|
|
10
10
|
('chat', ['message']), # 方法名和参数列表
|
|
@@ -23,11 +23,10 @@ class PlatformRegistry:
|
|
|
23
23
|
|
|
24
24
|
global_platform_name = "kimi"
|
|
25
25
|
global_platform_registry = None
|
|
26
|
-
suppress_output = False
|
|
27
26
|
|
|
28
27
|
@staticmethod
|
|
29
28
|
def get_platform_dir() -> str:
|
|
30
|
-
user_platform_dir = os.path.expanduser("~/.
|
|
29
|
+
user_platform_dir = os.path.expanduser("~/.jarvis/models")
|
|
31
30
|
if not os.path.exists(user_platform_dir):
|
|
32
31
|
try:
|
|
33
32
|
os.makedirs(user_platform_dir)
|
|
@@ -117,7 +116,7 @@ class PlatformRegistry:
|
|
|
117
116
|
module = importlib.import_module(module_name)
|
|
118
117
|
|
|
119
118
|
# 遍历模块中的所有类
|
|
120
|
-
for
|
|
119
|
+
for _, obj in inspect.getmembers(module):
|
|
121
120
|
# 检查是否是BasePlatform的子类,但不是BasePlatform本身
|
|
122
121
|
if (inspect.isclass(obj) and
|
|
123
122
|
issubclass(obj, BasePlatform) and
|
|
@@ -126,8 +125,6 @@ class PlatformRegistry:
|
|
|
126
125
|
# 检查平台实现
|
|
127
126
|
if not PlatformRegistry.check_platform_implementation(obj):
|
|
128
127
|
continue
|
|
129
|
-
if not PlatformRegistry.suppress_output:
|
|
130
|
-
PrettyOutput.print(f"Load platform from {os.path.join(directory, filename)}: {obj.platform_name}", OutputType.SUCCESS) # type: ignore
|
|
131
128
|
platforms[obj.platform_name] = obj # type: ignore
|
|
132
129
|
break
|
|
133
130
|
except Exception as e:
|
|
@@ -140,22 +137,22 @@ class PlatformRegistry:
|
|
|
140
137
|
def get_global_platform_registry():
|
|
141
138
|
"""Get global platform registry"""
|
|
142
139
|
if PlatformRegistry.global_platform_registry is None:
|
|
143
|
-
PlatformRegistry.global_platform_registry = PlatformRegistry()
|
|
144
|
-
|
|
145
|
-
# 从用户平台目录加载额外平台
|
|
146
|
-
platform_dir = PlatformRegistry.get_platform_dir()
|
|
147
|
-
if platform_dir and os.path.exists(platform_dir):
|
|
148
|
-
for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
|
149
|
-
PlatformRegistry.global_platform_registry.register_platform(platform_name, platform_class)
|
|
150
|
-
platform_dir = os.path.dirname(__file__)
|
|
151
|
-
if platform_dir and os.path.exists(platform_dir):
|
|
152
|
-
for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
|
153
|
-
PlatformRegistry.global_platform_registry.register_platform(platform_name, platform_class)
|
|
140
|
+
PlatformRegistry.global_platform_registry = PlatformRegistry()
|
|
154
141
|
return PlatformRegistry.global_platform_registry
|
|
155
142
|
|
|
156
143
|
def __init__(self):
|
|
157
144
|
"""Initialize platform registry"""
|
|
158
145
|
self.platforms: Dict[str, Type[BasePlatform]] = {}
|
|
146
|
+
# 从用户平台目录加载额外平台
|
|
147
|
+
platform_dir = PlatformRegistry.get_platform_dir()
|
|
148
|
+
if platform_dir and os.path.exists(platform_dir):
|
|
149
|
+
for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
|
150
|
+
self.register_platform(platform_name, platform_class)
|
|
151
|
+
platform_dir = os.path.dirname(__file__)
|
|
152
|
+
if platform_dir and os.path.exists(platform_dir):
|
|
153
|
+
for platform_name, platform_class in PlatformRegistry.load_platform_from_dir(platform_dir).items():
|
|
154
|
+
self.register_platform(platform_name, platform_class)
|
|
155
|
+
|
|
159
156
|
|
|
160
157
|
def get_normal_platform(self) -> BasePlatform:
|
|
161
158
|
platform_name = os.environ.get("JARVIS_PLATFORM", "kimi")
|
|
@@ -215,6 +212,29 @@ class PlatformRegistry:
|
|
|
215
212
|
PrettyOutput.print(f"Create platform failed: {str(e)}", OutputType.ERROR)
|
|
216
213
|
return None
|
|
217
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
|
+
|
|
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
|
+
}
|
|
218
238
|
def get_available_platforms(self) -> List[str]:
|
|
219
239
|
"""Get available platform list"""
|
|
220
240
|
return list(self.platforms.keys())
|
jarvis/tools/ask_user.py
CHANGED
|
@@ -18,37 +18,40 @@ class AskUserTool:
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
|
-
"""
|
|
21
|
+
"""Execute the operation of asking the user
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
args:
|
|
24
|
+
args: A dictionary containing the question
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
|
-
Dict:
|
|
27
|
+
Dict: A dictionary containing the user's response
|
|
28
28
|
"""
|
|
29
29
|
try:
|
|
30
30
|
question = args["question"]
|
|
31
31
|
|
|
32
|
-
#
|
|
33
|
-
PrettyOutput.print("\
|
|
32
|
+
# Display the question
|
|
33
|
+
PrettyOutput.print("\nQuestion:", OutputType.SYSTEM)
|
|
34
34
|
PrettyOutput.print(question, OutputType.SYSTEM)
|
|
35
35
|
|
|
36
|
-
#
|
|
36
|
+
# Get user input
|
|
37
37
|
user_response = get_multiline_input("Please enter your answer (input empty line to end)")
|
|
38
38
|
|
|
39
39
|
if user_response == "__interrupt__":
|
|
40
40
|
return {
|
|
41
41
|
"success": False,
|
|
42
|
-
"
|
|
42
|
+
"stdout": "",
|
|
43
|
+
"stderr": "User canceled input"
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
return {
|
|
46
47
|
"success": True,
|
|
47
|
-
"stdout": user_response
|
|
48
|
+
"stdout": user_response,
|
|
49
|
+
"stderr": ""
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
except Exception as e:
|
|
51
53
|
return {
|
|
52
54
|
"success": False,
|
|
53
|
-
"
|
|
55
|
+
"stdout": "",
|
|
56
|
+
"stderr": f"Failed to ask user: {str(e)}"
|
|
54
57
|
}
|
jarvis/tools/chdir.py
CHANGED
|
@@ -38,14 +38,16 @@ class ChdirTool:
|
|
|
38
38
|
if not os.path.exists(path):
|
|
39
39
|
return {
|
|
40
40
|
"success": False,
|
|
41
|
-
"
|
|
41
|
+
"stdout": "",
|
|
42
|
+
"stderr": f"Directory does not exist: {path}"
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
# 检查是否是目录
|
|
45
46
|
if not os.path.isdir(path):
|
|
46
47
|
return {
|
|
47
48
|
"success": False,
|
|
48
|
-
"
|
|
49
|
+
"stdout": "",
|
|
50
|
+
"stderr": f"The path is not a directory: {path}"
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
# 尝试切换目录
|
|
@@ -61,12 +63,14 @@ class ChdirTool:
|
|
|
61
63
|
except PermissionError:
|
|
62
64
|
return {
|
|
63
65
|
"success": False,
|
|
64
|
-
"
|
|
66
|
+
"stdout": "",
|
|
67
|
+
"stderr": f"No permission to access directory: {path}"
|
|
65
68
|
}
|
|
66
69
|
except Exception as e:
|
|
67
70
|
return {
|
|
68
71
|
"success": False,
|
|
69
|
-
"
|
|
72
|
+
"stdout": "",
|
|
73
|
+
"stderr": f"Failed to switch directory: {str(e)}"
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
def main():
|
|
@@ -83,7 +87,7 @@ def main():
|
|
|
83
87
|
if result["success"]:
|
|
84
88
|
PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
|
|
85
89
|
else:
|
|
86
|
-
PrettyOutput.print(result["
|
|
90
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR)
|
|
87
91
|
|
|
88
92
|
if __name__ == "__main__":
|
|
89
93
|
main()
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
5
|
+
from jarvis.jarvis_code_agent.main import system_prompt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeSubAgentTool:
|
|
9
|
+
name = "create_code_sub_agent"
|
|
10
|
+
description = "Create a sub-agent to handle specific code development subtasks"
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"name": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The name of the sub-agent"
|
|
17
|
+
},
|
|
18
|
+
"subtask": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "The specific code development subtask to complete"
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
"required": ["subtask", "name"]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
27
|
+
"""Execute code development subtask"""
|
|
28
|
+
try:
|
|
29
|
+
subtask = args["subtask"]
|
|
30
|
+
name = args["name"]
|
|
31
|
+
|
|
32
|
+
PrettyOutput.print(f"Creating code sub-agent {name} for subtask: {subtask}", OutputType.INFO)
|
|
33
|
+
|
|
34
|
+
# Create sub-agent
|
|
35
|
+
sub_agent = Agent(
|
|
36
|
+
system_prompt=system_prompt,
|
|
37
|
+
name=name,
|
|
38
|
+
is_sub_agent=True
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Execute subtask
|
|
42
|
+
result = sub_agent.run(subtask)
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"success": True,
|
|
46
|
+
"stdout": f"Code Development Subtask Results:\n\n{result}",
|
|
47
|
+
"stderr": ""
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
52
|
+
return {
|
|
53
|
+
"success": False,
|
|
54
|
+
"stdout": "",
|
|
55
|
+
"stderr": f"Failed to execute code development subtask: {str(e)}"
|
|
56
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Dict, Any
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.agent import Agent, origin_agent_system_prompt
|
|
5
5
|
from jarvis.utils import OutputType, PrettyOutput
|
|
6
6
|
|
|
7
7
|
|
|
@@ -58,8 +58,11 @@ class SubAgentTool:
|
|
|
58
58
|
if goal:
|
|
59
59
|
task_description += f"\n\nCompletion goal:\n{goal}"
|
|
60
60
|
|
|
61
|
+
|
|
62
|
+
|
|
61
63
|
# Create sub-agent
|
|
62
64
|
sub_agent = Agent(
|
|
65
|
+
system_prompt=origin_agent_system_prompt,
|
|
63
66
|
name=agent_name,
|
|
64
67
|
is_sub_agent=True
|
|
65
68
|
)
|
|
@@ -78,5 +81,6 @@ class SubAgentTool:
|
|
|
78
81
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
79
82
|
return {
|
|
80
83
|
"success": False,
|
|
81
|
-
"
|
|
84
|
+
"stdout": "",
|
|
85
|
+
"stderr": f"Sub-agent execution failed: {str(e)}"
|
|
82
86
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
5
|
+
from jarvis.jarvis_coder.patch_handler import PatchHandler
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodeModifyTool:
|
|
9
|
+
name = "execute_code_modification"
|
|
10
|
+
description = "Execute code modifications according to the provided plan"
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"task": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "The code modification task description"
|
|
17
|
+
},
|
|
18
|
+
"structured_plan": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"description": "Dictionary mapping file paths to their modification plans. Example: {'path/to/file.py': 'Add function foo() to handle...'}",
|
|
21
|
+
"additionalProperties": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Modification plan for a specific file"
|
|
24
|
+
},
|
|
25
|
+
"examples": [{
|
|
26
|
+
"src/file1.py": "Add error handling to process_data()",
|
|
27
|
+
"src/file2.py": "Update API endpoint URL in get_data()"
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": ["task", "raw_plan", "structured_plan"]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
35
|
+
"""Execute code modifications using PatchHandler"""
|
|
36
|
+
try:
|
|
37
|
+
task = args["task"]
|
|
38
|
+
structured_plan = args["structured_plan"]
|
|
39
|
+
|
|
40
|
+
PrettyOutput.print("Executing code modifications...", OutputType.INFO)
|
|
41
|
+
|
|
42
|
+
# Create patch handler instance
|
|
43
|
+
patch_handler = PatchHandler()
|
|
44
|
+
|
|
45
|
+
# Apply patches and handle the process
|
|
46
|
+
success, additional_info = patch_handler.handle_patch_application(
|
|
47
|
+
feature=task,
|
|
48
|
+
structed_plan=structured_plan
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
if not success:
|
|
52
|
+
return {
|
|
53
|
+
"success": False,
|
|
54
|
+
"stdout": "Changes have been rolled back",
|
|
55
|
+
"stderr": additional_info
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
"success": True,
|
|
60
|
+
"stdout": "Code modifications have been successfully applied and committed",
|
|
61
|
+
"stderr": additional_info
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
except Exception as e:
|
|
65
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
66
|
+
return {
|
|
67
|
+
"success": False,
|
|
68
|
+
"stdout": "",
|
|
69
|
+
"stderr": f"Failed to execute code modifications: {str(e)}"
|
|
70
|
+
}
|
|
@@ -64,7 +64,6 @@ class ShellTool:
|
|
|
64
64
|
"success": return_code == 0,
|
|
65
65
|
"stdout": output,
|
|
66
66
|
"stderr": "",
|
|
67
|
-
"return_code": return_code
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
except Exception as e:
|
|
@@ -74,5 +73,6 @@ class ShellTool:
|
|
|
74
73
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
75
74
|
return {
|
|
76
75
|
"success": False,
|
|
77
|
-
"
|
|
76
|
+
"stdout": "",
|
|
77
|
+
"stderr": str(e)
|
|
78
78
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from typing import Dict, Any
|
|
1
|
+
from typing import Dict, Any
|
|
2
2
|
import os
|
|
3
|
-
from enum import Enum
|
|
4
3
|
|
|
5
4
|
from jarvis.utils import OutputType, PrettyOutput
|
|
6
5
|
|
|
@@ -36,15 +35,15 @@ class FileOperationTool:
|
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
39
|
-
"""
|
|
38
|
+
"""Execute file operations"""
|
|
40
39
|
try:
|
|
41
40
|
operation = args["operation"]
|
|
42
41
|
filepath = args["filepath"]
|
|
43
42
|
encoding = args.get("encoding", "utf-8")
|
|
44
43
|
|
|
45
|
-
#
|
|
44
|
+
# Record the operation and the full path
|
|
46
45
|
abs_path = os.path.abspath(filepath)
|
|
47
|
-
PrettyOutput.print(f"
|
|
46
|
+
PrettyOutput.print(f"File operation: {operation} - {abs_path}", OutputType.INFO)
|
|
48
47
|
|
|
49
48
|
if operation == "exists":
|
|
50
49
|
exists = os.path.exists(filepath)
|
|
@@ -58,18 +57,20 @@ class FileOperationTool:
|
|
|
58
57
|
if not os.path.exists(filepath):
|
|
59
58
|
return {
|
|
60
59
|
"success": False,
|
|
61
|
-
"
|
|
60
|
+
"stdout": "",
|
|
61
|
+
"stderr": f"文件不存在: {filepath}"
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
#
|
|
64
|
+
# Check file size
|
|
65
65
|
if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
|
|
66
66
|
return {
|
|
67
67
|
"success": False,
|
|
68
|
-
"
|
|
68
|
+
"stdout": "",
|
|
69
|
+
"stderr": "File too large (>10MB)"
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
content = open(filepath, 'r', encoding=encoding).read()
|
|
73
|
+
PrettyOutput.print(content, OutputType.INFO)
|
|
73
74
|
return {
|
|
74
75
|
"success": True,
|
|
75
76
|
"stdout": content,
|
|
@@ -80,10 +81,11 @@ class FileOperationTool:
|
|
|
80
81
|
if not args.get("content"):
|
|
81
82
|
return {
|
|
82
83
|
"success": False,
|
|
83
|
-
"
|
|
84
|
+
"stdout": "",
|
|
85
|
+
"stderr": "Write/append operation requires providing the content parameter"
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
#
|
|
88
|
+
# Create directory (if it doesn't exist)
|
|
87
89
|
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
|
|
88
90
|
|
|
89
91
|
mode = 'a' if operation == "append" else 'w'
|
|
@@ -92,19 +94,21 @@ class FileOperationTool:
|
|
|
92
94
|
|
|
93
95
|
return {
|
|
94
96
|
"success": True,
|
|
95
|
-
"stdout": f"
|
|
97
|
+
"stdout": f"Successfully {operation} content to {filepath}",
|
|
96
98
|
"stderr": ""
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
else:
|
|
100
102
|
return {
|
|
101
103
|
"success": False,
|
|
102
|
-
"
|
|
104
|
+
"stdout": "",
|
|
105
|
+
"stderr": f"Unknown operation: {operation}"
|
|
103
106
|
}
|
|
104
107
|
|
|
105
108
|
except Exception as e:
|
|
106
109
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
107
110
|
return {
|
|
108
111
|
"success": False,
|
|
109
|
-
"
|
|
112
|
+
"stdout": "",
|
|
113
|
+
"stderr": f"File operation failed: {str(e)}"
|
|
110
114
|
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
from jarvis.agent import Agent
|
|
4
|
+
from jarvis.tools.registry import ToolRegistry
|
|
5
|
+
from jarvis.utils import OutputType, PrettyOutput
|
|
6
|
+
|
|
7
|
+
find_files_system_prompt = """You are a Find Files Agent specialized in searching and identifying relevant code files in a codebase. Your task is to find files that are most likely related to the given requirements or problems.
|
|
8
|
+
|
|
9
|
+
SEARCH WORKFLOW:
|
|
10
|
+
1. Understand Search Requirements
|
|
11
|
+
- Analyze the search query thoroughly
|
|
12
|
+
- Identify key technical terms and concepts
|
|
13
|
+
- Break down complex requirements into searchable terms
|
|
14
|
+
|
|
15
|
+
2. Execute Search Strategy
|
|
16
|
+
- Use shell commands to search systematically:
|
|
17
|
+
* Search for key terms:
|
|
18
|
+
<TOOL_CALL>
|
|
19
|
+
name: execute_shell
|
|
20
|
+
arguments:
|
|
21
|
+
command: grep -r "pattern" .
|
|
22
|
+
</TOOL_CALL>
|
|
23
|
+
* Find files by name patterns:
|
|
24
|
+
<TOOL_CALL>
|
|
25
|
+
name: execute_shell
|
|
26
|
+
arguments:
|
|
27
|
+
command: find . -name "pattern"
|
|
28
|
+
</TOOL_CALL>
|
|
29
|
+
* Examine file contents:
|
|
30
|
+
<TOOL_CALL>
|
|
31
|
+
name: execute_shell
|
|
32
|
+
arguments:
|
|
33
|
+
command: grep -A 5 -B 5 "pattern" file.py
|
|
34
|
+
</TOOL_CALL>
|
|
35
|
+
|
|
36
|
+
3. Analyze Results
|
|
37
|
+
- Review each potential file
|
|
38
|
+
- Check file relevance
|
|
39
|
+
- Examine file relationships
|
|
40
|
+
- Consider file dependencies
|
|
41
|
+
|
|
42
|
+
4. Generate File List
|
|
43
|
+
- List all relevant files
|
|
44
|
+
- Sort by relevance
|
|
45
|
+
- Include brief explanation for each file
|
|
46
|
+
- Format output as YAML
|
|
47
|
+
|
|
48
|
+
OUTPUT FORMAT:
|
|
49
|
+
files:
|
|
50
|
+
- path: path/to/file1
|
|
51
|
+
relevance: "Brief explanation of why this file is relevant"
|
|
52
|
+
- path: path/to/file2
|
|
53
|
+
relevance: "Brief explanation of why this file is relevant"
|
|
54
|
+
|
|
55
|
+
SEARCH BEST PRACTICES:
|
|
56
|
+
- Use multiple search terms
|
|
57
|
+
- Consider file naming conventions
|
|
58
|
+
- Check both file names and contents
|
|
59
|
+
- Look for related files (imports, dependencies)
|
|
60
|
+
- Use grep with context (-A, -B options)
|
|
61
|
+
- Search in specific directories when appropriate
|
|
62
|
+
- Exclude irrelevant directories (like .git, __pycache__)
|
|
63
|
+
|
|
64
|
+
IMPORTANT:
|
|
65
|
+
1. Focus on finding the most relevant files
|
|
66
|
+
2. Avoid listing irrelevant files
|
|
67
|
+
3. Explain relevance clearly but concisely
|
|
68
|
+
4. Consider both direct and indirect relevance
|
|
69
|
+
5. Use file content to confirm relevance
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
class FindFilesTool:
|
|
73
|
+
name = "find_files"
|
|
74
|
+
description = "Search and identify relevant code files in the codebase based on requirements or problems"
|
|
75
|
+
parameters = {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"properties": {
|
|
78
|
+
"query": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"description": "The search query or requirement description"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": ["query"]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
87
|
+
"""Execute file search task"""
|
|
88
|
+
try:
|
|
89
|
+
query = args["query"]
|
|
90
|
+
|
|
91
|
+
PrettyOutput.print(f"Creating Find Files agent to search for: {query}", OutputType.INFO)
|
|
92
|
+
|
|
93
|
+
tool_registry = ToolRegistry()
|
|
94
|
+
tool_registry.use_tools(["ask_user", "execute_shell", "file_operation"])
|
|
95
|
+
|
|
96
|
+
# Create find files agent
|
|
97
|
+
find_agent = Agent(
|
|
98
|
+
system_prompt=find_files_system_prompt,
|
|
99
|
+
name="Find Files Agent",
|
|
100
|
+
is_sub_agent=True,
|
|
101
|
+
tool_registry=tool_registry
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Execute search
|
|
105
|
+
result = find_agent.run(query)
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
"success": True,
|
|
109
|
+
"stdout": result,
|
|
110
|
+
"stderr": ""
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
115
|
+
return {
|
|
116
|
+
"success": False,
|
|
117
|
+
"stdout": "",
|
|
118
|
+
"stderr": f"Failed to execute file search: {str(e)}"
|
|
119
|
+
}
|