jarvis-ai-assistant 0.1.97__py3-none-any.whl → 0.1.99__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 +203 -0
- jarvis/jarvis_codebase/main.py +412 -284
- jarvis/jarvis_coder/file_select.py +209 -0
- jarvis/jarvis_coder/git_utils.py +81 -19
- jarvis/jarvis_coder/main.py +68 -446
- jarvis/jarvis_coder/patch_handler.py +117 -47
- jarvis/jarvis_coder/plan_generator.py +69 -27
- jarvis/jarvis_platform/main.py +38 -38
- jarvis/jarvis_rag/main.py +189 -189
- jarvis/jarvis_smart_shell/main.py +22 -24
- jarvis/models/base.py +6 -1
- jarvis/models/ollama.py +2 -2
- jarvis/models/registry.py +3 -6
- jarvis/tools/ask_user.py +6 -6
- jarvis/tools/codebase_qa.py +5 -7
- jarvis/tools/create_code_sub_agent.py +55 -0
- jarvis/tools/{sub_agent.py → create_sub_agent.py} +4 -1
- jarvis/tools/execute_code_modification.py +72 -0
- jarvis/tools/{file_ops.py → file_operation.py} +13 -14
- jarvis/tools/find_related_files.py +86 -0
- jarvis/tools/methodology.py +25 -25
- jarvis/tools/rag.py +32 -32
- jarvis/tools/registry.py +72 -36
- jarvis/tools/search.py +1 -1
- jarvis/tools/select_code_files.py +64 -0
- jarvis/utils.py +153 -49
- {jarvis_ai_assistant-0.1.97.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.99.dist-info/RECORD +52 -0
- {jarvis_ai_assistant-0.1.97.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/entry_points.txt +2 -1
- jarvis/main.py +0 -155
- jarvis/tools/coder.py +0 -69
- jarvis_ai_assistant-0.1.97.dist-info/RECORD +0 -47
- /jarvis/tools/{shell.py → execute_shell.py} +0 -0
- /jarvis/tools/{generator.py → generate_tool.py} +0 -0
- /jarvis/tools/{webpage.py → read_webpage.py} +0 -0
- {jarvis_ai_assistant-0.1.97.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.97.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.97.dist-info → jarvis_ai_assistant-0.1.99.dist-info}/top_level.txt +0 -0
|
@@ -4,48 +4,46 @@ import os
|
|
|
4
4
|
import sys
|
|
5
5
|
import readline
|
|
6
6
|
from typing import Optional
|
|
7
|
-
from yaspin import yaspin
|
|
8
|
-
from yaspin.spinners import Spinners
|
|
7
|
+
from yaspin import yaspin # type: ignore
|
|
8
|
+
from yaspin.spinners import Spinners # type: ignore
|
|
9
9
|
|
|
10
10
|
from jarvis.models.registry import PlatformRegistry
|
|
11
|
-
from jarvis.utils import PrettyOutput, OutputType, load_env_from_file
|
|
11
|
+
from jarvis.utils import PrettyOutput, OutputType, get_single_line_input, load_env_from_file
|
|
12
12
|
|
|
13
13
|
def execute_command(command: str) -> None:
|
|
14
|
-
"""
|
|
14
|
+
"""Show command and allow user to edit, then execute, Ctrl+C to cancel"""
|
|
15
15
|
try:
|
|
16
|
-
|
|
17
|
-
# 预填充输入行
|
|
16
|
+
# Pre-fill input line
|
|
18
17
|
readline.set_startup_hook(lambda: readline.insert_text(command))
|
|
19
18
|
try:
|
|
20
|
-
edited_command =
|
|
21
|
-
if edited_command.strip(): #
|
|
19
|
+
edited_command = get_single_line_input("Generated command (can be edited, press Enter to execute, Ctrl+C to cancel)")
|
|
20
|
+
if edited_command.strip(): # Ensure command is not empty
|
|
22
21
|
os.system(edited_command)
|
|
23
22
|
except KeyboardInterrupt:
|
|
24
|
-
print("
|
|
23
|
+
PrettyOutput.print("Execution cancelled", OutputType.INFO)
|
|
25
24
|
finally:
|
|
26
|
-
readline.set_startup_hook() #
|
|
25
|
+
readline.set_startup_hook() # Clear pre-filled
|
|
27
26
|
except Exception as e:
|
|
28
|
-
PrettyOutput.print(f"
|
|
27
|
+
PrettyOutput.print(f"Failed to execute command: {str(e)}", OutputType.ERROR)
|
|
29
28
|
|
|
30
29
|
def process_request(request: str) -> Optional[str]:
|
|
31
|
-
"""
|
|
30
|
+
"""Process user request and return corresponding shell command
|
|
32
31
|
|
|
33
32
|
Args:
|
|
34
|
-
request:
|
|
33
|
+
request: User's natural language request
|
|
35
34
|
|
|
36
35
|
Returns:
|
|
37
|
-
Optional[str]:
|
|
36
|
+
Optional[str]: Corresponding shell command, return None if processing fails
|
|
38
37
|
"""
|
|
39
38
|
try:
|
|
40
|
-
#
|
|
41
|
-
PlatformRegistry.suppress_output = True
|
|
39
|
+
# Get language model instance
|
|
42
40
|
model = PlatformRegistry.get_global_platform_registry().get_normal_platform()
|
|
43
41
|
model.set_suppress_output(True)
|
|
44
42
|
|
|
45
43
|
shell = os.environ.get("SHELL") or "bash"
|
|
46
44
|
current_path = os.getcwd()
|
|
47
45
|
|
|
48
|
-
#
|
|
46
|
+
# Set system prompt
|
|
49
47
|
system_message = f"""You are a shell command generation assistant.
|
|
50
48
|
|
|
51
49
|
Your only task is to convert user's natural language requirements into corresponding shell commands.
|
|
@@ -85,26 +83,26 @@ Remember: Only return the command itself, without any additional content.
|
|
|
85
83
|
return None
|
|
86
84
|
|
|
87
85
|
except Exception as e:
|
|
88
|
-
PrettyOutput.print(f"
|
|
86
|
+
PrettyOutput.print(f"Failed to process request: {str(e)}", OutputType.ERROR)
|
|
89
87
|
return None
|
|
90
88
|
|
|
91
89
|
def main():
|
|
92
90
|
# 创建参数解析器
|
|
93
91
|
load_env_from_file()
|
|
94
92
|
parser = argparse.ArgumentParser(
|
|
95
|
-
description="
|
|
93
|
+
description="Convert natural language requirements to shell commands",
|
|
96
94
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
97
95
|
epilog="""
|
|
98
|
-
|
|
99
|
-
%(prog)s "
|
|
100
|
-
%(prog)s "
|
|
101
|
-
%(prog)s "
|
|
96
|
+
Example:
|
|
97
|
+
%(prog)s "Find all Python files in the current directory"
|
|
98
|
+
%(prog)s "Compress all jpg images"
|
|
99
|
+
%(prog)s "Find documents modified in the last week"
|
|
102
100
|
""")
|
|
103
101
|
|
|
104
102
|
# 添加参数
|
|
105
103
|
parser.add_argument(
|
|
106
104
|
"request",
|
|
107
|
-
help="
|
|
105
|
+
help="Describe the operation you want to perform in natural language"
|
|
108
106
|
)
|
|
109
107
|
|
|
110
108
|
# 解析参数
|
jarvis/models/base.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
import re
|
|
2
3
|
from typing import Dict, List, Tuple
|
|
3
4
|
|
|
4
5
|
from jarvis.utils import OutputType, PrettyOutput, while_success, while_true
|
|
@@ -26,7 +27,11 @@ class BasePlatform(ABC):
|
|
|
26
27
|
raise NotImplementedError("chat is not implemented")
|
|
27
28
|
|
|
28
29
|
def chat_until_success(self, message: str) -> str:
|
|
29
|
-
|
|
30
|
+
def _chat():
|
|
31
|
+
response = self.chat(message)
|
|
32
|
+
response = re.sub(r'<think>(.*?)</think>', '', response, flags=re.DOTALL)
|
|
33
|
+
return response
|
|
34
|
+
return while_true(lambda: while_success(lambda: _chat(), 5), 5)
|
|
30
35
|
|
|
31
36
|
@abstractmethod
|
|
32
37
|
def upload_files(self, file_list: List[str]) -> List[Dict]:
|
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/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,7 +23,6 @@ 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:
|
|
@@ -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:
|
jarvis/tools/ask_user.py
CHANGED
|
@@ -18,22 +18,22 @@ 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__":
|
jarvis/tools/codebase_qa.py
CHANGED
|
@@ -4,7 +4,7 @@ from jarvis.jarvis_codebase.main import CodeBase
|
|
|
4
4
|
from jarvis.utils import find_git_root, PrettyOutput, OutputType
|
|
5
5
|
|
|
6
6
|
class CodebaseQATool:
|
|
7
|
-
"""
|
|
7
|
+
"""Codebase QA Tool"""
|
|
8
8
|
|
|
9
9
|
name = "codebase_qa"
|
|
10
10
|
description = "Answer questions about the codebase, can query and understand code functionality, structure, and implementation details"
|
|
@@ -29,7 +29,7 @@ class CodebaseQATool:
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
32
|
-
"""
|
|
32
|
+
"""Execute codebase QA"""
|
|
33
33
|
try:
|
|
34
34
|
dir = params.get("dir")
|
|
35
35
|
question = params["question"]
|
|
@@ -42,7 +42,7 @@ class CodebaseQATool:
|
|
|
42
42
|
return {
|
|
43
43
|
"success": False,
|
|
44
44
|
"stdout": "",
|
|
45
|
-
"stderr": "
|
|
45
|
+
"stderr": "Error: Current directory is not in a Git repository",
|
|
46
46
|
"error": "NotInGitRepository"
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -62,13 +62,11 @@ class CodebaseQATool:
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
except Exception as e:
|
|
65
|
-
PrettyOutput.print(f"
|
|
65
|
+
PrettyOutput.print(f"Codebase QA error: {str(e)}", output_type=OutputType.ERROR)
|
|
66
66
|
return {
|
|
67
67
|
"success": False,
|
|
68
68
|
"stdout": "",
|
|
69
|
-
"stderr": f"
|
|
69
|
+
"stderr": f"Error executing codebase QA: {str(e)}",
|
|
70
70
|
"error": str(type(e).__name__)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
def register():
|
|
74
|
-
return CodebaseQATool()
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
"error": f"Failed to execute code development subtask: {str(e)}"
|
|
55
|
+
}
|
|
@@ -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
|
)
|
|
@@ -0,0 +1,72 @@
|
|
|
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 = 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
|
+
"error": "Code modification was cancelled or failed",
|
|
55
|
+
"stdout": "Changes have been rolled back",
|
|
56
|
+
"stderr": ""
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
"success": True,
|
|
61
|
+
"stdout": "Code modifications have been successfully applied and committed",
|
|
62
|
+
"stderr": ""
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
67
|
+
return {
|
|
68
|
+
"success": False,
|
|
69
|
+
"error": f"Failed to execute code modifications: {str(e)}",
|
|
70
|
+
"stdout": "",
|
|
71
|
+
"stderr": str(e)
|
|
72
|
+
}
|
|
@@ -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)
|
|
@@ -61,15 +60,15 @@ class FileOperationTool:
|
|
|
61
60
|
"error": f"文件不存在: {filepath}"
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
#
|
|
63
|
+
# Check file size
|
|
65
64
|
if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
|
|
66
65
|
return {
|
|
67
66
|
"success": False,
|
|
68
|
-
"error": "
|
|
67
|
+
"error": "File too large (>10MB)"
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
content = open(filepath, 'r', encoding=encoding).read()
|
|
71
|
+
PrettyOutput.print(content, OutputType.INFO)
|
|
73
72
|
return {
|
|
74
73
|
"success": True,
|
|
75
74
|
"stdout": content,
|
|
@@ -80,10 +79,10 @@ class FileOperationTool:
|
|
|
80
79
|
if not args.get("content"):
|
|
81
80
|
return {
|
|
82
81
|
"success": False,
|
|
83
|
-
"error": "
|
|
82
|
+
"error": "Write/append operation requires providing the content parameter"
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
#
|
|
85
|
+
# Create directory (if it doesn't exist)
|
|
87
86
|
os.makedirs(os.path.dirname(os.path.abspath(filepath)), exist_ok=True)
|
|
88
87
|
|
|
89
88
|
mode = 'a' if operation == "append" else 'w'
|
|
@@ -92,19 +91,19 @@ class FileOperationTool:
|
|
|
92
91
|
|
|
93
92
|
return {
|
|
94
93
|
"success": True,
|
|
95
|
-
"stdout": f"
|
|
94
|
+
"stdout": f"Successfully {operation} content to {filepath}",
|
|
96
95
|
"stderr": ""
|
|
97
96
|
}
|
|
98
97
|
|
|
99
98
|
else:
|
|
100
99
|
return {
|
|
101
100
|
"success": False,
|
|
102
|
-
"error": f"
|
|
101
|
+
"error": f"Unknown operation: {operation}"
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
except Exception as e:
|
|
106
105
|
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
107
106
|
return {
|
|
108
107
|
"success": False,
|
|
109
|
-
"error": f"
|
|
108
|
+
"error": f"File operation failed: {str(e)}"
|
|
110
109
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Any, Dict
|
|
3
|
+
from jarvis.jarvis_codebase.main import CodeBase
|
|
4
|
+
from jarvis.utils import find_git_root, PrettyOutput, OutputType
|
|
5
|
+
|
|
6
|
+
class CodebaseSearchTool:
|
|
7
|
+
"""Codebase Search Tool"""
|
|
8
|
+
|
|
9
|
+
name = "find_related_files"
|
|
10
|
+
description = "Find code files related to the given requirement by searching the codebase using NLP-based semantic search. IMPORTANT: Requires complete sentence descriptions - keywords or phrases alone will not yield comprehensive results."
|
|
11
|
+
parameters = {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"properties": {
|
|
14
|
+
"dir": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Project root directory"
|
|
17
|
+
},
|
|
18
|
+
"query": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": """Requirement description to find related code files. Must be a complete sentence that clearly describes what you're looking for.
|
|
21
|
+
|
|
22
|
+
Good examples:
|
|
23
|
+
- 'Need to modify the user authentication process to add password validation'
|
|
24
|
+
- 'Want to update the database connection configuration to support multiple databases'
|
|
25
|
+
- 'Looking for the code that handles file upload functionality in the system'
|
|
26
|
+
|
|
27
|
+
Bad examples (will not work well):
|
|
28
|
+
- 'user auth'
|
|
29
|
+
- 'database config'
|
|
30
|
+
- 'upload'""",
|
|
31
|
+
"examples": [
|
|
32
|
+
"Need to modify the error handling in the authentication process",
|
|
33
|
+
"Want to update how the system processes user uploads",
|
|
34
|
+
"Looking for the code that manages database connections"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"top_k": {
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"description": "Number of most relevant files to return",
|
|
40
|
+
"default": 5
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"required": ["query"]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
def execute(self, params: Dict[str, Any]) -> Dict[str, Any]:
|
|
47
|
+
"""Execute codebase search"""
|
|
48
|
+
try:
|
|
49
|
+
dir = params.get("dir")
|
|
50
|
+
query = params["query"]
|
|
51
|
+
top_k = params.get("top_k", 5)
|
|
52
|
+
|
|
53
|
+
# Find the root directory of the codebase
|
|
54
|
+
current_dir = os.getcwd()
|
|
55
|
+
root_dir = find_git_root(dir or current_dir)
|
|
56
|
+
if not root_dir:
|
|
57
|
+
return {
|
|
58
|
+
"success": False,
|
|
59
|
+
"stdout": "",
|
|
60
|
+
"stderr": "Error: Current directory is not in a Git repository",
|
|
61
|
+
"error": "NotInGitRepository"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
os.chdir(root_dir)
|
|
65
|
+
codebase = CodeBase(root_dir)
|
|
66
|
+
# Generate index
|
|
67
|
+
|
|
68
|
+
codebase.generate_codebase()
|
|
69
|
+
# Execute search
|
|
70
|
+
response = codebase.search_similar(query, top_k)
|
|
71
|
+
os.chdir(current_dir)
|
|
72
|
+
return {
|
|
73
|
+
"success": True,
|
|
74
|
+
"stdout": str(response),
|
|
75
|
+
"stderr": "",
|
|
76
|
+
"error": None
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
except Exception as e:
|
|
80
|
+
PrettyOutput.print(f"Codebase QA error: {str(e)}", output_type=OutputType.ERROR)
|
|
81
|
+
return {
|
|
82
|
+
"success": False,
|
|
83
|
+
"stdout": "",
|
|
84
|
+
"stderr": f"Error executing codebase QA: {str(e)}",
|
|
85
|
+
"error": str(type(e).__name__)
|
|
86
|
+
}
|