jarvis-ai-assistant 0.1.120__py3-none-any.whl → 0.1.122__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +3 -3
- jarvis/jarvis_code_agent/code_agent.py +36 -58
- jarvis/jarvis_code_agent/file_select.py +3 -3
- jarvis/jarvis_code_agent/patch.py +155 -298
- jarvis/jarvis_code_agent/relevant_files.py +2 -4
- jarvis/jarvis_codebase/main.py +2 -2
- jarvis/jarvis_lsp/registry.py +7 -7
- jarvis/jarvis_platform/ai8.py +10 -10
- jarvis/jarvis_platform/kimi.py +3 -5
- jarvis/jarvis_platform/oyi.py +11 -11
- jarvis/jarvis_platform/registry.py +3 -3
- jarvis/jarvis_platform_manager/main.py +2 -2
- jarvis/jarvis_tools/ask_codebase.py +1 -1
- jarvis/jarvis_tools/code_review.py +1 -1
- jarvis/jarvis_tools/create_code_agent.py +1 -1
- jarvis/jarvis_tools/file_operation.py +0 -3
- jarvis/jarvis_tools/git_commiter.py +26 -7
- jarvis/jarvis_tools/rag.py +1 -1
- jarvis/jarvis_tools/read_code.py +1 -2
- jarvis/jarvis_utils/__init__.py +120 -47
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/METADATA +1 -1
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/RECORD +27 -28
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/entry_points.txt +0 -1
- jarvis/jarvis_dev/main.py +0 -878
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.120.dist-info → jarvis_ai_assistant-0.1.122.dist-info}/top_level.txt +0 -0
|
@@ -21,246 +21,112 @@ class PatchOutputHandler(OutputHandler):
|
|
|
21
21
|
|
|
22
22
|
def prompt(self) -> str:
|
|
23
23
|
return """
|
|
24
|
-
#
|
|
25
|
-
<
|
|
26
|
-
File
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# 🗑️ DELETE: Remove existing code
|
|
41
|
-
<DELETE>
|
|
42
|
-
File: path/to/file
|
|
43
|
-
Lines: [start,end] or [start,end)
|
|
44
|
-
</DELETE>
|
|
45
|
-
|
|
46
|
-
# 🆕 NEW_FILE: Create new file
|
|
47
|
-
<NEW_FILE>
|
|
48
|
-
File: path/to/file
|
|
49
|
-
[new content]
|
|
50
|
-
...
|
|
51
|
-
</NEW_FILE>
|
|
52
|
-
|
|
53
|
-
# ➡️ MOVE_FILE: Relocate a file
|
|
54
|
-
<MOVE_FILE>
|
|
55
|
-
File: path/to/source/file
|
|
56
|
-
NewPath: path/to/destination/file
|
|
57
|
-
</MOVE_FILE>
|
|
58
|
-
|
|
59
|
-
# ❌ REMOVE_FILE: Delete entire file
|
|
60
|
-
<REMOVE_FILE>
|
|
61
|
-
File: path/to/file
|
|
62
|
-
</REMOVE_FILE>
|
|
63
|
-
|
|
64
|
-
# 📋 Formatting Rules
|
|
65
|
-
1. File Paths
|
|
66
|
-
- Use relative paths from project root
|
|
67
|
-
- Must be exact and case-sensitive
|
|
68
|
-
- Example: src/module/file.py
|
|
69
|
-
|
|
70
|
-
2. Line Numbers
|
|
71
|
-
- Format: [start,end] (inclusive) or [start,end) (right-exclusive)
|
|
72
|
-
- 1-based line numbers
|
|
73
|
-
- Single number for INSERT
|
|
74
|
-
- Omit for NEW_FILE/REMOVE_FILE
|
|
75
|
-
|
|
76
|
-
3. Content
|
|
77
|
-
- Maintain original indentation
|
|
78
|
-
- Follow existing code style
|
|
79
|
-
|
|
80
|
-
# 📌 Usage Examples
|
|
81
|
-
## REPLACE Example (Closed Interval)
|
|
82
|
-
<REPLACE>
|
|
83
|
-
File: src/utils.py
|
|
84
|
-
Lines: [9,13]
|
|
24
|
+
# 🛠️ Simplified Patch Format
|
|
25
|
+
<PATCH>
|
|
26
|
+
File path [Operation parameters]
|
|
27
|
+
Code content
|
|
28
|
+
</PATCH>
|
|
29
|
+
|
|
30
|
+
Operation types:
|
|
31
|
+
- Replace: [Start line,End line] Replace line range (e.g. [5,8] replaces lines 5-8)
|
|
32
|
+
- Delete: [Start line,End line] Delete line range (e.g. [10,10] deletes line 10)
|
|
33
|
+
- Insert: [Line number] Insert before specified line (e.g. [3] inserts before line 3)
|
|
34
|
+
- New file: [1] Create new file
|
|
35
|
+
|
|
36
|
+
Examples:
|
|
37
|
+
# Replace operation
|
|
38
|
+
<PATCH>
|
|
39
|
+
src/app.py [5,8]
|
|
85
40
|
def updated_function():
|
|
86
|
-
|
|
87
|
-
return
|
|
88
|
-
</
|
|
41
|
+
print("Replaced lines 5-8")
|
|
42
|
+
return new_value * 2
|
|
43
|
+
</PATCH>
|
|
89
44
|
|
|
90
|
-
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def new_calculation():
|
|
95
|
-
# Replaces lines 5-7 (excludes line 8)
|
|
96
|
-
return 42
|
|
97
|
-
</REPLACE>
|
|
45
|
+
# Delete operation
|
|
46
|
+
<PATCH>
|
|
47
|
+
src/old.py [10,10]
|
|
48
|
+
</PATCH>
|
|
98
49
|
|
|
99
|
-
|
|
100
|
-
<
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
new_feature()
|
|
105
|
-
</INSERT>
|
|
50
|
+
# Insert operation
|
|
51
|
+
<PATCH>
|
|
52
|
+
utils/logger.py [3]
|
|
53
|
+
print("Inserted before original line 3")
|
|
54
|
+
</PATCH>
|
|
106
55
|
|
|
107
|
-
## NEW_FILE Example
|
|
108
|
-
<NEW_FILE>
|
|
109
|
-
File: src/new_module.py
|
|
110
56
|
# New file creation
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
File: src/utils.py
|
|
118
|
-
Lines: [9,13]
|
|
119
|
-
</DELETE>
|
|
120
|
-
|
|
121
|
-
## MOVE_FILE Example
|
|
122
|
-
<MOVE_FILE>
|
|
123
|
-
File: src/old_dir/file.py
|
|
124
|
-
NewPath: src/new_dir/file.py
|
|
125
|
-
</MOVE_FILE>
|
|
126
|
-
|
|
127
|
-
## REMOVE_FILE Example
|
|
128
|
-
<REMOVE_FILE>
|
|
129
|
-
File: src/obsolete.py
|
|
130
|
-
</REMOVE_FILE>
|
|
131
|
-
|
|
132
|
-
# 🚨 Critical Requirements
|
|
133
|
-
1. One change per block
|
|
134
|
-
2. Use correct operation type
|
|
135
|
-
3. Match existing code style
|
|
136
|
-
4. Preserve indentation levels
|
|
137
|
-
5. Exact file paths required
|
|
138
|
-
6. Handle edge cases properly
|
|
139
|
-
7. Include error handling
|
|
140
|
-
8. Maintain code consistency
|
|
57
|
+
<PATCH>
|
|
58
|
+
config.yaml [1]
|
|
59
|
+
database:
|
|
60
|
+
host: localhost
|
|
61
|
+
port: 5432
|
|
62
|
+
</PATCH>
|
|
141
63
|
"""
|
|
142
64
|
|
|
143
65
|
|
|
144
66
|
def _parse_patch(patch_str: str) -> Dict[str, List[Dict[str, Any]]]:
|
|
145
|
-
"""
|
|
67
|
+
"""解析补丁格式"""
|
|
146
68
|
result = {}
|
|
147
|
-
|
|
69
|
+
header_pattern = re.compile(
|
|
70
|
+
r'^\s*"?(.+?)"?\s*\[(\d+)(?:,(\d+))?\]\s*$' # Match file path and line number
|
|
71
|
+
)
|
|
72
|
+
patches = re.findall(r'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
|
|
148
73
|
|
|
149
|
-
for
|
|
150
|
-
|
|
151
|
-
|
|
74
|
+
for patch in patches:
|
|
75
|
+
# 分割首行和内容
|
|
76
|
+
parts = patch.split('\n', 1)
|
|
77
|
+
if len(parts) < 1:
|
|
152
78
|
continue
|
|
79
|
+
header_line = parts[0].strip()
|
|
80
|
+
content = parts[1] if len(parts) > 1 else ''
|
|
81
|
+
|
|
82
|
+
# 仅在内容非空时添加换行符
|
|
83
|
+
if content and not content.endswith('\n'):
|
|
84
|
+
content += '\n'
|
|
153
85
|
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
if not
|
|
86
|
+
# 解析文件路径和行号
|
|
87
|
+
header_match = header_pattern.match(header_line)
|
|
88
|
+
if not header_match:
|
|
157
89
|
continue
|
|
158
|
-
filepath = file_match.group(1).strip()
|
|
159
|
-
|
|
160
|
-
# Initialize line numbers
|
|
161
|
-
start_line = end_line = 0
|
|
162
|
-
|
|
163
|
-
# Parse line numbers based on operation type
|
|
164
|
-
if patch_type in ['REPLACE', 'DELETE']:
|
|
165
|
-
# 增强正则表达式兼容性
|
|
166
|
-
line_match = re.match(
|
|
167
|
-
r"^Lines:\s*\[\s*(\d+)\s*,\s*(\d+)\s*([\]\)])\s*$", # 匹配行尾
|
|
168
|
-
lines[1].strip(), # 去除前后空格
|
|
169
|
-
re.IGNORECASE
|
|
170
|
-
)
|
|
171
|
-
if line_match:
|
|
172
|
-
start_line = int(line_match.group(1))
|
|
173
|
-
end_value = int(line_match.group(2))
|
|
174
|
-
bracket_type = line_match.group(3).strip()
|
|
175
|
-
|
|
176
|
-
# 根据括号类型处理区间
|
|
177
|
-
if bracket_type == ')': # [m,n)
|
|
178
|
-
end_line = end_value - 1
|
|
179
|
-
else: # [m,n]
|
|
180
|
-
end_line = end_value
|
|
181
|
-
|
|
182
|
-
# 确保 end_line >= start_line
|
|
183
|
-
end_line = max(end_line, start_line)
|
|
184
|
-
else:
|
|
185
|
-
PrettyOutput.print(f"无法解析行号格式: {lines[1]}", OutputType.WARNING)
|
|
186
|
-
continue
|
|
187
|
-
elif patch_type == 'INSERT':
|
|
188
|
-
line_match = re.match(r"Line:\s*(\d+)", lines[1])
|
|
189
|
-
if line_match:
|
|
190
|
-
start_line = int(line_match.group(1)) # 1-based
|
|
191
|
-
end_line = start_line
|
|
192
|
-
elif patch_type == 'MOVE_FILE':
|
|
193
|
-
new_path_match = re.match(r"NewPath:\s*([^\s]+)", lines[1])
|
|
194
|
-
if new_path_match:
|
|
195
|
-
new_path = new_path_match.group(1).strip()
|
|
196
|
-
else:
|
|
197
|
-
continue
|
|
198
|
-
|
|
199
|
-
# Get content (after metadata)
|
|
200
|
-
if patch_type in ['REPLACE', 'DELETE']:
|
|
201
|
-
content_start = 2 # File + Lines
|
|
202
|
-
elif patch_type == 'INSERT':
|
|
203
|
-
content_start = 2 # File + Line
|
|
204
|
-
elif patch_type == 'NEW_FILE':
|
|
205
|
-
content_start = 1 # File
|
|
206
|
-
elif patch_type == 'MOVE_FILE':
|
|
207
|
-
content_start = 2 # File + NewPath
|
|
208
|
-
elif patch_type == 'REMOVE_FILE':
|
|
209
|
-
content_start = 1 # File
|
|
210
|
-
|
|
211
|
-
content_lines = lines[content_start:]
|
|
212
|
-
content = '\n'.join(content_lines).strip()
|
|
213
90
|
|
|
91
|
+
filepath = header_match.group(1)
|
|
92
|
+
start = int(header_match.group(2)) # 保持1-based行号
|
|
93
|
+
end = int(header_match.group(3)) + 1 if header_match.group(3) else start
|
|
94
|
+
|
|
95
|
+
# 存储参数
|
|
214
96
|
if filepath not in result:
|
|
215
97
|
result[filepath] = []
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
else:
|
|
225
|
-
result[filepath].append({
|
|
226
|
-
'type': patch_type,
|
|
227
|
-
'start_line': start_line,
|
|
228
|
-
'end_line': end_line,
|
|
229
|
-
'content': content
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
# Sort patches by start line in reverse order to apply from bottom to top
|
|
233
|
-
for filepath in result:
|
|
234
|
-
result[filepath].sort(key=lambda x: x.get('start_line', 0), reverse=True)
|
|
235
|
-
|
|
98
|
+
result[filepath].append({
|
|
99
|
+
'filepath': filepath,
|
|
100
|
+
'start': start,
|
|
101
|
+
'end': end,
|
|
102
|
+
'content': content # 保留原始内容(可能为空)
|
|
103
|
+
})
|
|
104
|
+
for filepath in result.keys():
|
|
105
|
+
result[filepath] = sorted(result[filepath], key=lambda x: x['start'], reverse=True)
|
|
236
106
|
return result
|
|
237
107
|
|
|
238
108
|
|
|
239
109
|
def apply_patch(output_str: str) -> str:
|
|
240
110
|
"""Apply patches to files"""
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
for patch in patch_info:
|
|
247
|
-
patch_type = patch['type']
|
|
248
|
-
|
|
249
|
-
if patch_type == 'MOVE_FILE':
|
|
250
|
-
handle_move_file(filepath, patch)
|
|
251
|
-
elif patch_type == 'NEW_FILE':
|
|
252
|
-
handle_new_file(filepath, patch)
|
|
253
|
-
elif patch_type == 'REMOVE_FILE':
|
|
254
|
-
handle_remove_file(filepath)
|
|
255
|
-
else:
|
|
256
|
-
handle_code_operation(filepath, patch)
|
|
257
|
-
|
|
258
|
-
except Exception as e:
|
|
259
|
-
PrettyOutput.print(f"应用 {patch_type} 操作到 {filepath} 失败: {str(e)}", OutputType.ERROR)
|
|
260
|
-
continue
|
|
111
|
+
try:
|
|
112
|
+
patches = _parse_patch(output_str)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
|
|
115
|
+
return ""
|
|
261
116
|
|
|
117
|
+
ret = ""
|
|
118
|
+
|
|
119
|
+
for filepath, patch_list in patches.items():
|
|
120
|
+
for patch in patch_list:
|
|
121
|
+
try:
|
|
122
|
+
handle_code_operation(filepath, patch)
|
|
123
|
+
PrettyOutput.print(f"成功处理 操作", OutputType.SUCCESS)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
|
|
126
|
+
|
|
262
127
|
if has_uncommitted_changes():
|
|
263
|
-
|
|
128
|
+
diff = get_diff()
|
|
129
|
+
if handle_commit_workflow(diff):
|
|
264
130
|
ret += "Successfully applied the patch\n"
|
|
265
131
|
# Get modified line ranges
|
|
266
132
|
modified_ranges = get_modified_line_ranges()
|
|
@@ -269,25 +135,37 @@ def apply_patch(output_str: str) -> str:
|
|
|
269
135
|
ret += "New code:\n"
|
|
270
136
|
ret += modified_code["stdout"]
|
|
271
137
|
else:
|
|
272
|
-
ret += "User rejected the patch"
|
|
273
|
-
|
|
138
|
+
ret += "User rejected the patch\nThis is your patch preview:\n"
|
|
139
|
+
ret += diff
|
|
140
|
+
user_input = get_multiline_input("你可以继续输入(输入空行重试,Ctrl+C退出): ")
|
|
274
141
|
if user_input:
|
|
275
142
|
ret += "\n" + user_input
|
|
276
143
|
else:
|
|
277
|
-
|
|
144
|
+
ret = ""
|
|
278
145
|
|
|
279
146
|
return ret # Ensure a string is always returned
|
|
280
|
-
|
|
281
|
-
def
|
|
147
|
+
|
|
148
|
+
def get_diff() -> str:
|
|
149
|
+
"""使用更安全的subprocess代替os.system"""
|
|
150
|
+
import subprocess
|
|
151
|
+
try:
|
|
152
|
+
subprocess.run(['git', 'add', '.'], check=True)
|
|
153
|
+
result = subprocess.run(
|
|
154
|
+
['git', 'diff', 'HEAD'],
|
|
155
|
+
capture_output=True,
|
|
156
|
+
text=True,
|
|
157
|
+
check=True
|
|
158
|
+
)
|
|
159
|
+
return result.stdout
|
|
160
|
+
finally:
|
|
161
|
+
subprocess.run(['git', 'reset', 'HEAD'], check=True)
|
|
162
|
+
|
|
163
|
+
def handle_commit_workflow(diff:str)->bool:
|
|
282
164
|
"""Handle the git commit workflow and return the commit details.
|
|
283
165
|
|
|
284
166
|
Returns:
|
|
285
167
|
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
286
168
|
"""
|
|
287
|
-
os.system("git add .")
|
|
288
|
-
diff = os.popen("git diff HEAD").read()
|
|
289
|
-
os.system("git reset HEAD")
|
|
290
|
-
PrettyOutput.print(diff, OutputType.CODE, lang="diff")
|
|
291
169
|
if not user_confirm("是否要提交代码?", default=True):
|
|
292
170
|
os.system("git reset HEAD")
|
|
293
171
|
os.system("git checkout -- .")
|
|
@@ -303,7 +181,7 @@ def get_modified_line_ranges() -> Dict[str, Tuple[int, int]]:
|
|
|
303
181
|
|
|
304
182
|
Returns:
|
|
305
183
|
Dictionary mapping file paths to tuple with (start_line, end_line) ranges
|
|
306
|
-
for modified sections. Line numbers are
|
|
184
|
+
for modified sections. Line numbers are 1-based.
|
|
307
185
|
"""
|
|
308
186
|
# Get git diff for all files
|
|
309
187
|
diff_output = os.popen("git show").read()
|
|
@@ -322,91 +200,70 @@ def get_modified_line_ranges() -> Dict[str, Tuple[int, int]]:
|
|
|
322
200
|
# Match lines like "@@ -100,5 +100,7 @@" where the + part shows new lines
|
|
323
201
|
range_match = re.match(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@", line)
|
|
324
202
|
if range_match and current_file:
|
|
325
|
-
start_line = int(range_match.group(1))
|
|
203
|
+
start_line = int(range_match.group(1)) # Keep as 1-based
|
|
326
204
|
line_count = int(range_match.group(2)) if range_match.group(2) else 1
|
|
327
|
-
end_line = start_line + line_count
|
|
205
|
+
end_line = start_line + line_count - 1
|
|
328
206
|
result[current_file] = (start_line, end_line)
|
|
329
207
|
|
|
330
208
|
return result
|
|
331
|
-
|
|
332
209
|
# New handler functions below ▼▼▼
|
|
333
210
|
|
|
334
|
-
def handle_move_file(filepath: str, patch: Dict[str, Any]):
|
|
335
|
-
"""Handle file moving operation"""
|
|
336
|
-
new_path = patch['new_path']
|
|
337
|
-
os.makedirs(os.path.dirname(new_path), exist_ok=True)
|
|
338
|
-
if os.path.exists(filepath):
|
|
339
|
-
os.rename(filepath, new_path)
|
|
340
|
-
PrettyOutput.print(f"成功移动文件 {filepath} -> {new_path}", OutputType.SUCCESS)
|
|
341
|
-
else:
|
|
342
|
-
PrettyOutput.print(f"源文件不存在: {filepath}", OutputType.WARNING)
|
|
343
|
-
|
|
344
211
|
def handle_new_file(filepath: str, patch: Dict[str, Any]):
|
|
345
|
-
"""
|
|
346
|
-
new_content = patch.get('content', '').splitlines(keepends=True)
|
|
347
|
-
if new_content and new_content[-1] and new_content[-1][-1] != '\n':
|
|
348
|
-
new_content[-1] += '\n'
|
|
349
|
-
|
|
212
|
+
"""统一参数格式处理新文件"""
|
|
350
213
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
351
214
|
with open(filepath, 'w', encoding='utf-8') as f:
|
|
352
|
-
f.
|
|
353
|
-
PrettyOutput.print(f"成功创建新文件 {filepath}", OutputType.SUCCESS)
|
|
354
|
-
|
|
355
|
-
def handle_remove_file(filepath: str):
|
|
356
|
-
"""Handle file removal"""
|
|
357
|
-
if os.path.exists(filepath):
|
|
358
|
-
os.remove(filepath)
|
|
359
|
-
PrettyOutput.print(f"成功删除文件 {filepath}", OutputType.SUCCESS)
|
|
360
|
-
else:
|
|
361
|
-
PrettyOutput.print(f"文件不存在: {filepath}", OutputType.WARNING)
|
|
215
|
+
f.write(patch['content'])
|
|
362
216
|
|
|
363
217
|
def handle_code_operation(filepath: str, patch: Dict[str, Any]):
|
|
364
|
-
"""
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
218
|
+
"""处理紧凑格式补丁"""
|
|
219
|
+
try:
|
|
220
|
+
# 新建文件时强制覆盖
|
|
221
|
+
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
222
|
+
if not os.path.exists(filepath):
|
|
223
|
+
open(filepath, 'w', encoding='utf-8').close()
|
|
224
|
+
with open(filepath, 'r+', encoding='utf-8') as f:
|
|
225
|
+
lines = f.readlines()
|
|
226
|
+
|
|
227
|
+
new_lines = validate_and_apply_changes(
|
|
228
|
+
lines,
|
|
229
|
+
patch['start'],
|
|
230
|
+
patch['end'],
|
|
231
|
+
patch['content']
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
f.seek(0)
|
|
235
|
+
f.writelines(new_lines)
|
|
236
|
+
f.truncate()
|
|
381
237
|
|
|
382
|
-
|
|
383
|
-
lines = f.readlines()
|
|
384
|
-
validate_and_apply_changes(patch_type, lines, start_line, end_line, new_content)
|
|
385
|
-
f.seek(0)
|
|
386
|
-
f.writelines(lines)
|
|
387
|
-
f.truncate()
|
|
238
|
+
PrettyOutput.print(f"成功更新 {filepath}", OutputType.SUCCESS)
|
|
388
239
|
|
|
389
|
-
|
|
240
|
+
except Exception as e:
|
|
241
|
+
PrettyOutput.print(f"操作失败: {str(e)}", OutputType.ERROR)
|
|
390
242
|
|
|
391
243
|
def validate_and_apply_changes(
|
|
392
|
-
patch_type: str,
|
|
393
244
|
lines: List[str],
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
):
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
245
|
+
start: int,
|
|
246
|
+
end: int,
|
|
247
|
+
content: str
|
|
248
|
+
) -> List[str]:
|
|
249
|
+
|
|
250
|
+
new_content = content.splitlines(keepends=True)
|
|
251
|
+
|
|
252
|
+
# 插入操作处理
|
|
253
|
+
if start == end:
|
|
254
|
+
if start < 1 or start > len(lines)+1:
|
|
255
|
+
raise ValueError(f"无效插入位置: {start}")
|
|
256
|
+
# 在指定位置前插入
|
|
257
|
+
return lines[:start-1] + new_content + lines[start-1:]
|
|
258
|
+
|
|
259
|
+
# 范围替换/删除操作
|
|
260
|
+
if start > end:
|
|
261
|
+
raise ValueError(f"起始行{start}不能大于结束行{end}")
|
|
262
|
+
|
|
263
|
+
max_line = len(lines)
|
|
264
|
+
# 自动修正行号范围
|
|
265
|
+
start = max(1, min(start, max_line+1))
|
|
266
|
+
end = max(start, min(end, max_line+1))
|
|
267
|
+
|
|
268
|
+
# 执行替换
|
|
269
|
+
return lines[:start-1] + new_content + lines[end-1:]
|
|
@@ -60,14 +60,12 @@ Identify customization options:
|
|
|
60
60
|
- "Should we create a new class?"
|
|
61
61
|
|
|
62
62
|
# 🎨 Question Template
|
|
63
|
-
|
|
63
|
+
3-5 specific questions about existing implementations:
|
|
64
64
|
<QUESTION>
|
|
65
|
-
[3-5 specific questions about existing implementations:
|
|
66
65
|
1. System architecture question
|
|
67
66
|
2. Implementation details question
|
|
68
|
-
3. Integration or extension question
|
|
67
|
+
3. Integration or extension question
|
|
69
68
|
</QUESTION>
|
|
70
|
-
```
|
|
71
69
|
|
|
72
70
|
# 🔎 Investigation Focus
|
|
73
71
|
1. Current System
|
jarvis/jarvis_codebase/main.py
CHANGED
|
@@ -336,7 +336,7 @@ Content: {content}
|
|
|
336
336
|
vectors = np.vstack(vectors)
|
|
337
337
|
if len(vectors) != len(ids):
|
|
338
338
|
PrettyOutput.print(f"向量数量不匹配: {len(vectors)} 个向量 vs {len(ids)} 个ID",
|
|
339
|
-
output_type=OutputType.
|
|
339
|
+
output_type=OutputType.WARNING)
|
|
340
340
|
self.index = None
|
|
341
341
|
return
|
|
342
342
|
|
|
@@ -441,7 +441,7 @@ Content: {content}
|
|
|
441
441
|
output_lines.append("删除的文件:")
|
|
442
442
|
output_lines.extend(f" {f}" for f in deleted_files)
|
|
443
443
|
|
|
444
|
-
PrettyOutput.print("\n".join(output_lines), output_type=OutputType.
|
|
444
|
+
PrettyOutput.print("\n".join(output_lines), output_type=OutputType.INFO)
|
|
445
445
|
|
|
446
446
|
# If force is True, continue directly
|
|
447
447
|
if not force:
|
jarvis/jarvis_lsp/registry.py
CHANGED
|
@@ -59,8 +59,8 @@ class LSPRegistry:
|
|
|
59
59
|
|
|
60
60
|
if missing_methods:
|
|
61
61
|
PrettyOutput.print(
|
|
62
|
-
f"LSP {lsp_class.__name__}
|
|
63
|
-
OutputType.
|
|
62
|
+
f"LSP {lsp_class.__name__} 缺少必要的方法: {', '.join(missing_methods)}",
|
|
63
|
+
OutputType.WARNING
|
|
64
64
|
)
|
|
65
65
|
return False
|
|
66
66
|
|
|
@@ -72,7 +72,7 @@ class LSPRegistry:
|
|
|
72
72
|
lsp_servers = {}
|
|
73
73
|
|
|
74
74
|
if not os.path.exists(directory):
|
|
75
|
-
PrettyOutput.print(f"LSP 目录不存在: {directory}", OutputType.
|
|
75
|
+
PrettyOutput.print(f"LSP 目录不存在: {directory}", OutputType.WARNING)
|
|
76
76
|
return lsp_servers
|
|
77
77
|
|
|
78
78
|
package_name = None
|
|
@@ -138,7 +138,7 @@ class LSPRegistry:
|
|
|
138
138
|
def create_lsp(self, language: str) -> Optional[BaseLSP]:
|
|
139
139
|
"""Create LSP instance for specified language."""
|
|
140
140
|
if language not in self.lsp_servers:
|
|
141
|
-
PrettyOutput.print(f"没有找到 LSP 支持的语言: {language}", OutputType.
|
|
141
|
+
PrettyOutput.print(f"没有找到 LSP 支持的语言: {language}", OutputType.WARNING)
|
|
142
142
|
return None
|
|
143
143
|
|
|
144
144
|
try:
|
|
@@ -186,11 +186,11 @@ def main():
|
|
|
186
186
|
lsp = registry.create_lsp(args.language)
|
|
187
187
|
|
|
188
188
|
if not lsp:
|
|
189
|
-
PrettyOutput.print(f"没有 LSP 支持的语言: {args.language}", OutputType.
|
|
189
|
+
PrettyOutput.print(f"没有 LSP 支持的语言: {args.language}", OutputType.WARNING)
|
|
190
190
|
return 1
|
|
191
191
|
|
|
192
192
|
if not lsp.initialize(os.path.dirname(os.path.abspath(args.file))):
|
|
193
|
-
PrettyOutput.print("LSP 初始化失败", OutputType.
|
|
193
|
+
PrettyOutput.print("LSP 初始化失败", OutputType.WARNING)
|
|
194
194
|
return 1
|
|
195
195
|
|
|
196
196
|
try:
|
|
@@ -208,7 +208,7 @@ def main():
|
|
|
208
208
|
|
|
209
209
|
elif args.action in ('references', 'definition'):
|
|
210
210
|
if args.line is None or args.character is None:
|
|
211
|
-
PrettyOutput.print("需要行和字符位置用于 references/definition", OutputType.
|
|
211
|
+
PrettyOutput.print("需要行和字符位置用于 references/definition", OutputType.WARNING)
|
|
212
212
|
return 1
|
|
213
213
|
|
|
214
214
|
if args.action == 'references':
|