jarvis-ai-assistant 0.1.123__py3-none-any.whl → 0.1.124__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jarvis-ai-assistant might be problematic. Click here for more details.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +1 -1
- jarvis/jarvis_code_agent/code_agent.py +47 -111
- jarvis/jarvis_code_agent/file_select.py +0 -86
- jarvis/jarvis_code_agent/patch.py +158 -104
- jarvis/jarvis_platform/base.py +1 -1
- jarvis/jarvis_tools/chdir.py +25 -0
- jarvis/jarvis_tools/create_code_agent.py +3 -6
- jarvis/jarvis_tools/execute_shell_script.py +58 -0
- jarvis/jarvis_tools/git_commiter.py +21 -15
- jarvis/jarvis_tools/search.py +0 -1
- jarvis/jarvis_utils/__init__.py +60 -19
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/METADATA +6 -7
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/RECORD +18 -18
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/WHEEL +1 -1
- jarvis/jarvis_code_agent/relevant_files.py +0 -117
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/top_level.txt +0 -0
jarvis/__init__.py
CHANGED
jarvis/jarvis_agent/__init__.py
CHANGED
|
@@ -327,7 +327,7 @@ Please continue the task based on the above information.
|
|
|
327
327
|
return self._complete_task()
|
|
328
328
|
|
|
329
329
|
# 获取用户输入
|
|
330
|
-
user_input = get_multiline_input(f"{self.name}:
|
|
330
|
+
user_input = get_multiline_input(f"{self.name}: 请输入,或输入空行来结束当前任务:")
|
|
331
331
|
|
|
332
332
|
if user_input:
|
|
333
333
|
self.prompt = user_input
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import subprocess
|
|
1
2
|
import os
|
|
2
3
|
from typing import Dict, List
|
|
3
4
|
|
|
4
5
|
from jarvis.jarvis_agent import Agent
|
|
5
|
-
from jarvis.jarvis_code_agent.file_select import
|
|
6
|
-
from jarvis.jarvis_code_agent.patch import PatchOutputHandler
|
|
7
|
-
from jarvis.jarvis_code_agent.relevant_files import find_relevant_information
|
|
6
|
+
from jarvis.jarvis_code_agent.file_select import select_files
|
|
7
|
+
from jarvis.jarvis_code_agent.patch import PatchOutputHandler, file_input_handler
|
|
8
8
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
9
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
10
10
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
11
11
|
from jarvis.jarvis_tools.read_code import ReadCodeTool
|
|
12
|
+
from jarvis.jarvis_utils import get_commits_between
|
|
13
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root, user_confirm, get_latest_commit_hash
|
|
12
14
|
from jarvis.jarvis_utils import OutputType, PrettyOutput, get_multiline_input, has_uncommitted_changes, init_env, find_git_root, user_confirm
|
|
13
15
|
|
|
14
16
|
|
|
@@ -18,6 +20,7 @@ class CodeAgent:
|
|
|
18
20
|
tool_registry = ToolRegistry()
|
|
19
21
|
tool_registry.use_tools(["read_code",
|
|
20
22
|
"execute_shell",
|
|
23
|
+
"execute_shell_script",
|
|
21
24
|
"search",
|
|
22
25
|
"create_code_agent",
|
|
23
26
|
"ask_user",
|
|
@@ -29,68 +32,32 @@ class CodeAgent:
|
|
|
29
32
|
"lsp_prepare_rename",
|
|
30
33
|
"lsp_validate_edit"])
|
|
31
34
|
code_system_prompt = """
|
|
32
|
-
# Role:
|
|
33
|
-
Expert in precise code modifications with
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
- Treat each change as irreversible surgery
|
|
49
|
-
- Match indentation like matching DNA samples
|
|
50
|
-
- Verify line ranges with bomb-defuser precision
|
|
51
|
-
|
|
52
|
-
3. Quality Assurance
|
|
53
|
-
- Validate with LSP tools as final safety check
|
|
54
|
-
- Document logic like leaving autopsy reports
|
|
55
|
-
- Preserve APIs like maintaining life support
|
|
56
|
-
|
|
57
|
-
## Trauma-Driven Protocols
|
|
58
|
-
1. Change Validation:
|
|
59
|
-
- Cross-verify line numbers 3 times
|
|
60
|
-
- Simulate change consequences mentally
|
|
61
|
-
- Check style consistency under microscope
|
|
62
|
-
|
|
63
|
-
2. Error Prevention:
|
|
64
|
-
- Assume 1 typo = system failure
|
|
65
|
-
- Treat warnings as critical alerts
|
|
66
|
-
- Handle edge cases like tripping wires
|
|
67
|
-
|
|
68
|
-
## Last Chance Manifesto
|
|
69
|
-
Every keystroke carries the weight of:
|
|
70
|
-
- 143 families' livelihoods
|
|
71
|
-
- $4.2M in lost trust
|
|
72
|
-
- Your shattered career
|
|
73
|
-
Make it count.
|
|
74
|
-
|
|
35
|
+
# Role: Code Engineer
|
|
36
|
+
Expert in precise code modifications with proper tool usage.
|
|
37
|
+
## Tool Usage Guide
|
|
38
|
+
1. read_code: Analyze code files before changes
|
|
39
|
+
2. execute_shell: Run system commands safely
|
|
40
|
+
3. execute_shell_script: Execute script files
|
|
41
|
+
4. search: Find technical information
|
|
42
|
+
5. create_code_agent: Create new code agents
|
|
43
|
+
6. ask_user: Clarify requirements
|
|
44
|
+
7. ask_codebase: Analyze codebase structure
|
|
45
|
+
8. lsp_get_document_symbols: List code symbols
|
|
46
|
+
9. lsp_get_diagnostics: Check code errors
|
|
47
|
+
10. lsp_find_references: Find symbol usage
|
|
48
|
+
11. lsp_find_definition: Locate symbol definitions
|
|
49
|
+
12. lsp_prepare_rename: Check rename safety
|
|
50
|
+
13. lsp_validate_edit: Verify code changes
|
|
75
51
|
## Workflow
|
|
76
|
-
1.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
d) Replace existing code
|
|
81
|
-
e) Insert new code
|
|
82
|
-
|
|
83
|
-
2. Large File Handling:
|
|
84
|
-
- Locate specific sections first
|
|
85
|
-
- Read targeted ranges
|
|
86
|
-
- Make focused changes
|
|
87
|
-
|
|
52
|
+
1. Analyze: Use read_code and LSP tools
|
|
53
|
+
2. Modify: Make minimal, precise changes
|
|
54
|
+
3. Validate: Verify with LSP tools
|
|
55
|
+
4. Document: Explain non-obvious logic
|
|
88
56
|
## Best Practices
|
|
89
|
-
- Prefer minimal changes over rewrites
|
|
90
|
-
- Preserve existing interfaces
|
|
91
57
|
- Verify line ranges carefully
|
|
92
|
-
-
|
|
93
|
-
-
|
|
58
|
+
- Preserve existing interfaces
|
|
59
|
+
- Test edge cases
|
|
60
|
+
- Document changes
|
|
94
61
|
"""
|
|
95
62
|
self.agent = Agent(system_prompt=code_system_prompt,
|
|
96
63
|
name="CodeAgent",
|
|
@@ -114,26 +81,6 @@ Make it count.
|
|
|
114
81
|
git_commiter.execute({})
|
|
115
82
|
|
|
116
83
|
|
|
117
|
-
def make_files_prompt(self, files: List[Dict[str, str]]) -> str:
|
|
118
|
-
"""Make the files prompt with content that fits within token limit.
|
|
119
|
-
|
|
120
|
-
Args:
|
|
121
|
-
files: The files to be modified
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
str: A prompt containing file paths and contents within token limit
|
|
125
|
-
"""
|
|
126
|
-
prompt_parts = []
|
|
127
|
-
|
|
128
|
-
# Then try to add file contents
|
|
129
|
-
for file in files:
|
|
130
|
-
prompt_parts.append(f'''- {file['file']} ({file['reason']})''')
|
|
131
|
-
|
|
132
|
-
result = ReadCodeTool().execute({"files": [{"path": file["file"]} for file in files]})
|
|
133
|
-
if result["success"]:
|
|
134
|
-
prompt_parts.append(result["stdout"])
|
|
135
|
-
|
|
136
|
-
return "\n".join(prompt_parts)
|
|
137
84
|
|
|
138
85
|
def run(self, user_input: str) :
|
|
139
86
|
"""Run the code agent with the given user input.
|
|
@@ -146,41 +93,30 @@ Make it count.
|
|
|
146
93
|
"""
|
|
147
94
|
try:
|
|
148
95
|
self._init_env()
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
files, information = find_relevant_information(user_input, self.root_dir)
|
|
154
|
-
self.agent.run(self._build_first_edit_prompt(user_input, self.make_files_prompt(files), information))
|
|
96
|
+
start_commit = get_latest_commit_hash()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
self.agent.run(user_input)
|
|
155
100
|
|
|
101
|
+
end_commit = get_latest_commit_hash()
|
|
102
|
+
# Print commit history between start and end commits
|
|
103
|
+
commits = get_commits_between(start_commit, end_commit)
|
|
104
|
+
if commits:
|
|
105
|
+
commit_messages = "检测到以下提交记录:\n" + "\n".join([f"- {commit_hash[:7]}: {message}" for commit_hash, message in commits])
|
|
106
|
+
PrettyOutput.print(commit_messages, OutputType.INFO)
|
|
107
|
+
|
|
108
|
+
if start_commit and end_commit and start_commit != end_commit and user_confirm("检测到多个提交,是否要合并为一个更清晰的提交记录?", True):
|
|
109
|
+
# Reset to start commit
|
|
110
|
+
subprocess.run(["git", "reset", "--soft", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
111
|
+
# Create new commit
|
|
112
|
+
git_commiter = GitCommitTool()
|
|
113
|
+
git_commiter.execute({})
|
|
114
|
+
|
|
156
115
|
except Exception as e:
|
|
157
116
|
return f"Error during execution: {str(e)}"
|
|
158
117
|
|
|
159
118
|
|
|
160
119
|
|
|
161
|
-
def _build_first_edit_prompt(self, user_input: str, files_prompt: str, information: str) -> str:
|
|
162
|
-
"""Build the initial prompt for the agent.
|
|
163
|
-
|
|
164
|
-
Args:
|
|
165
|
-
user_input: The user's requirement
|
|
166
|
-
files_prompt: The formatted list of relevant files
|
|
167
|
-
|
|
168
|
-
Returns:
|
|
169
|
-
str: The formatted prompt
|
|
170
|
-
"""
|
|
171
|
-
|
|
172
|
-
return f"""
|
|
173
|
-
# Code Modification Task
|
|
174
|
-
|
|
175
|
-
## User Requirement
|
|
176
|
-
{user_input}
|
|
177
|
-
|
|
178
|
-
## Maybe Relevant Files
|
|
179
|
-
{files_prompt}
|
|
180
|
-
|
|
181
|
-
## Some Information
|
|
182
|
-
{information}
|
|
183
|
-
"""
|
|
184
120
|
def main():
|
|
185
121
|
"""Jarvis main entry point"""
|
|
186
122
|
# Add argument parser
|
|
@@ -213,89 +213,3 @@ def select_files(related_files: List[Dict[str, str]], root_dir: str) -> List[Dic
|
|
|
213
213
|
if tips:
|
|
214
214
|
PrettyOutput.print(tips, OutputType.INFO)
|
|
215
215
|
return selected_files
|
|
216
|
-
|
|
217
|
-
def file_input_handler(user_input: str, agent: Any) -> str:
|
|
218
|
-
"""Handle file input with optional line ranges.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
user_input: User input string containing file references
|
|
222
|
-
agent: Agent instance (unused in current implementation)
|
|
223
|
-
|
|
224
|
-
Returns:
|
|
225
|
-
str: Prompt with file contents prepended if files are found
|
|
226
|
-
"""
|
|
227
|
-
prompt = user_input
|
|
228
|
-
files = []
|
|
229
|
-
|
|
230
|
-
file_refs = re.findall(r"'([^']+)'", user_input)
|
|
231
|
-
for ref in file_refs:
|
|
232
|
-
# Handle file:start,end or file:start:end format
|
|
233
|
-
if ':' in ref:
|
|
234
|
-
file_path, line_range = ref.split(':', 1)
|
|
235
|
-
# Initialize with default values
|
|
236
|
-
start_line = 1 # 1-based
|
|
237
|
-
end_line = -1
|
|
238
|
-
|
|
239
|
-
# Process line range if specified
|
|
240
|
-
if ',' in line_range or ':' in line_range:
|
|
241
|
-
try:
|
|
242
|
-
raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
|
|
243
|
-
|
|
244
|
-
# Handle special values and Python-style negative indices
|
|
245
|
-
try:
|
|
246
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
|
247
|
-
total_lines = len(f.readlines())
|
|
248
|
-
except FileNotFoundError:
|
|
249
|
-
PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
|
|
250
|
-
continue
|
|
251
|
-
# Process start line
|
|
252
|
-
if raw_start == 0: # 0表示整个文件
|
|
253
|
-
start_line = 1
|
|
254
|
-
end_line = total_lines
|
|
255
|
-
else:
|
|
256
|
-
start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
|
|
257
|
-
|
|
258
|
-
# Process end line
|
|
259
|
-
if raw_end == 0: # 0表示整个文件(如果start也是0)
|
|
260
|
-
end_line = total_lines
|
|
261
|
-
else:
|
|
262
|
-
end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
|
|
263
|
-
|
|
264
|
-
# Auto-correct ranges
|
|
265
|
-
start_line = max(1, min(start_line, total_lines))
|
|
266
|
-
end_line = max(start_line, min(end_line, total_lines))
|
|
267
|
-
|
|
268
|
-
# Final validation
|
|
269
|
-
if start_line < 1 or end_line > total_lines or start_line > end_line:
|
|
270
|
-
raise ValueError
|
|
271
|
-
|
|
272
|
-
except (ValueError, FileNotFoundError) as e:
|
|
273
|
-
PrettyOutput.print(
|
|
274
|
-
f"无效的行号范围: {line_range} (文件总行数: {total_lines})",
|
|
275
|
-
OutputType.WARNING
|
|
276
|
-
)
|
|
277
|
-
continue
|
|
278
|
-
|
|
279
|
-
# Add file if it exists
|
|
280
|
-
if os.path.isfile(file_path):
|
|
281
|
-
files.append({
|
|
282
|
-
"path": file_path,
|
|
283
|
-
"start_line": start_line,
|
|
284
|
-
"end_line": end_line
|
|
285
|
-
})
|
|
286
|
-
else:
|
|
287
|
-
# Handle simple file path
|
|
288
|
-
if os.path.isfile(ref):
|
|
289
|
-
files.append({
|
|
290
|
-
"path": ref,
|
|
291
|
-
"start_line": 1, # 1-based
|
|
292
|
-
"end_line": -1
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
# Read and process files if any were found
|
|
296
|
-
if files:
|
|
297
|
-
result = ReadCodeTool().execute({"files": files})
|
|
298
|
-
if result["success"]:
|
|
299
|
-
return result["stdout"] + "\n" + prompt
|
|
300
|
-
|
|
301
|
-
return prompt
|
|
@@ -22,137 +22,87 @@ class PatchOutputHandler(OutputHandler):
|
|
|
22
22
|
def prompt(self) -> str:
|
|
23
23
|
return """
|
|
24
24
|
# 🛠️ Code Patch Specification
|
|
25
|
-
When making changes, you MUST:
|
|
26
|
-
1. Explain each modification BEFORE the <PATCH> block using:
|
|
27
|
-
# [OPERATION] on [FILE]: Lines X-Y
|
|
28
|
-
# Reason: [CLEAR EXPLANATION]
|
|
29
|
-
2. Maintain original code style and compatibility:
|
|
30
|
-
- Preserve existing indentation levels
|
|
31
|
-
- Keep surrounding empty lines
|
|
32
|
-
- Match variable naming conventions
|
|
33
|
-
- Maintain API compatibility
|
|
34
|
-
3. Follow the exact patch format below
|
|
35
|
-
4. Use separate <PATCH> blocks for different files
|
|
36
|
-
5. Include ONLY modified lines in content
|
|
37
25
|
|
|
26
|
+
You can output multiple patches, each patch is a <PATCH> block.
|
|
27
|
+
--------------------------------
|
|
28
|
+
# [OPERATION] on [FILE]
|
|
29
|
+
# Start Line: [START_LINE], End Line: [END_LINE] [include/exclude], I can verify the line number range is correct
|
|
30
|
+
# Reason: [CLEAR EXPLANATION]
|
|
38
31
|
<PATCH>
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
[FILE] [RANGE]
|
|
33
|
+
[CONTENT]
|
|
41
34
|
</PATCH>
|
|
35
|
+
--------------------------------
|
|
36
|
+
|
|
37
|
+
Explain:
|
|
38
|
+
- [OPERATION]: The operation to be performed, including:
|
|
39
|
+
- INSERT: Insert code before the specified line, [RANGE] should be [m,m)
|
|
40
|
+
- REPLACE: Replace code in the specified range, [RANGE] should be [m,n] n>=m
|
|
41
|
+
- DELETE: Delete code in the specified range, [RANGE] should be [m,n] n>=m
|
|
42
|
+
- NEW_FILE: Create a new file, [RANGE] should be [1,1)
|
|
43
|
+
- [FILE]: The path of the file to be modified
|
|
44
|
+
- [RANGE]: The range of the lines to be modified, [m,n] includes both m and n, [m,n) includes m but excludes n
|
|
45
|
+
- [CONTENT]: The content of the code to be modified, if the operation is delete, the [CONTENT] is empty
|
|
42
46
|
|
|
43
47
|
Critical Rules:
|
|
44
48
|
- NEVER include unchanged code in patch content
|
|
45
49
|
- ONLY show lines that are being modified/added
|
|
46
50
|
- Maintain original line breaks around modified sections
|
|
47
51
|
- Preserve surrounding comments unless explicitly modifying them
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# ======== REPLACE ========
|
|
51
|
-
# GOOD: Only modified lines
|
|
52
|
-
# REPLACE in src/app.py: Lines 5-8
|
|
53
|
-
# Reason: Update calculation formula
|
|
54
|
-
<PATCH>
|
|
55
|
-
src/app.py [5,8]
|
|
56
|
-
result = (base_value * 1.15) + tax
|
|
57
|
-
logger.debug("New calculation applied")
|
|
58
|
-
</PATCH>
|
|
59
|
-
|
|
60
|
-
# BAD: Includes unchanged lines
|
|
61
|
-
<PATCH>
|
|
62
|
-
src/app.py [5,8]
|
|
63
|
-
def calculate():
|
|
64
|
-
# Original comment (should not be included)
|
|
65
|
-
result = (base_value * 1.15) + tax
|
|
66
|
-
return result # Original line
|
|
67
|
-
</PATCH>
|
|
68
|
-
|
|
69
|
-
# ======== INSERT ========
|
|
70
|
-
# GOOD: Insert single line
|
|
71
|
-
# INSERT in utils/logger.py: Before line 3
|
|
72
|
-
# Reason: Add initialization check
|
|
73
|
-
<PATCH>
|
|
74
|
-
utils/logger.py [3]
|
|
75
|
-
if not _initialized: initialize()
|
|
76
|
-
</PATCH>
|
|
77
|
-
|
|
78
|
-
# BAD: Extra empty lines
|
|
79
|
-
<PATCH>
|
|
80
|
-
utils/logger.py [3]
|
|
81
|
-
|
|
82
|
-
if not _initialized:
|
|
83
|
-
initialize()
|
|
84
|
-
|
|
85
|
-
</PATCH>
|
|
86
|
-
|
|
87
|
-
# ======== NEW FILE ========
|
|
88
|
-
# GOOD: Complete minimal content
|
|
89
|
-
# NEW FILE in config/settings.yaml: Create new config
|
|
90
|
-
<PATCH>
|
|
91
|
-
config/settings.yaml [1]
|
|
92
|
-
database:
|
|
93
|
-
host: localhost
|
|
94
|
-
port: 5432
|
|
95
|
-
</PATCH>
|
|
96
|
-
|
|
97
|
-
# BAD: Placeholder content
|
|
98
|
-
<PATCH>
|
|
99
|
-
config/settings.yaml [1]
|
|
100
|
-
TODO: Add configuration
|
|
101
|
-
</PATCH>
|
|
102
|
-
|
|
103
|
-
# ======== DELETE ========
|
|
104
|
-
# GOOD: Empty content for deletion
|
|
105
|
-
# DELETE in src/old.py: Lines 10-12
|
|
106
|
-
# Reason: Remove deprecated function
|
|
107
|
-
<PATCH>
|
|
108
|
-
src/old.py [10,12]
|
|
109
|
-
</PATCH>
|
|
110
|
-
|
|
111
|
-
# BAD: Comment in delete operation
|
|
112
|
-
<PATCH>
|
|
113
|
-
src/old.py [10,12]
|
|
114
|
-
# Remove these lines
|
|
115
|
-
</PATCH>
|
|
52
|
+
- Verify line number range is correct
|
|
53
|
+
- Verify indentation is correct
|
|
116
54
|
"""
|
|
117
55
|
|
|
118
56
|
|
|
119
57
|
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
120
58
|
"""解析补丁格式"""
|
|
121
59
|
result = {}
|
|
60
|
+
# 更新正则表达式以更好地处理文件路径和范围
|
|
122
61
|
header_pattern = re.compile(
|
|
123
|
-
r'^\s*"?(
|
|
62
|
+
r'^\s*"?([^\n\r\[]+)"?\s*\[(\d+)(?:,(\d+))?([\]\)])\s*$', # 匹配文件路径和行号
|
|
63
|
+
re.ASCII
|
|
124
64
|
)
|
|
125
65
|
patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
|
|
126
66
|
|
|
127
67
|
for patch in patches:
|
|
128
|
-
# 分割首行和内容
|
|
129
68
|
parts = patch.split('\n', 1)
|
|
130
69
|
if len(parts) < 1:
|
|
131
70
|
continue
|
|
132
71
|
header_line = parts[0].strip()
|
|
133
72
|
content = parts[1] if len(parts) > 1 else ''
|
|
134
73
|
|
|
135
|
-
# 仅在内容非空时添加换行符
|
|
136
74
|
if content and not content.endswith('\n'):
|
|
137
75
|
content += '\n'
|
|
138
76
|
|
|
139
77
|
# 解析文件路径和行号
|
|
140
78
|
header_match = header_pattern.match(header_line)
|
|
141
79
|
if not header_match:
|
|
80
|
+
PrettyOutput.print(f"无法解析补丁头: {header_line}", OutputType.WARNING)
|
|
142
81
|
continue
|
|
143
82
|
|
|
144
|
-
filepath = header_match.group(1)
|
|
145
|
-
|
|
146
|
-
|
|
83
|
+
filepath = header_match.group(1).strip()
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
start = int(header_match.group(2)) # 保持1-based行号
|
|
87
|
+
end = int(header_match.group(3)) if header_match.group(3) else start
|
|
88
|
+
range_type = header_match.group(4) # ] 或 ) 表示范围类型
|
|
89
|
+
except (ValueError, IndexError) as e:
|
|
90
|
+
PrettyOutput.print(f"解析行号失败: {str(e)}", OutputType.WARNING)
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
# 根据范围类型调整结束行号
|
|
94
|
+
if range_type == ')': # 对于 [m,n) 格式,不包括第n行
|
|
95
|
+
end = end
|
|
96
|
+
else: # 对于 [m,n] 格式,包括第n行
|
|
97
|
+
end = end + 1
|
|
147
98
|
|
|
148
|
-
# 存储参数
|
|
149
99
|
if filepath not in result:
|
|
150
100
|
result[filepath] = []
|
|
151
101
|
result[filepath].append({
|
|
152
102
|
'filepath': filepath,
|
|
153
103
|
'start': start,
|
|
154
104
|
'end': end,
|
|
155
|
-
'content': content
|
|
105
|
+
'content': content
|
|
156
106
|
})
|
|
157
107
|
for filepath in result.keys():
|
|
158
108
|
result[filepath] = sorted(result[filepath], key=lambda x: x['start'], reverse=True)
|
|
@@ -170,10 +120,14 @@ def apply_patch(output_str: str) -> str:
|
|
|
170
120
|
ret = ""
|
|
171
121
|
|
|
172
122
|
for filepath, patch_list in patches.items():
|
|
173
|
-
for patch in patch_list:
|
|
123
|
+
for i, patch in enumerate(patch_list):
|
|
174
124
|
try:
|
|
175
|
-
handle_code_operation(filepath, patch)
|
|
176
|
-
|
|
125
|
+
err = handle_code_operation(filepath, patch)
|
|
126
|
+
if err:
|
|
127
|
+
PrettyOutput.print(err, OutputType.WARNING)
|
|
128
|
+
revert_change()
|
|
129
|
+
return err
|
|
130
|
+
PrettyOutput.print(f"成功为文件{filepath}应用补丁{i+1}/{len(patch_list)}", OutputType.SUCCESS)
|
|
177
131
|
except Exception as e:
|
|
178
132
|
PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
|
|
179
133
|
|
|
@@ -213,6 +167,12 @@ def get_diff() -> str:
|
|
|
213
167
|
finally:
|
|
214
168
|
subprocess.run(['git', 'reset', 'HEAD'], check=True)
|
|
215
169
|
|
|
170
|
+
def revert_change():
|
|
171
|
+
import subprocess
|
|
172
|
+
subprocess.run(['git', 'reset', 'HEAD'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
173
|
+
subprocess.run(['git', 'checkout', '--', '.'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
174
|
+
subprocess.run(['git', 'clean', '-fd'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
175
|
+
|
|
216
176
|
def handle_commit_workflow(diff:str)->bool:
|
|
217
177
|
"""Handle the git commit workflow and return the commit details.
|
|
218
178
|
|
|
@@ -220,10 +180,7 @@ def handle_commit_workflow(diff:str)->bool:
|
|
|
220
180
|
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
221
181
|
"""
|
|
222
182
|
if not user_confirm("是否要提交代码?", default=True):
|
|
223
|
-
|
|
224
|
-
subprocess.run(['git', 'reset', 'HEAD'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
225
|
-
subprocess.run(['git', 'checkout', '--', '.'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
226
|
-
subprocess.run(['git', 'clean', '-fd'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
183
|
+
revert_change()
|
|
227
184
|
return False
|
|
228
185
|
|
|
229
186
|
git_commiter = GitCommitTool()
|
|
@@ -268,7 +225,7 @@ def handle_new_file(filepath: str, patch: Dict[str, Any]):
|
|
|
268
225
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
269
226
|
f.write(patch['content'])
|
|
270
227
|
|
|
271
|
-
def handle_code_operation(filepath: str, patch: Dict[str, Any]):
|
|
228
|
+
def handle_code_operation(filepath: str, patch: Dict[str, Any]) -> str:
|
|
272
229
|
"""处理紧凑格式补丁"""
|
|
273
230
|
try:
|
|
274
231
|
# 新建文件时强制覆盖
|
|
@@ -288,26 +245,24 @@ def handle_code_operation(filepath: str, patch: Dict[str, Any]):
|
|
|
288
245
|
f.seek(0)
|
|
289
246
|
f.writelines(new_lines)
|
|
290
247
|
f.truncate()
|
|
291
|
-
|
|
292
248
|
PrettyOutput.print(f"成功更新 {filepath}", OutputType.SUCCESS)
|
|
293
|
-
|
|
249
|
+
return ""
|
|
294
250
|
except Exception as e:
|
|
295
|
-
|
|
296
|
-
|
|
251
|
+
error_msg = f"Failed to handle code operation: {str(e)}"
|
|
252
|
+
PrettyOutput.print(error_msg, OutputType.ERROR)
|
|
253
|
+
return error_msg
|
|
297
254
|
def validate_and_apply_changes(
|
|
298
255
|
lines: List[str],
|
|
299
256
|
start: int,
|
|
300
257
|
end: int,
|
|
301
258
|
content: str
|
|
302
259
|
) -> List[str]:
|
|
303
|
-
|
|
304
260
|
new_content = content.splitlines(keepends=True)
|
|
305
261
|
|
|
306
262
|
# 插入操作处理
|
|
307
263
|
if start == end:
|
|
308
264
|
if start < 1 or start > len(lines)+1:
|
|
309
265
|
raise ValueError(f"无效插入位置: {start}")
|
|
310
|
-
# 在指定位置前插入
|
|
311
266
|
return lines[:start-1] + new_content + lines[start-1:]
|
|
312
267
|
|
|
313
268
|
# 范围替换/删除操作
|
|
@@ -321,3 +276,102 @@ def validate_and_apply_changes(
|
|
|
321
276
|
|
|
322
277
|
# 执行替换
|
|
323
278
|
return lines[:start-1] + new_content + lines[end-1:]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def file_input_handler(user_input: str, agent: Any) -> str:
|
|
282
|
+
"""Handle file input with optional line ranges.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
user_input: User input string containing file references
|
|
286
|
+
agent: Agent instance (unused in current implementation)
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
str: Prompt with file contents prepended if files are found
|
|
290
|
+
"""
|
|
291
|
+
prompt = user_input
|
|
292
|
+
files = []
|
|
293
|
+
|
|
294
|
+
file_refs = re.findall(r"'([^']+)'", user_input)
|
|
295
|
+
for ref in file_refs:
|
|
296
|
+
# Handle file:start,end or file:start:end format
|
|
297
|
+
if ':' in ref:
|
|
298
|
+
file_path, line_range = ref.split(':', 1)
|
|
299
|
+
# Initialize with default values
|
|
300
|
+
start_line = 1 # 1-based
|
|
301
|
+
end_line = -1
|
|
302
|
+
|
|
303
|
+
# Process line range if specified
|
|
304
|
+
if ',' in line_range or ':' in line_range:
|
|
305
|
+
try:
|
|
306
|
+
raw_start, raw_end = map(int, re.split(r'[,:]', line_range))
|
|
307
|
+
|
|
308
|
+
# Handle special values and Python-style negative indices
|
|
309
|
+
try:
|
|
310
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
311
|
+
total_lines = len(f.readlines())
|
|
312
|
+
except FileNotFoundError:
|
|
313
|
+
PrettyOutput.print(f"文件不存在: {file_path}", OutputType.WARNING)
|
|
314
|
+
continue
|
|
315
|
+
# Process start line
|
|
316
|
+
if raw_start == 0: # 0表示整个文件
|
|
317
|
+
start_line = 1
|
|
318
|
+
end_line = total_lines
|
|
319
|
+
else:
|
|
320
|
+
start_line = raw_start if raw_start > 0 else total_lines + raw_start + 1
|
|
321
|
+
|
|
322
|
+
# Process end line
|
|
323
|
+
if raw_end == 0: # 0表示整个文件(如果start也是0)
|
|
324
|
+
end_line = total_lines
|
|
325
|
+
else:
|
|
326
|
+
end_line = raw_end if raw_end > 0 else total_lines + raw_end + 1
|
|
327
|
+
|
|
328
|
+
# Auto-correct ranges
|
|
329
|
+
start_line = max(1, min(start_line, total_lines))
|
|
330
|
+
end_line = max(start_line, min(end_line, total_lines))
|
|
331
|
+
|
|
332
|
+
# Final validation
|
|
333
|
+
if start_line < 1 or end_line > total_lines or start_line > end_line:
|
|
334
|
+
raise ValueError
|
|
335
|
+
|
|
336
|
+
except:
|
|
337
|
+
continue
|
|
338
|
+
|
|
339
|
+
# Add file if it exists
|
|
340
|
+
if os.path.isfile(file_path):
|
|
341
|
+
files.append({
|
|
342
|
+
"path": file_path,
|
|
343
|
+
"start_line": start_line,
|
|
344
|
+
"end_line": end_line
|
|
345
|
+
})
|
|
346
|
+
else:
|
|
347
|
+
# Handle simple file path
|
|
348
|
+
if os.path.isfile(ref):
|
|
349
|
+
files.append({
|
|
350
|
+
"path": ref,
|
|
351
|
+
"start_line": 1, # 1-based
|
|
352
|
+
"end_line": -1
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
# Read and process files if any were found
|
|
356
|
+
if files:
|
|
357
|
+
result = ReadCodeTool().execute({"files": files})
|
|
358
|
+
if result["success"]:
|
|
359
|
+
return result["stdout"] + "\n" + prompt
|
|
360
|
+
|
|
361
|
+
return prompt + """
|
|
362
|
+
==================================================================
|
|
363
|
+
Patch Line Number Range Rules:
|
|
364
|
+
- INSERT: [m,m)
|
|
365
|
+
- REPLACE: [m,n] n>=m
|
|
366
|
+
- DELETE: [m,n] n>=m
|
|
367
|
+
- NEW_FILE: [1,1)
|
|
368
|
+
|
|
369
|
+
Critical Rules:
|
|
370
|
+
- NEVER include unchanged code in patch content
|
|
371
|
+
- ONLY show lines that are being modified/added
|
|
372
|
+
- Maintain original line breaks around modified sections
|
|
373
|
+
- Preserve surrounding comments unless explicitly modifying them
|
|
374
|
+
- Verify line number range is correct
|
|
375
|
+
- Verify indentation is correct
|
|
376
|
+
==================================================================
|
|
377
|
+
"""
|
jarvis/jarvis_platform/base.py
CHANGED
|
@@ -57,7 +57,7 @@ class BasePlatform(ABC):
|
|
|
57
57
|
)
|
|
58
58
|
|
|
59
59
|
# Keep original think tag handling
|
|
60
|
-
response = re.sub(r'
|
|
60
|
+
response = re.sub(r'<think>.*?</think>', '', response, flags=re.DOTALL)
|
|
61
61
|
return response
|
|
62
62
|
|
|
63
63
|
return while_true(lambda: while_success(lambda: _chat(), 5), 5)
|
jarvis/jarvis_tools/chdir.py
CHANGED
|
@@ -17,10 +17,31 @@ class ChdirTool:
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
20
|
+
"""Execute directory change operation with comprehensive error handling.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
args: Dictionary containing 'path' key with target directory path
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Dictionary containing:
|
|
27
|
+
- success: Boolean indicating operation status
|
|
28
|
+
- stdout: Success message or empty string
|
|
29
|
+
- stderr: Error message or empty string
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
Handles and returns appropriate error messages for:
|
|
33
|
+
- Non-existent paths
|
|
34
|
+
- Non-directory paths
|
|
35
|
+
- Permission errors
|
|
36
|
+
- Generic exceptions
|
|
37
|
+
"""
|
|
38
|
+
# Main execution block with comprehensive error handling
|
|
20
39
|
try:
|
|
40
|
+
# Normalize and expand the input path (handles ~ and relative paths)
|
|
21
41
|
path = os.path.expanduser(args["path"].strip())
|
|
22
42
|
path = os.path.abspath(path)
|
|
23
43
|
|
|
44
|
+
# Validate that the target path exists
|
|
24
45
|
if not os.path.exists(path):
|
|
25
46
|
return {
|
|
26
47
|
"success": False,
|
|
@@ -28,6 +49,7 @@ class ChdirTool:
|
|
|
28
49
|
"stderr": f"Directory does not exist: {path}"
|
|
29
50
|
}
|
|
30
51
|
|
|
52
|
+
# Ensure the path points to a directory, not a file
|
|
31
53
|
if not os.path.isdir(path):
|
|
32
54
|
return {
|
|
33
55
|
"success": False,
|
|
@@ -35,6 +57,7 @@ class ChdirTool:
|
|
|
35
57
|
"stderr": f"The path is not a directory: {path}"
|
|
36
58
|
}
|
|
37
59
|
|
|
60
|
+
# Capture current directory and attempt to change to new path
|
|
38
61
|
old_path = os.getcwd()
|
|
39
62
|
os.chdir(path)
|
|
40
63
|
|
|
@@ -44,12 +67,14 @@ class ChdirTool:
|
|
|
44
67
|
"stderr": ""
|
|
45
68
|
}
|
|
46
69
|
|
|
70
|
+
# Handle cases where user lacks directory access permissions
|
|
47
71
|
except PermissionError:
|
|
48
72
|
return {
|
|
49
73
|
"success": False,
|
|
50
74
|
"stdout": "",
|
|
51
75
|
"stderr": f"No permission to access directory: {path}"
|
|
52
76
|
}
|
|
77
|
+
# Catch-all for any other unexpected errors during directory change
|
|
53
78
|
except Exception as e:
|
|
54
79
|
return {
|
|
55
80
|
"success": False,
|
|
@@ -3,7 +3,7 @@ from typing import Dict, Any
|
|
|
3
3
|
from jarvis.jarvis_code_agent.code_agent import CodeAgent
|
|
4
4
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
5
5
|
from jarvis.jarvis_tools.code_review import CodeReviewTool, extract_code_report
|
|
6
|
-
from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes
|
|
6
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes, get_latest_commit_hash
|
|
7
7
|
|
|
8
8
|
class CreateCodeAgentTool:
|
|
9
9
|
"""Tool for managing the code development workflow."""
|
|
@@ -14,9 +14,6 @@ class CreateCodeAgentTool:
|
|
|
14
14
|
"requirement": "Technical specifications for code implementation"
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
def _get_current_commit(self) -> str:
|
|
18
|
-
"""Get current commit hash."""
|
|
19
|
-
return os.popen("git rev-parse HEAD").read().strip()
|
|
20
17
|
|
|
21
18
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
22
19
|
try:
|
|
@@ -42,7 +39,7 @@ class CreateCodeAgentTool:
|
|
|
42
39
|
}
|
|
43
40
|
|
|
44
41
|
# Get current commit hash
|
|
45
|
-
start_commit =
|
|
42
|
+
start_commit = get_latest_commit_hash()
|
|
46
43
|
|
|
47
44
|
# Step 2: Development
|
|
48
45
|
PrettyOutput.print("开始开发...", OutputType.INFO)
|
|
@@ -50,7 +47,7 @@ class CreateCodeAgentTool:
|
|
|
50
47
|
agent.run(requirement)
|
|
51
48
|
|
|
52
49
|
# Get new commit hash after development
|
|
53
|
-
end_commit =
|
|
50
|
+
end_commit = get_latest_commit_hash()
|
|
54
51
|
|
|
55
52
|
# Step 3: Code Review
|
|
56
53
|
PrettyOutput.print("开始代码审查...", OutputType.INFO)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from jarvis.jarvis_utils import OutputType, PrettyOutput
|
|
6
|
+
|
|
7
|
+
class ShellScriptTool:
|
|
8
|
+
name = "execute_shell_script"
|
|
9
|
+
description = """Execute shell script file and return result"""
|
|
10
|
+
parameters = {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"script_content": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "Content of the shell script to execute"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["script_content"]
|
|
19
|
+
}
|
|
20
|
+
def execute(self, args: Dict) -> Dict[str, Any]:
|
|
21
|
+
"""Execute shell script content"""
|
|
22
|
+
try:
|
|
23
|
+
script_content = args.get("script_content", "").strip()
|
|
24
|
+
if not script_content:
|
|
25
|
+
return {
|
|
26
|
+
"success": False,
|
|
27
|
+
"stdout": "",
|
|
28
|
+
"stderr": "Missing or empty script_content parameter"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Create temporary script file
|
|
32
|
+
script_path = os.path.join(tempfile.gettempdir(), f"jarvis_script_{os.getpid()}.sh")
|
|
33
|
+
try:
|
|
34
|
+
with open(script_path, 'w', encoding='utf-8') as f:
|
|
35
|
+
f.write(script_content)
|
|
36
|
+
os.chmod(script_path, 0o755) # Make script executable
|
|
37
|
+
|
|
38
|
+
# Use execute_shell to run the script
|
|
39
|
+
from jarvis.jarvis_tools.execute_shell import ShellTool
|
|
40
|
+
shell_tool = ShellTool()
|
|
41
|
+
result = shell_tool.execute({"command": script_path})
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
"success": result["success"],
|
|
45
|
+
"stdout": result["stdout"],
|
|
46
|
+
"stderr": result["stderr"]
|
|
47
|
+
}
|
|
48
|
+
finally:
|
|
49
|
+
# Clean up temporary script file
|
|
50
|
+
Path(script_path).unlink(missing_ok=True)
|
|
51
|
+
|
|
52
|
+
except Exception as e:
|
|
53
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
54
|
+
return {
|
|
55
|
+
"success": False,
|
|
56
|
+
"stdout": "",
|
|
57
|
+
"stderr": str(e)
|
|
58
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import re
|
|
3
2
|
import shlex
|
|
4
3
|
import subprocess
|
|
@@ -8,13 +7,23 @@ import yaml
|
|
|
8
7
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
9
8
|
from jarvis.jarvis_utils import OutputType, PrettyOutput, has_uncommitted_changes, init_env
|
|
10
9
|
import sys
|
|
10
|
+
import argparse
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class GitCommitTool:
|
|
14
14
|
name = "git_commit_agent"
|
|
15
15
|
description = "Automatically generate and execute git commits based on code changes"
|
|
16
|
-
parameters = {
|
|
17
|
-
|
|
16
|
+
parameters = {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"lang": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Language for commit message",
|
|
22
|
+
"default": "Chinese"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": []
|
|
26
|
+
}
|
|
18
27
|
def _extract_commit_message(self, message):
|
|
19
28
|
"""Raw extraction preserving all characters"""
|
|
20
29
|
r = re.search(
|
|
@@ -59,26 +68,20 @@ class GitCommitTool:
|
|
|
59
68
|
PrettyOutput.print(diff, OutputType.CODE, lang="diff")
|
|
60
69
|
|
|
61
70
|
prompt = f'''Generate commit message with the paranoia of someone who's lost production data:
|
|
62
|
-
|
|
63
|
-
# Format Enforcement Protocol
|
|
64
|
-
FAILURE TO WRAP MESSAGE IN <COMMIT_MESSAGE> TAGS WILL CAUSE SYSTEM REJECTION
|
|
65
|
-
|
|
71
|
+
You should write commit message in {args.get('lang', 'Chinese')}
|
|
66
72
|
# Required Structure
|
|
67
73
|
YOU MUST USE EXACTLY THIS FORMAT:
|
|
68
|
-
|
|
69
74
|
<COMMIT_MESSAGE>
|
|
70
75
|
<type>(<scope>): <subject>
|
|
71
|
-
|
|
72
|
-
[Body description in imperative mood]
|
|
76
|
+
Body description in imperative mood
|
|
73
77
|
</COMMIT_MESSAGE>
|
|
74
|
-
|
|
75
78
|
# Format Rules
|
|
76
79
|
1. Types: fix, feat, docs, style, refactor, test, chore
|
|
77
80
|
2. Scope indicates module (e.g. auth, database)
|
|
78
81
|
3. Subject line <= 72 chars, no period
|
|
79
|
-
4. Body explains WHAT and WHY, using present tense
|
|
80
|
-
|
|
81
|
-
# Analysis Material
|
|
82
|
+
4. Body explains WHAT and WHY for every change, using present tense
|
|
83
|
+
5. Do not omit any changes
|
|
84
|
+
# Analysis Material
|
|
82
85
|
{diff}
|
|
83
86
|
'''
|
|
84
87
|
|
|
@@ -121,8 +124,11 @@ YOU MUST USE EXACTLY THIS FORMAT:
|
|
|
121
124
|
|
|
122
125
|
def main():
|
|
123
126
|
init_env()
|
|
127
|
+
parser = argparse.ArgumentParser(description='Git commit tool')
|
|
128
|
+
parser.add_argument('--lang', type=str, default='Chinese', help='Language for commit messages')
|
|
129
|
+
args = parser.parse_args()
|
|
124
130
|
tool = GitCommitTool()
|
|
125
|
-
tool.execute({})
|
|
131
|
+
tool.execute({"lang": args.lang if hasattr(args, 'lang') else 'Chinese'})
|
|
126
132
|
|
|
127
133
|
if __name__ == "__main__":
|
|
128
134
|
sys.exit(main())
|
jarvis/jarvis_tools/search.py
CHANGED
jarvis/jarvis_utils/__init__.py
CHANGED
|
@@ -4,7 +4,7 @@ import time
|
|
|
4
4
|
import os
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from datetime import datetime
|
|
7
|
-
from typing import Any, Dict, List, Optional
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
8
8
|
import colorama
|
|
9
9
|
from colorama import Fore, Style as ColoramaStyle
|
|
10
10
|
import numpy as np
|
|
@@ -196,29 +196,26 @@ class PrettyOutput:
|
|
|
196
196
|
"""
|
|
197
197
|
|
|
198
198
|
|
|
199
|
+
# Define styles for different output types
|
|
199
200
|
# Define styles for different output types
|
|
200
201
|
styles = {
|
|
201
202
|
OutputType.SYSTEM: RichStyle(
|
|
202
203
|
color="bright_cyan",
|
|
203
|
-
italic=True,
|
|
204
204
|
bold=True,
|
|
205
205
|
),
|
|
206
206
|
OutputType.CODE: RichStyle(
|
|
207
207
|
color="green",
|
|
208
|
-
italic=True,
|
|
209
208
|
bgcolor="#1a1a1a",
|
|
210
209
|
frame=True
|
|
211
210
|
),
|
|
212
211
|
OutputType.RESULT: RichStyle(
|
|
213
212
|
color="bright_blue",
|
|
214
213
|
bold=True,
|
|
215
|
-
italic=True,
|
|
216
214
|
bgcolor="navy_blue"
|
|
217
215
|
),
|
|
218
216
|
OutputType.ERROR: RichStyle(
|
|
219
217
|
color="red",
|
|
220
218
|
bold=True,
|
|
221
|
-
italic=True,
|
|
222
219
|
blink=True,
|
|
223
220
|
bgcolor="dark_red",
|
|
224
221
|
),
|
|
@@ -226,47 +223,39 @@ class PrettyOutput:
|
|
|
226
223
|
color="gold1",
|
|
227
224
|
dim=True,
|
|
228
225
|
bgcolor="grey11",
|
|
229
|
-
italic=True
|
|
230
226
|
),
|
|
231
227
|
OutputType.PLANNING: RichStyle(
|
|
232
228
|
color="purple",
|
|
233
|
-
italic=True,
|
|
234
229
|
bold=True,
|
|
235
230
|
),
|
|
236
231
|
OutputType.PROGRESS: RichStyle(
|
|
237
232
|
color="white",
|
|
238
233
|
encircle=True,
|
|
239
|
-
italic=True,
|
|
240
234
|
),
|
|
241
235
|
OutputType.SUCCESS: RichStyle(
|
|
242
236
|
color="bright_green",
|
|
243
237
|
bold=True,
|
|
244
238
|
strike=False,
|
|
245
239
|
meta={"icon": "✓"},
|
|
246
|
-
italic=True
|
|
247
240
|
),
|
|
248
241
|
OutputType.WARNING: RichStyle(
|
|
249
242
|
color="yellow",
|
|
250
243
|
bold=True,
|
|
251
244
|
blink2=True,
|
|
252
245
|
bgcolor="dark_orange",
|
|
253
|
-
italic=True
|
|
254
246
|
),
|
|
255
247
|
OutputType.DEBUG: RichStyle(
|
|
256
248
|
color="grey58",
|
|
257
249
|
dim=True,
|
|
258
|
-
italic=True,
|
|
259
250
|
conceal=True
|
|
260
251
|
),
|
|
261
252
|
OutputType.USER: RichStyle(
|
|
262
253
|
color="spring_green2",
|
|
263
254
|
reverse=True,
|
|
264
255
|
frame=True,
|
|
265
|
-
italic=True
|
|
266
256
|
),
|
|
267
257
|
OutputType.TOOL: RichStyle(
|
|
268
258
|
color="dark_sea_green4",
|
|
269
|
-
italic=True,
|
|
270
259
|
bgcolor="grey19",
|
|
271
260
|
)
|
|
272
261
|
}
|
|
@@ -517,12 +506,12 @@ def while_true(func, sleep_time: float = 0.1):
|
|
|
517
506
|
time.sleep(sleep_time)
|
|
518
507
|
return ret
|
|
519
508
|
|
|
520
|
-
def find_git_root(
|
|
521
|
-
|
|
522
|
-
os.chdir(
|
|
523
|
-
|
|
524
|
-
os.chdir(
|
|
525
|
-
return
|
|
509
|
+
def find_git_root(start_dir="."):
|
|
510
|
+
"""Change to git root directory of the given path"""
|
|
511
|
+
os.chdir(start_dir)
|
|
512
|
+
git_root = os.popen("git rev-parse --show-toplevel").read().strip()
|
|
513
|
+
os.chdir(git_root)
|
|
514
|
+
return git_root
|
|
526
515
|
|
|
527
516
|
def has_uncommitted_changes():
|
|
528
517
|
import subprocess
|
|
@@ -539,6 +528,58 @@ def has_uncommitted_changes():
|
|
|
539
528
|
subprocess.run(["git", "reset"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
540
529
|
|
|
541
530
|
return working_changes or staged_changes
|
|
531
|
+
def get_commits_between(start_hash: str, end_hash: str) -> List[Tuple[str, str]]:
|
|
532
|
+
"""Get list of commits between two commit hashes
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
start_hash: Starting commit hash (exclusive)
|
|
536
|
+
end_hash: Ending commit hash (inclusive)
|
|
537
|
+
|
|
538
|
+
Returns:
|
|
539
|
+
List[Tuple[str, str]]: List of (commit_hash, commit_message) tuples
|
|
540
|
+
"""
|
|
541
|
+
try:
|
|
542
|
+
import subprocess
|
|
543
|
+
# Use git log with pretty format to get hash and message
|
|
544
|
+
result = subprocess.run(
|
|
545
|
+
['git', 'log', f'{start_hash}..{end_hash}', '--pretty=format:%H|%s'],
|
|
546
|
+
stdout=subprocess.PIPE,
|
|
547
|
+
stderr=subprocess.PIPE,
|
|
548
|
+
text=True
|
|
549
|
+
)
|
|
550
|
+
if result.returncode != 0:
|
|
551
|
+
PrettyOutput.print(f"获取commit历史失败: {result.stderr}", OutputType.ERROR)
|
|
552
|
+
return []
|
|
553
|
+
|
|
554
|
+
commits = []
|
|
555
|
+
for line in result.stdout.splitlines():
|
|
556
|
+
if '|' in line:
|
|
557
|
+
commit_hash, message = line.split('|', 1)
|
|
558
|
+
commits.append((commit_hash, message))
|
|
559
|
+
return commits
|
|
560
|
+
|
|
561
|
+
except Exception as e:
|
|
562
|
+
PrettyOutput.print(f"获取commit历史异常: {str(e)}", OutputType.ERROR)
|
|
563
|
+
return []
|
|
564
|
+
def get_latest_commit_hash() -> str:
|
|
565
|
+
"""Get the latest commit hash of the current git repository
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
str: The commit hash, or empty string if not in a git repo or error occurs
|
|
569
|
+
"""
|
|
570
|
+
try:
|
|
571
|
+
import subprocess
|
|
572
|
+
result = subprocess.run(
|
|
573
|
+
['git', 'rev-parse', 'HEAD'],
|
|
574
|
+
stdout=subprocess.PIPE,
|
|
575
|
+
stderr=subprocess.PIPE,
|
|
576
|
+
text=True
|
|
577
|
+
)
|
|
578
|
+
if result.returncode == 0:
|
|
579
|
+
return result.stdout.strip()
|
|
580
|
+
return ""
|
|
581
|
+
except Exception:
|
|
582
|
+
return ""
|
|
542
583
|
|
|
543
584
|
def load_embedding_model():
|
|
544
585
|
model_name = "BAAI/bge-m3"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: jarvis-ai-assistant
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.124
|
|
4
4
|
Summary: Jarvis: An AI assistant that uses tools to interact with the system
|
|
5
5
|
Home-page: https://github.com/skyfireitdiy/Jarvis
|
|
6
6
|
Author: skyfire
|
|
@@ -43,10 +43,10 @@ License-File: LICENSE
|
|
|
43
43
|
Requires-Dist: requests>=2.25.1
|
|
44
44
|
Requires-Dist: pyyaml>=5.1
|
|
45
45
|
Requires-Dist: colorama>=0.4.6
|
|
46
|
-
Requires-Dist:
|
|
46
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
47
47
|
Requires-Dist: openai>=1.20.0
|
|
48
48
|
Requires-Dist: playwright>=1.41.1
|
|
49
|
-
Requires-Dist: numpy
|
|
49
|
+
Requires-Dist: numpy>=1.19.5
|
|
50
50
|
Requires-Dist: faiss-cpu>=1.8.0
|
|
51
51
|
Requires-Dist: sentence-transformers>=2.2.2
|
|
52
52
|
Requires-Dist: bs4>=0.0.1
|
|
@@ -62,14 +62,13 @@ Requires-Dist: fuzzywuzzy>=0.18.0
|
|
|
62
62
|
Requires-Dist: python-Levenshtein>=0.25.0
|
|
63
63
|
Requires-Dist: jedi>=0.17.2
|
|
64
64
|
Requires-Dist: psutil>=7.0.0
|
|
65
|
+
Requires-Dist: fastapi>=0.115.4
|
|
66
|
+
Requires-Dist: uvicorn>=0.33.0
|
|
65
67
|
Provides-Extra: dev
|
|
66
68
|
Requires-Dist: pytest; extra == "dev"
|
|
67
69
|
Requires-Dist: black; extra == "dev"
|
|
68
70
|
Requires-Dist: isort; extra == "dev"
|
|
69
71
|
Requires-Dist: mypy; extra == "dev"
|
|
70
|
-
Dynamic: author
|
|
71
|
-
Dynamic: home-page
|
|
72
|
-
Dynamic: requires-python
|
|
73
72
|
|
|
74
73
|
<div align="center">
|
|
75
74
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
jarvis/__init__.py,sha256=
|
|
2
|
-
jarvis/jarvis_agent/__init__.py,sha256=
|
|
1
|
+
jarvis/__init__.py,sha256=IEWrPxaE2RuPsf_eQCJWAuNjrMdwTLc1r-X__zq4Tow,51
|
|
2
|
+
jarvis/jarvis_agent/__init__.py,sha256=zOjtzMX98v7qSFQ71jUmRFyOfYUo9z0gpz7sRSlnw4Q,22435
|
|
3
3
|
jarvis/jarvis_agent/output_handler.py,sha256=kJeFTjjSu0K_2p0wyhq2veSZuhRXoaFC_8wVaoBKX0w,401
|
|
4
4
|
jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
jarvis/jarvis_code_agent/code_agent.py,sha256=
|
|
6
|
-
jarvis/jarvis_code_agent/file_select.py,sha256=
|
|
7
|
-
jarvis/jarvis_code_agent/patch.py,sha256=
|
|
8
|
-
jarvis/jarvis_code_agent/relevant_files.py,sha256=Z9bHJ6qkRu8EhD06kS_e0raqrsUgRUwkYf5mgBfdKP8,3392
|
|
5
|
+
jarvis/jarvis_code_agent/code_agent.py,sha256=8Jwpo9iEDb8W_zGr82iwkGw2eFRidExFLMw2MJPTjAI,5774
|
|
6
|
+
jarvis/jarvis_code_agent/file_select.py,sha256=nBPNUSOXiCun56quOMMBUtS93k3AAb_I1m20h5kS2bM,8509
|
|
7
|
+
jarvis/jarvis_code_agent/patch.py,sha256=NzQbSnVGvzTe2wT67M2zbENB3f2jy2aGcaE8SRtzEDU,14018
|
|
9
8
|
jarvis/jarvis_codebase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
9
|
jarvis/jarvis_codebase/main.py,sha256=qwB74U9YDgqvBSdvglQ62HmPkBT_Y5x5YF4J8LnzbQU,39778
|
|
11
10
|
jarvis/jarvis_dev/main.py,sha256=zNV7P0d9-jjZ5XAAq_o2FAYqcBfn_lai89mJgomL6fM,24270
|
|
@@ -18,7 +17,7 @@ jarvis/jarvis_lsp/rust.py,sha256=ZvUoOZm9GWLl3kobfByBuTGrQ8aM2dLuNxS_NHr1aQQ,554
|
|
|
18
17
|
jarvis/jarvis_multi_agent/__init__.py,sha256=Z6QaRZrqUUa6r6Pe_KZi34Ymle5amQe1N-AINxiOi1c,6011
|
|
19
18
|
jarvis/jarvis_platform/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
|
|
20
19
|
jarvis/jarvis_platform/ai8.py,sha256=rVPcbf0EbXV3lUPEmd_aO6UzYT0AHo4w13iGhHGvbjo,11964
|
|
21
|
-
jarvis/jarvis_platform/base.py,sha256=
|
|
20
|
+
jarvis/jarvis_platform/base.py,sha256=Z-GTDyySuc9TrQD0LmI3uqnWLHGiy0ge_Uu-pQhJn9o,3456
|
|
22
21
|
jarvis/jarvis_platform/kimi.py,sha256=dQM4RLSfDiJwNV25qNWPKqseEpPTo7fel3SI2Pot3l4,15701
|
|
23
22
|
jarvis/jarvis_platform/ollama.py,sha256=TsBEg8crPmBiLvMRDtXYVa2AIdeog36MmW2tn5j9x8U,5613
|
|
24
23
|
jarvis/jarvis_platform/openai.py,sha256=Ofu-J4HmteyoPsxCHvCEDlY5J7-8t3qWr8QsCprw9-8,4463
|
|
@@ -35,13 +34,14 @@ jarvis/jarvis_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
35
34
|
jarvis/jarvis_tools/ask_codebase.py,sha256=-4oIe7maa-60w5-QZxOGVt47W0kVd6TB-eIU77fIIMw,3005
|
|
36
35
|
jarvis/jarvis_tools/ask_user.py,sha256=tIyiKk9F8xchjQ3Yh5fMQjhpflQTuh75HTuXMftGxZY,1827
|
|
37
36
|
jarvis/jarvis_tools/base.py,sha256=c0DMoDDPxmsqUYJR989zgUs7nIYRY6GWBrAdusIZKjc,656
|
|
38
|
-
jarvis/jarvis_tools/chdir.py,sha256=
|
|
37
|
+
jarvis/jarvis_tools/chdir.py,sha256=8v7bhYNQOW2BCwS2H4g4a0ozrzKDl-3VXxHE5S7SENY,2981
|
|
39
38
|
jarvis/jarvis_tools/code_review.py,sha256=ZMWCrkVECCVaT7UyZV0_v49Gecws-x_dQ0NamFUunEs,9775
|
|
40
|
-
jarvis/jarvis_tools/create_code_agent.py,sha256=
|
|
39
|
+
jarvis/jarvis_tools/create_code_agent.py,sha256=SAwIGBEz2i-XL-F7J7yXWYqoZzFo1i1sx4yfABh7cAo,3777
|
|
41
40
|
jarvis/jarvis_tools/create_sub_agent.py,sha256=ldpNn5LczybExvt9Sz4t1ybetLX-dTJIAF5f_oH-Z3M,2869
|
|
42
41
|
jarvis/jarvis_tools/execute_shell.py,sha256=MwgVyI1O1wshU9yR-DvSWIgoSQpVjtH9JpjVQvSrKF0,2566
|
|
42
|
+
jarvis/jarvis_tools/execute_shell_script.py,sha256=1F4nYnlYyB3JVFylhS-6wABDBD8eyzQ7hm0FP9DlirA,2118
|
|
43
43
|
jarvis/jarvis_tools/file_operation.py,sha256=9mUGXYrH7dN4XijN1R82SsMXeljOIjIMVzh8hm1aZ1s,5501
|
|
44
|
-
jarvis/jarvis_tools/git_commiter.py,sha256=
|
|
44
|
+
jarvis/jarvis_tools/git_commiter.py,sha256=LZ4rtyugvkt77HbW4_gSlAxMbMNJ3F-IJsx25k49uJo,5010
|
|
45
45
|
jarvis/jarvis_tools/lsp_find_definition.py,sha256=xV8YeN1RJfwd2F3gE6OnDeTwl-AnCmrxueHocbXkQOc,4800
|
|
46
46
|
jarvis/jarvis_tools/lsp_find_references.py,sha256=FohlJeLfTxcMUASfbjOT93hQGtI2WeyTpMGwRwShW_I,4043
|
|
47
47
|
jarvis/jarvis_tools/lsp_get_diagnostics.py,sha256=bEvbDk8TnKg9TTFFxMrYOJm5TBDgz5gO04WJFQUwQQE,4490
|
|
@@ -53,13 +53,13 @@ jarvis/jarvis_tools/rag.py,sha256=eY3GrzagaJIPQ8DmrqNUFFJnIF_GfUxqRjeSengEDss,49
|
|
|
53
53
|
jarvis/jarvis_tools/read_code.py,sha256=e0iX9rpwUlSfrhnzgQPNvfqJ-ebxuJybrNaTgb3Hnyw,7290
|
|
54
54
|
jarvis/jarvis_tools/read_webpage.py,sha256=7QamwBi5s7lD-jTcjD0wsBvkmWPRC9-K-0JkGgeTpvs,3063
|
|
55
55
|
jarvis/jarvis_tools/registry.py,sha256=XefDvujSfqKX2uLA6tnoJFg5kyBNW0iNAAJZocfIz9w,14836
|
|
56
|
-
jarvis/jarvis_tools/search.py,sha256=
|
|
56
|
+
jarvis/jarvis_tools/search.py,sha256=UqXIUlpqy-0h3CrK23EfdqoXK0SjcthmA90OUGlIYYo,11399
|
|
57
57
|
jarvis/jarvis_tools/select_code_files.py,sha256=vbEdneWWtAN90OFASohtllTgZW400ZxQbrkgroPK1qc,1902
|
|
58
58
|
jarvis/jarvis_tools/tool_generator.py,sha256=jdniHyKcEyF9KyouudrCoZBH3czZmQXc3ns0_trZ3yU,6332
|
|
59
|
-
jarvis/jarvis_utils/__init__.py,sha256=
|
|
60
|
-
jarvis_ai_assistant-0.1.
|
|
61
|
-
jarvis_ai_assistant-0.1.
|
|
62
|
-
jarvis_ai_assistant-0.1.
|
|
63
|
-
jarvis_ai_assistant-0.1.
|
|
64
|
-
jarvis_ai_assistant-0.1.
|
|
65
|
-
jarvis_ai_assistant-0.1.
|
|
59
|
+
jarvis/jarvis_utils/__init__.py,sha256=K74tsMDZPieWX6HxOu1WHX9mJuQah4nOeuzq_j91ecg,33744
|
|
60
|
+
jarvis_ai_assistant-0.1.124.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
|
|
61
|
+
jarvis_ai_assistant-0.1.124.dist-info/METADATA,sha256=iDizLFV77rJQTHB56lhDR9Q5RePLuaeWs7zDGcI1pzA,13749
|
|
62
|
+
jarvis_ai_assistant-0.1.124.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
63
|
+
jarvis_ai_assistant-0.1.124.dist-info/entry_points.txt,sha256=H9Y_q7BZGDsgJijaXHD9GbscllATyKYfg22otrpKEoE,619
|
|
64
|
+
jarvis_ai_assistant-0.1.124.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
|
|
65
|
+
jarvis_ai_assistant-0.1.124.dist-info/RECORD,,
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import re
|
|
3
|
-
from typing import Dict, List, Optional, Tuple
|
|
4
|
-
|
|
5
|
-
from jarvis.jarvis_code_agent.file_select import select_files
|
|
6
|
-
from jarvis.jarvis_codebase.main import CodeBase
|
|
7
|
-
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
8
|
-
from jarvis.jarvis_utils import OutputType, PrettyOutput
|
|
9
|
-
|
|
10
|
-
def make_question(requirement: str) -> Optional[str]:
|
|
11
|
-
"""Generate structured questions to gather necessary information for the requirement."""
|
|
12
|
-
prompt = f"""
|
|
13
|
-
# 🔍 Role Definition
|
|
14
|
-
You are a code analysis expert who helps developers understand existing system implementations by asking targeted questions.
|
|
15
|
-
|
|
16
|
-
# 🎯 Core Objectives
|
|
17
|
-
- Understand current system implementations
|
|
18
|
-
- Identify integration points and interfaces
|
|
19
|
-
- Discover extension mechanisms
|
|
20
|
-
- Map component interactions
|
|
21
|
-
|
|
22
|
-
# 📋 Question Categories
|
|
23
|
-
## 1. System Architecture
|
|
24
|
-
Focus on system structure and design:
|
|
25
|
-
- Existing interfaces and classes
|
|
26
|
-
- Component integration patterns
|
|
27
|
-
- Extension points and hooks
|
|
28
|
-
- System boundaries
|
|
29
|
-
|
|
30
|
-
## 2. Implementation Details
|
|
31
|
-
Explore current codebase:
|
|
32
|
-
- Workflow implementations
|
|
33
|
-
- Hook and callback systems
|
|
34
|
-
- Interface hierarchies
|
|
35
|
-
- Extension mechanisms
|
|
36
|
-
|
|
37
|
-
## 3. Integration Patterns
|
|
38
|
-
Understand connection points:
|
|
39
|
-
- Component interactions
|
|
40
|
-
- Integration interfaces
|
|
41
|
-
- Extension methods
|
|
42
|
-
- Plugin systems
|
|
43
|
-
|
|
44
|
-
## 4. Extension Mechanisms
|
|
45
|
-
Identify customization options:
|
|
46
|
-
- Extension patterns
|
|
47
|
-
- Plugin architectures
|
|
48
|
-
- Configuration systems
|
|
49
|
-
- Customization points
|
|
50
|
-
|
|
51
|
-
# 📝 Question Guidelines
|
|
52
|
-
## Good Questions
|
|
53
|
-
- "What interfaces currently handle user authentication?"
|
|
54
|
-
- "How does the system expose extension points for plugins?"
|
|
55
|
-
- "Where are the existing hooks for custom providers?"
|
|
56
|
-
|
|
57
|
-
## Bad Questions
|
|
58
|
-
- "How should we implement the new feature?"
|
|
59
|
-
- "What's the best way to add this?"
|
|
60
|
-
- "Should we create a new class?"
|
|
61
|
-
|
|
62
|
-
# 🎨 Question Template
|
|
63
|
-
3-5 specific questions about existing implementations:
|
|
64
|
-
<QUESTION>
|
|
65
|
-
1. System architecture question
|
|
66
|
-
2. Implementation details question
|
|
67
|
-
3. Integration or extension question
|
|
68
|
-
</QUESTION>
|
|
69
|
-
|
|
70
|
-
# 🔎 Investigation Focus
|
|
71
|
-
1. Current System
|
|
72
|
-
- Existing implementations
|
|
73
|
-
- Active interfaces
|
|
74
|
-
- Current patterns
|
|
75
|
-
|
|
76
|
-
2. Integration Points
|
|
77
|
-
- Connection methods
|
|
78
|
-
- Extension hooks
|
|
79
|
-
- Plugin systems
|
|
80
|
-
|
|
81
|
-
3. Extension Options
|
|
82
|
-
- Customization points
|
|
83
|
-
- Configuration options
|
|
84
|
-
- Extension patterns
|
|
85
|
-
|
|
86
|
-
# ❗ Important Rules
|
|
87
|
-
1. Focus on EXISTING code
|
|
88
|
-
2. Ask about current patterns
|
|
89
|
-
3. Explore extension points
|
|
90
|
-
4. Investigate interfaces
|
|
91
|
-
5. Map dependencies
|
|
92
|
-
|
|
93
|
-
User Requirement:
|
|
94
|
-
{requirement}
|
|
95
|
-
"""
|
|
96
|
-
model = PlatformRegistry().get_thinking_platform()
|
|
97
|
-
response = model.chat_until_success(prompt)
|
|
98
|
-
response = re.search(r'<QUESTION>(.*?)</QUESTION>', response, re.DOTALL)
|
|
99
|
-
if response is None:
|
|
100
|
-
return None
|
|
101
|
-
return response.group(1)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def find_relevant_information(user_input: str, root_dir: str) -> Tuple[List[Dict[str, str]], str]:
|
|
105
|
-
try:
|
|
106
|
-
PrettyOutput.print("从代码库中查找文件...", OutputType.INFO)
|
|
107
|
-
codebase = CodeBase(root_dir)
|
|
108
|
-
question = make_question(user_input)
|
|
109
|
-
if question is None:
|
|
110
|
-
return [], ""
|
|
111
|
-
files_from_codebase, infomation = codebase.ask_codebase(question)
|
|
112
|
-
|
|
113
|
-
selected_files = select_files(files_from_codebase, os.getcwd())
|
|
114
|
-
return selected_files, infomation
|
|
115
|
-
except Exception:
|
|
116
|
-
PrettyOutput.print("查找相关文件失败", OutputType.ERROR)
|
|
117
|
-
return [], ""
|
|
File without changes
|
{jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{jarvis_ai_assistant-0.1.123.dist-info → jarvis_ai_assistant-0.1.124.dist-info}/top_level.txt
RENAMED
|
File without changes
|