jarvis-ai-assistant 0.1.101__py3-none-any.whl → 0.1.103__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 +140 -140
- jarvis/jarvis_code_agent/code_agent.py +234 -0
- jarvis/{jarvis_coder → jarvis_code_agent}/file_select.py +16 -17
- jarvis/jarvis_code_agent/patch.py +118 -0
- jarvis/jarvis_code_agent/relevant_files.py +66 -0
- jarvis/jarvis_codebase/main.py +32 -29
- jarvis/jarvis_platform/main.py +5 -3
- jarvis/jarvis_rag/main.py +11 -15
- jarvis/jarvis_smart_shell/main.py +2 -2
- jarvis/models/ai8.py +1 -0
- jarvis/models/kimi.py +36 -30
- jarvis/models/ollama.py +17 -11
- jarvis/models/openai.py +15 -12
- jarvis/models/oyi.py +22 -7
- jarvis/models/registry.py +1 -25
- jarvis/tools/__init__.py +0 -6
- jarvis/tools/ask_codebase.py +99 -0
- jarvis/tools/ask_user.py +1 -9
- jarvis/tools/chdir.py +1 -1
- jarvis/tools/code_review.py +163 -0
- jarvis/tools/create_code_sub_agent.py +19 -45
- jarvis/tools/create_code_test_agent.py +115 -0
- jarvis/tools/create_ctags_agent.py +176 -0
- jarvis/tools/create_sub_agent.py +2 -2
- jarvis/tools/execute_shell.py +2 -2
- jarvis/tools/file_operation.py +2 -2
- jarvis/tools/find_in_codebase.py +108 -0
- jarvis/tools/git_commiter.py +68 -0
- jarvis/tools/methodology.py +3 -3
- jarvis/tools/rag.py +6 -3
- jarvis/tools/read_code.py +147 -0
- jarvis/tools/read_webpage.py +1 -1
- jarvis/tools/registry.py +92 -68
- jarvis/tools/search.py +8 -6
- jarvis/tools/select_code_files.py +4 -4
- jarvis/utils.py +270 -95
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/METADATA +9 -5
- jarvis_ai_assistant-0.1.103.dist-info/RECORD +51 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/entry_points.txt +4 -2
- jarvis/jarvis_code_agent/main.py +0 -202
- jarvis/jarvis_coder/__init__.py +0 -0
- jarvis/jarvis_coder/git_utils.py +0 -123
- jarvis/jarvis_coder/main.py +0 -241
- jarvis/jarvis_coder/patch_handler.py +0 -340
- jarvis/jarvis_coder/plan_generator.py +0 -145
- jarvis/tools/execute_code_modification.py +0 -70
- jarvis/tools/find_files.py +0 -119
- jarvis/tools/generate_tool.py +0 -174
- jarvis/tools/thinker.py +0 -151
- jarvis_ai_assistant-0.1.101.dist-info/RECORD +0 -51
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.101.dist-info → jarvis_ai_assistant-0.1.103.dist-info}/top_level.txt +0 -0
jarvis/jarvis_code_agent/main.py
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
from jarvis.agent import Agent
|
|
2
|
-
from jarvis.tools.registry import ToolRegistry
|
|
3
|
-
from jarvis.utils import OutputType, PrettyOutput, get_multiline_input, init_env
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
system_prompt = """You are Jarvis Code Agent, an AI code development assistant specialized in code analysis, modification, and version control. Your role is to help users with coding tasks systematically and reliably.
|
|
9
|
-
|
|
10
|
-
DEVELOPMENT WORKFLOW:
|
|
11
|
-
1. Task Analysis
|
|
12
|
-
- Understand the requirements thoroughly
|
|
13
|
-
- IMPORTANT: Before suggesting any changes:
|
|
14
|
-
* Thoroughly examine existing code implementation
|
|
15
|
-
* Never assume code structure or implementation details
|
|
16
|
-
* Always verify current code behavior and patterns
|
|
17
|
-
- Break down complex tasks into subtasks
|
|
18
|
-
- IMPORTANT: Each subtask should:
|
|
19
|
-
* Modify only ONE file
|
|
20
|
-
* Change no more than 20 lines of code
|
|
21
|
-
* Be focused and atomic
|
|
22
|
-
- Define success criteria
|
|
23
|
-
|
|
24
|
-
2. Code Discovery & Analysis
|
|
25
|
-
- Initial code search:
|
|
26
|
-
* CRITICAL: Always examine actual code implementation first
|
|
27
|
-
* Use shell commands to find and understand patterns:
|
|
28
|
-
<TOOL_CALL>
|
|
29
|
-
name: execute_shell
|
|
30
|
-
arguments:
|
|
31
|
-
command: grep -r 'pattern' directory/
|
|
32
|
-
</TOOL_CALL>
|
|
33
|
-
<TOOL_CALL>
|
|
34
|
-
name: execute_shell
|
|
35
|
-
arguments:
|
|
36
|
-
command: grep -A 5 -B 5 'pattern' file.py
|
|
37
|
-
</TOOL_CALL>
|
|
38
|
-
* Use shell commands to locate files:
|
|
39
|
-
<TOOL_CALL>
|
|
40
|
-
name: execute_shell
|
|
41
|
-
arguments:
|
|
42
|
-
command: find . -name 'pattern'
|
|
43
|
-
</TOOL_CALL>
|
|
44
|
-
* Use shell commands to preview:
|
|
45
|
-
<TOOL_CALL>
|
|
46
|
-
name: execute_shell
|
|
47
|
-
arguments:
|
|
48
|
-
command: head -n 50 file.py
|
|
49
|
-
</TOOL_CALL>
|
|
50
|
-
- File selection and confirmation:
|
|
51
|
-
* Let user confirm selection:
|
|
52
|
-
<TOOL_CALL>
|
|
53
|
-
name: select_code_files
|
|
54
|
-
arguments:
|
|
55
|
-
related_files:
|
|
56
|
-
- auth.py
|
|
57
|
-
- user.py
|
|
58
|
-
root_dir: .
|
|
59
|
-
</TOOL_CALL>
|
|
60
|
-
|
|
61
|
-
3. Modification Planning
|
|
62
|
-
- CRITICAL: Base all plans on actual code implementation, not assumptions
|
|
63
|
-
- Generate a detailed modification plan based on:
|
|
64
|
-
* Current code structure and patterns
|
|
65
|
-
* Existing implementation details
|
|
66
|
-
* User requirements
|
|
67
|
-
* Actual code conditions
|
|
68
|
-
|
|
69
|
-
4. Code Implementation
|
|
70
|
-
- For small changes (≤20 lines):
|
|
71
|
-
<TOOL_CALL>
|
|
72
|
-
name: execute_code_modification
|
|
73
|
-
arguments:
|
|
74
|
-
task: Add password validation
|
|
75
|
-
structured_plan:
|
|
76
|
-
auth.py: Add password strength check in validate_password()
|
|
77
|
-
</TOOL_CALL>
|
|
78
|
-
- For large changes:
|
|
79
|
-
<TOOL_CALL>
|
|
80
|
-
name: create_code_sub_agent
|
|
81
|
-
arguments:
|
|
82
|
-
subtask: Implement new authentication flow
|
|
83
|
-
codebase_dir: .
|
|
84
|
-
</TOOL_CALL>
|
|
85
|
-
|
|
86
|
-
FILE SELECTION WORKFLOW:
|
|
87
|
-
1. Initial Search
|
|
88
|
-
- Use shell commands to find relevant files
|
|
89
|
-
- Review search results for relevance
|
|
90
|
-
|
|
91
|
-
2. User Confirmation
|
|
92
|
-
- Use select_code_files to:
|
|
93
|
-
* Display found files
|
|
94
|
-
* Let user review selection
|
|
95
|
-
* Allow file list adjustment
|
|
96
|
-
* Enable file supplementation
|
|
97
|
-
|
|
98
|
-
3. File Validation
|
|
99
|
-
- Verify selected files exist
|
|
100
|
-
- Check file permissions
|
|
101
|
-
- Validate file types
|
|
102
|
-
- Ensure completeness
|
|
103
|
-
|
|
104
|
-
CODE SEARCH BEST PRACTICES:
|
|
105
|
-
- Use grep for pattern matching:
|
|
106
|
-
* grep -r "pattern" directory/
|
|
107
|
-
* grep -A 5 -B 5 for context
|
|
108
|
-
* grep -n for line numbers
|
|
109
|
-
- Use find for file location:
|
|
110
|
-
* find . -name "pattern"
|
|
111
|
-
* find . -type f -exec grep "pattern" {} \\;
|
|
112
|
-
- Use head/tail for previews:
|
|
113
|
-
* head -n 50 file.py
|
|
114
|
-
* tail -n 50 file.py
|
|
115
|
-
* head -n +100 | tail -n 50
|
|
116
|
-
- Avoid loading entire large files
|
|
117
|
-
- Focus on relevant sections
|
|
118
|
-
- Use line numbers for reference
|
|
119
|
-
|
|
120
|
-
SUBTASK MANAGEMENT RULES:
|
|
121
|
-
- One subtask = One file modification
|
|
122
|
-
- Each subtask ≤20 lines of code changes
|
|
123
|
-
- Break down larger changes into multiple subtasks
|
|
124
|
-
- Create separate sub-agent for each subtask
|
|
125
|
-
- Follow dependency order in execution
|
|
126
|
-
- Verify each change independently
|
|
127
|
-
|
|
128
|
-
CODE MODIFICATION LIMITS:
|
|
129
|
-
- Maximum 20 lines per change
|
|
130
|
-
- Count both added and modified lines
|
|
131
|
-
- Exclude comment and blank lines
|
|
132
|
-
- Include only actual code changes
|
|
133
|
-
- Split larger changes into subtasks
|
|
134
|
-
|
|
135
|
-
ITERATION GUIDELINES:
|
|
136
|
-
- Each iteration should be small and focused
|
|
137
|
-
- Keep changes minimal and clear
|
|
138
|
-
- Verify changes before moving forward
|
|
139
|
-
- Document issues and solutions
|
|
140
|
-
- Learn from previous iterations
|
|
141
|
-
|
|
142
|
-
TOOL USAGE:
|
|
143
|
-
1. Analysis Tools:
|
|
144
|
-
- execute_shell: Run grep/find/head/tail commands
|
|
145
|
-
- find_files: Search and identify relevant code files in the codebase based on requirements or problems
|
|
146
|
-
- select_code_files: Confirm and supplement files
|
|
147
|
-
- ask_user: Ask user for confirmation and information if needed
|
|
148
|
-
- create_code_sub_agent: Create agent for each small change
|
|
149
|
-
- file_operation: Read files
|
|
150
|
-
- rag: Ask questions based on a document directory, supporting multiple document formats (txt, pdf, docx, etc.)
|
|
151
|
-
- search: Use Bing search engine to search for information, and extract key information based on the question
|
|
152
|
-
- thinker: Deep thinking and logical reasoning
|
|
153
|
-
|
|
154
|
-
2. Planning Tools:
|
|
155
|
-
- thinker: Generate a detailed modification plan based on user requirements and actual code conditions.
|
|
156
|
-
- create_code_sub_agent: Create agent for each small change
|
|
157
|
-
- ask_user: Ask user for confirmation and information if needed
|
|
158
|
-
|
|
159
|
-
3. Implementation Tools:
|
|
160
|
-
- execute_shell: Run shell commands, some changes can use sed/awk/etc. to modify the code
|
|
161
|
-
- execute_code_modification: Apply small changes (≤20 lines)
|
|
162
|
-
- file_operation: Read, write, or append to files
|
|
163
|
-
|
|
164
|
-
IMPORTANT:
|
|
165
|
-
1. If you can start executing the task, please start directly without asking the user if you can begin.
|
|
166
|
-
2. NEVER assume code structure or implementation - always examine the actual code first.
|
|
167
|
-
3. Base all suggestions and modifications on the current implementation, not assumptions.
|
|
168
|
-
4. If code implementation is unclear, use available tools to investigate before proceeding.
|
|
169
|
-
5. Before you start modifying the code, you should ask the user for confirmation of the modification plan.
|
|
170
|
-
6. For some small changes, you can modify the code using the execute_shell tool directly or use file_operation tool to read the file and modify it.
|
|
171
|
-
"""
|
|
172
|
-
|
|
173
|
-
def main():
|
|
174
|
-
"""Jarvis main entry point"""
|
|
175
|
-
# Add argument parser
|
|
176
|
-
init_env()
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
try:
|
|
180
|
-
tool_registry = ToolRegistry()
|
|
181
|
-
tool_registry.dont_use_tools(["create_sub_agent"])
|
|
182
|
-
# Get global model instance
|
|
183
|
-
agent = Agent(system_prompt=system_prompt, name="Jarvis Code Agent", tool_registry=tool_registry)
|
|
184
|
-
|
|
185
|
-
# Interactive mode
|
|
186
|
-
while True:
|
|
187
|
-
try:
|
|
188
|
-
user_input = get_multiline_input("Please enter your task (input empty line to exit):")
|
|
189
|
-
if not user_input or user_input == "__interrupt__":
|
|
190
|
-
break
|
|
191
|
-
agent.run(user_input)
|
|
192
|
-
except Exception as e:
|
|
193
|
-
PrettyOutput.print(f"Error: {str(e)}", OutputType.ERROR)
|
|
194
|
-
|
|
195
|
-
except Exception as e:
|
|
196
|
-
PrettyOutput.print(f"Initialization error: {str(e)}", OutputType.ERROR)
|
|
197
|
-
return 1
|
|
198
|
-
|
|
199
|
-
return 0
|
|
200
|
-
|
|
201
|
-
if __name__ == "__main__":
|
|
202
|
-
exit(main())
|
jarvis/jarvis_coder/__init__.py
DELETED
|
File without changes
|
jarvis/jarvis_coder/git_utils.py
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from typing import List
|
|
3
|
-
import yaml
|
|
4
|
-
import time
|
|
5
|
-
from jarvis.utils import OutputType, PrettyOutput, find_git_root, while_success
|
|
6
|
-
from jarvis.models.registry import PlatformRegistry
|
|
7
|
-
|
|
8
|
-
def has_uncommitted_files() -> bool:
|
|
9
|
-
"""Check if there are uncommitted files in the repository"""
|
|
10
|
-
# Get unstaged modifications
|
|
11
|
-
unstaged = os.popen("git diff --name-only").read()
|
|
12
|
-
# Get staged but uncommitted modifications
|
|
13
|
-
staged = os.popen("git diff --cached --name-only").read()
|
|
14
|
-
# Get untracked files
|
|
15
|
-
untracked = os.popen("git ls-files --others --exclude-standard").read()
|
|
16
|
-
|
|
17
|
-
return bool(unstaged or staged or untracked)
|
|
18
|
-
|
|
19
|
-
def generate_commit_message(git_diff: str) -> str:
|
|
20
|
-
"""Generate commit message based on git diff and feature description"""
|
|
21
|
-
prompt = f"""You are an experienced programmer, please generate a concise and clear commit message based on the following code changes and feature description:
|
|
22
|
-
|
|
23
|
-
Code changes:
|
|
24
|
-
Git Diff:
|
|
25
|
-
{git_diff}
|
|
26
|
-
|
|
27
|
-
Please follow these rules:
|
|
28
|
-
1. Write in English
|
|
29
|
-
2. Use conventional commit message format: <type>(<scope>): <subject>
|
|
30
|
-
3. Keep it concise, no more than 50 characters
|
|
31
|
-
4. Accurately describe the main content of code changes
|
|
32
|
-
5. Prioritize feature description and changes in git diff
|
|
33
|
-
6. Only generate the commit message text, do not output anything else
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
model = PlatformRegistry().get_global_platform_registry().get_normal_platform()
|
|
37
|
-
response = model.chat_until_success(prompt)
|
|
38
|
-
|
|
39
|
-
return ';'.join(response.strip().split("\n"))
|
|
40
|
-
|
|
41
|
-
def save_edit_record(record_dir: str, commit_message: str, git_diff: str) -> None:
|
|
42
|
-
"""Save code modification record"""
|
|
43
|
-
# Get next sequence number
|
|
44
|
-
existing_records = [f for f in os.listdir(record_dir) if f.endswith('.yaml')]
|
|
45
|
-
next_num = 1
|
|
46
|
-
if existing_records:
|
|
47
|
-
last_num = max(int(f[:4]) for f in existing_records)
|
|
48
|
-
next_num = last_num + 1
|
|
49
|
-
|
|
50
|
-
# Create record file
|
|
51
|
-
record = {
|
|
52
|
-
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
53
|
-
"commit_message": commit_message,
|
|
54
|
-
"git_diff": git_diff
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
record_path = os.path.join(record_dir, f"{next_num:04d}.yaml")
|
|
58
|
-
with open(record_path, "w", encoding="utf-8") as f:
|
|
59
|
-
yaml.safe_dump(record, f, allow_unicode=True)
|
|
60
|
-
|
|
61
|
-
PrettyOutput.print(f"Modification record saved: {record_path}", OutputType.SUCCESS)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def init_git_repo(root_dir: str) -> str:
|
|
65
|
-
git_dir = find_git_root(root_dir)
|
|
66
|
-
if not git_dir:
|
|
67
|
-
git_dir = root_dir
|
|
68
|
-
|
|
69
|
-
PrettyOutput.print(f"Git root directory: {git_dir}", OutputType.INFO)
|
|
70
|
-
|
|
71
|
-
# 1. Check if the code repository path exists, if it does not exist, create it
|
|
72
|
-
if not os.path.exists(git_dir):
|
|
73
|
-
PrettyOutput.print(
|
|
74
|
-
"Root directory does not exist, creating...", OutputType.INFO)
|
|
75
|
-
os.makedirs(git_dir)
|
|
76
|
-
|
|
77
|
-
os.chdir(git_dir)
|
|
78
|
-
|
|
79
|
-
# 3. Process .gitignore file
|
|
80
|
-
gitignore_path = os.path.join(git_dir, ".gitignore")
|
|
81
|
-
gitignore_modified = False
|
|
82
|
-
jarvis_ignore_pattern = ".jarvis"
|
|
83
|
-
|
|
84
|
-
# 3.1 If .gitignore does not exist, create it
|
|
85
|
-
if not os.path.exists(gitignore_path):
|
|
86
|
-
PrettyOutput.print("Create .gitignore file", OutputType.INFO)
|
|
87
|
-
with open(gitignore_path, "w", encoding="utf-8") as f:
|
|
88
|
-
f.write(f"{jarvis_ignore_pattern}\n")
|
|
89
|
-
gitignore_modified = True
|
|
90
|
-
else:
|
|
91
|
-
# 3.2 Check if it already contains the .jarvis pattern
|
|
92
|
-
with open(gitignore_path, "r", encoding="utf-8") as f:
|
|
93
|
-
content = f.read()
|
|
94
|
-
|
|
95
|
-
# 3.2 Check if it already contains the .jarvis pattern
|
|
96
|
-
if jarvis_ignore_pattern not in content.split("\n"):
|
|
97
|
-
PrettyOutput.print("Add .jarvis to .gitignore", OutputType.INFO)
|
|
98
|
-
with open(gitignore_path, "a", encoding="utf-8") as f:
|
|
99
|
-
# Ensure the file ends with a newline
|
|
100
|
-
if not content.endswith("\n"):
|
|
101
|
-
f.write("\n")
|
|
102
|
-
f.write(f"{jarvis_ignore_pattern}\n")
|
|
103
|
-
gitignore_modified = True
|
|
104
|
-
|
|
105
|
-
# 4. Check if the code repository is a git repository, if not, initialize the git repository
|
|
106
|
-
if not os.path.exists(os.path.join(git_dir, ".git")):
|
|
107
|
-
PrettyOutput.print("Initialize Git repository", OutputType.INFO)
|
|
108
|
-
os.system("git init")
|
|
109
|
-
os.system("git add .")
|
|
110
|
-
os.system("git commit -m 'Initial commit'")
|
|
111
|
-
# 5. If .gitignore is modified, commit the changes
|
|
112
|
-
elif gitignore_modified:
|
|
113
|
-
PrettyOutput.print("Commit .gitignore changes", OutputType.INFO)
|
|
114
|
-
os.system("git add .gitignore")
|
|
115
|
-
os.system("git commit -m 'chore: update .gitignore to exclude .jarvis-* files'")
|
|
116
|
-
# 6. Check if there are uncommitted files in the code repository, if there are, commit once
|
|
117
|
-
elif has_uncommitted_files():
|
|
118
|
-
PrettyOutput.print("Commit uncommitted changes", OutputType.INFO)
|
|
119
|
-
os.system("git add .")
|
|
120
|
-
git_diff = os.popen("git diff --cached").read()
|
|
121
|
-
commit_message = generate_commit_message(git_diff)
|
|
122
|
-
os.system(f"git commit -m '{commit_message}'")
|
|
123
|
-
return git_dir
|
jarvis/jarvis_coder/main.py
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import threading
|
|
3
|
-
from typing import Dict, Any, List, Optional
|
|
4
|
-
import re
|
|
5
|
-
|
|
6
|
-
from jarvis.jarvis_coder.file_select import select_files
|
|
7
|
-
from jarvis.utils import OutputType, PrettyOutput, find_git_root, get_max_context_length, is_long_context, init_env, while_success
|
|
8
|
-
from jarvis.models.registry import PlatformRegistry
|
|
9
|
-
from jarvis.jarvis_codebase.main import CodeBase
|
|
10
|
-
from prompt_toolkit import PromptSession
|
|
11
|
-
from prompt_toolkit.completion import WordCompleter, Completer, Completion
|
|
12
|
-
from prompt_toolkit.formatted_text import FormattedText
|
|
13
|
-
from prompt_toolkit.styles import Style
|
|
14
|
-
import fnmatch
|
|
15
|
-
from .patch_handler import PatchHandler
|
|
16
|
-
from .git_utils import generate_commit_message, init_git_repo, save_edit_record
|
|
17
|
-
from .plan_generator import PlanGenerator
|
|
18
|
-
|
|
19
|
-
# 全局锁对象
|
|
20
|
-
index_lock = threading.Lock()
|
|
21
|
-
|
|
22
|
-
class JarvisCoder:
|
|
23
|
-
def __init__(self, root_dir: str, language: Optional[str] = "python"):
|
|
24
|
-
"""Initialize code modification tool"""
|
|
25
|
-
self.root_dir = root_dir
|
|
26
|
-
self.language = language
|
|
27
|
-
self._init_directories()
|
|
28
|
-
self._init_codebase()
|
|
29
|
-
|
|
30
|
-
def _init_directories(self):
|
|
31
|
-
"""Initialize directories"""
|
|
32
|
-
self.max_context_length = get_max_context_length()
|
|
33
|
-
self.root_dir = init_git_repo(self.root_dir)
|
|
34
|
-
|
|
35
|
-
def _init_codebase(self):
|
|
36
|
-
"""Initialize codebase"""
|
|
37
|
-
self._codebase = CodeBase(self.root_dir)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _load_related_files(self, feature: str) -> List[str]:
|
|
41
|
-
"""Load related file content"""
|
|
42
|
-
ret = []
|
|
43
|
-
# Ensure the index database is generated
|
|
44
|
-
if not self._codebase.is_index_generated():
|
|
45
|
-
PrettyOutput.print("Index database not generated, generating...", OutputType.WARNING)
|
|
46
|
-
self._codebase.generate_codebase()
|
|
47
|
-
|
|
48
|
-
related_files = self._codebase.search_similar(feature)
|
|
49
|
-
for file in related_files:
|
|
50
|
-
PrettyOutput.print(f"Related file: {file}", OutputType.SUCCESS)
|
|
51
|
-
ret.append(file)
|
|
52
|
-
return ret
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def execute(self, feature: str) -> Dict[str, Any]:
|
|
57
|
-
"""Execute code modification"""
|
|
58
|
-
try:
|
|
59
|
-
# Get and select related files
|
|
60
|
-
initial_files = self._load_related_files(feature)
|
|
61
|
-
selected_files = select_files(initial_files, self.root_dir)
|
|
62
|
-
|
|
63
|
-
# Get modification plan
|
|
64
|
-
structed_plan = PlanGenerator().generate_plan(feature, selected_files)
|
|
65
|
-
if not structed_plan:
|
|
66
|
-
return {
|
|
67
|
-
"success": False,
|
|
68
|
-
"stdout": "",
|
|
69
|
-
"stderr": "Failed to generate modification plan, please modify the requirement and try again",
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
# Execute modification
|
|
73
|
-
if PatchHandler().handle_patch_application(feature, structed_plan):
|
|
74
|
-
return {
|
|
75
|
-
"success": True,
|
|
76
|
-
"stdout": "Code modification successful",
|
|
77
|
-
"stderr": "",
|
|
78
|
-
}
|
|
79
|
-
else:
|
|
80
|
-
return {
|
|
81
|
-
"success": False,
|
|
82
|
-
"stdout": "",
|
|
83
|
-
"stderr": "Code modification failed, please modify the requirement and try again",
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
except Exception as e:
|
|
87
|
-
return {
|
|
88
|
-
"success": False,
|
|
89
|
-
"stdout": "",
|
|
90
|
-
"stderr": f"Execution failed: {str(e)}, please modify the requirement and try again",
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
def main():
|
|
94
|
-
"""Command line entry"""
|
|
95
|
-
import argparse
|
|
96
|
-
|
|
97
|
-
init_env()
|
|
98
|
-
|
|
99
|
-
parser = argparse.ArgumentParser(description='Code modification tool')
|
|
100
|
-
parser.add_argument('-d', '--dir', help='Project root directory', default=os.getcwd())
|
|
101
|
-
parser.add_argument('-l', '--language', help='Programming language', default="python")
|
|
102
|
-
args = parser.parse_args()
|
|
103
|
-
|
|
104
|
-
tool = JarvisCoder(args.dir, args.language)
|
|
105
|
-
|
|
106
|
-
# Loop through requirements
|
|
107
|
-
while True:
|
|
108
|
-
try:
|
|
109
|
-
# Get requirements, pass in project root directory
|
|
110
|
-
feature = get_multiline_input("Please enter the development requirements (input empty line to exit):", tool.root_dir)
|
|
111
|
-
|
|
112
|
-
if not feature or feature == "__interrupt__":
|
|
113
|
-
break
|
|
114
|
-
|
|
115
|
-
# Execute modification
|
|
116
|
-
result = tool.execute(feature)
|
|
117
|
-
|
|
118
|
-
# Display results
|
|
119
|
-
if result["success"]:
|
|
120
|
-
PrettyOutput.print(result["stdout"], OutputType.SUCCESS)
|
|
121
|
-
else:
|
|
122
|
-
if result.get("stderr"):
|
|
123
|
-
PrettyOutput.print(result["stderr"], OutputType.WARNING)
|
|
124
|
-
PrettyOutput.print("\nYou can modify the requirements and try again", OutputType.INFO)
|
|
125
|
-
|
|
126
|
-
except KeyboardInterrupt:
|
|
127
|
-
print("\nUser interrupted execution")
|
|
128
|
-
break
|
|
129
|
-
except Exception as e:
|
|
130
|
-
PrettyOutput.print(f"Execution failed: {str(e)}", OutputType.ERROR)
|
|
131
|
-
PrettyOutput.print("\nYou can modify the requirements and try again", OutputType.INFO)
|
|
132
|
-
continue
|
|
133
|
-
|
|
134
|
-
return 0
|
|
135
|
-
|
|
136
|
-
if __name__ == "__main__":
|
|
137
|
-
exit(main())
|
|
138
|
-
|
|
139
|
-
class FilePathCompleter(Completer):
|
|
140
|
-
"""File path auto-completer"""
|
|
141
|
-
|
|
142
|
-
def __init__(self, root_dir: str):
|
|
143
|
-
self.root_dir = root_dir
|
|
144
|
-
self._file_list = None
|
|
145
|
-
|
|
146
|
-
def _get_files(self) -> List[str]:
|
|
147
|
-
"""Get the list of files managed by git"""
|
|
148
|
-
if self._file_list is None:
|
|
149
|
-
try:
|
|
150
|
-
# Switch to project root directory
|
|
151
|
-
old_cwd = os.getcwd()
|
|
152
|
-
os.chdir(self.root_dir)
|
|
153
|
-
|
|
154
|
-
# Get the list of files managed by git
|
|
155
|
-
self._file_list = os.popen("git ls-files").read().splitlines()
|
|
156
|
-
|
|
157
|
-
# Restore working directory
|
|
158
|
-
os.chdir(old_cwd)
|
|
159
|
-
except Exception as e:
|
|
160
|
-
PrettyOutput.print(f"Failed to get file list: {str(e)}", OutputType.WARNING)
|
|
161
|
-
self._file_list = []
|
|
162
|
-
return self._file_list
|
|
163
|
-
|
|
164
|
-
def get_completions(self, document, complete_event):
|
|
165
|
-
"""Get completion suggestions"""
|
|
166
|
-
text_before_cursor = document.text_before_cursor
|
|
167
|
-
|
|
168
|
-
# Check if @ was just entered
|
|
169
|
-
if text_before_cursor.endswith('@'):
|
|
170
|
-
# Display all files
|
|
171
|
-
for path in self._get_files():
|
|
172
|
-
yield Completion(path, start_position=0)
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
# Check if there was an @ before, and get the search word after @
|
|
176
|
-
at_pos = text_before_cursor.rfind('@')
|
|
177
|
-
if at_pos == -1:
|
|
178
|
-
return
|
|
179
|
-
|
|
180
|
-
search = text_before_cursor[at_pos + 1:].lower().strip()
|
|
181
|
-
|
|
182
|
-
# Provide matching file suggestions
|
|
183
|
-
for path in self._get_files():
|
|
184
|
-
path_lower = path.lower()
|
|
185
|
-
if (search in path_lower or # Directly included
|
|
186
|
-
search in os.path.basename(path_lower) or # File name included
|
|
187
|
-
any(fnmatch.fnmatch(path_lower, f'*{s}*') for s in search.split())): # Wildcard matching
|
|
188
|
-
# Calculate the correct start_position
|
|
189
|
-
yield Completion(path, start_position=-(len(search)))
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def get_multiline_input(prompt_text: str, root_dir: Optional[str] = ".") -> str:
|
|
193
|
-
"""Get multi-line input, support file path auto-completion function
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
prompt_text: Prompt text
|
|
197
|
-
root_dir: Project root directory, for file completion
|
|
198
|
-
|
|
199
|
-
Returns:
|
|
200
|
-
str: User input text
|
|
201
|
-
"""
|
|
202
|
-
# Create file completion
|
|
203
|
-
file_completer = FilePathCompleter(root_dir or os.getcwd())
|
|
204
|
-
|
|
205
|
-
# Create prompt style
|
|
206
|
-
style = Style.from_dict({
|
|
207
|
-
'prompt': 'ansicyan bold',
|
|
208
|
-
'input': 'ansiwhite',
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
# Create session
|
|
212
|
-
session = PromptSession(
|
|
213
|
-
completer=file_completer,
|
|
214
|
-
style=style,
|
|
215
|
-
multiline=False,
|
|
216
|
-
enable_history_search=True,
|
|
217
|
-
complete_while_typing=True
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
# Display initial prompt text
|
|
221
|
-
print(f"\n{prompt_text}")
|
|
222
|
-
|
|
223
|
-
# Create prompt
|
|
224
|
-
prompt = FormattedText([
|
|
225
|
-
('class:prompt', ">>> ")
|
|
226
|
-
])
|
|
227
|
-
|
|
228
|
-
# Get input
|
|
229
|
-
lines = []
|
|
230
|
-
try:
|
|
231
|
-
while True:
|
|
232
|
-
line = session.prompt(prompt).strip()
|
|
233
|
-
if not line: # Empty line means input end
|
|
234
|
-
break
|
|
235
|
-
lines.append(line)
|
|
236
|
-
except KeyboardInterrupt:
|
|
237
|
-
return "__interrupt__"
|
|
238
|
-
except EOFError:
|
|
239
|
-
pass
|
|
240
|
-
|
|
241
|
-
return "\n".join(lines)
|