jarvis-ai-assistant 0.1.130__py3-none-any.whl → 0.1.132__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 +71 -38
- jarvis/jarvis_agent/builtin_input_handler.py +73 -0
- jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
- jarvis/jarvis_agent/main.py +1 -1
- jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +77 -55
- jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +1 -2
- jarvis/jarvis_code_agent/code_agent.py +93 -88
- jarvis/jarvis_dev/main.py +335 -626
- jarvis/jarvis_git_squash/main.py +11 -32
- jarvis/jarvis_lsp/base.py +2 -26
- jarvis/jarvis_lsp/cpp.py +2 -14
- jarvis/jarvis_lsp/go.py +0 -13
- jarvis/jarvis_lsp/python.py +1 -30
- jarvis/jarvis_lsp/registry.py +10 -14
- jarvis/jarvis_lsp/rust.py +0 -12
- jarvis/jarvis_multi_agent/__init__.py +20 -29
- jarvis/jarvis_platform/ai8.py +7 -32
- jarvis/jarvis_platform/base.py +2 -7
- jarvis/jarvis_platform/kimi.py +3 -144
- jarvis/jarvis_platform/ollama.py +54 -68
- jarvis/jarvis_platform/openai.py +0 -4
- jarvis/jarvis_platform/oyi.py +0 -75
- jarvis/jarvis_platform/registry.py +1 -1
- jarvis/jarvis_platform/yuanbao.py +264 -0
- jarvis/jarvis_platform_manager/main.py +3 -3
- jarvis/jarvis_rag/file_processors.py +138 -0
- jarvis/jarvis_rag/main.py +1305 -425
- jarvis/jarvis_tools/ask_codebase.py +227 -41
- jarvis/jarvis_tools/code_review.py +229 -166
- jarvis/jarvis_tools/create_code_agent.py +76 -72
- jarvis/jarvis_tools/create_sub_agent.py +32 -15
- jarvis/jarvis_tools/execute_python_script.py +58 -0
- jarvis/jarvis_tools/execute_shell.py +15 -28
- jarvis/jarvis_tools/execute_shell_script.py +2 -2
- jarvis/jarvis_tools/file_analyzer.py +271 -0
- jarvis/jarvis_tools/file_operation.py +3 -3
- jarvis/jarvis_tools/find_caller.py +213 -0
- jarvis/jarvis_tools/find_symbol.py +211 -0
- jarvis/jarvis_tools/function_analyzer.py +248 -0
- jarvis/jarvis_tools/git_commiter.py +89 -70
- jarvis/jarvis_tools/lsp_find_definition.py +83 -67
- jarvis/jarvis_tools/lsp_find_references.py +62 -46
- jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
- jarvis/jarvis_tools/methodology.py +89 -48
- jarvis/jarvis_tools/project_analyzer.py +220 -0
- jarvis/jarvis_tools/read_code.py +24 -3
- jarvis/jarvis_tools/read_webpage.py +195 -81
- jarvis/jarvis_tools/registry.py +132 -11
- jarvis/jarvis_tools/search_web.py +73 -30
- jarvis/jarvis_tools/tool_generator.py +7 -9
- jarvis/jarvis_utils/__init__.py +1 -0
- jarvis/jarvis_utils/config.py +67 -3
- jarvis/jarvis_utils/embedding.py +344 -45
- jarvis/jarvis_utils/git_utils.py +18 -2
- jarvis/jarvis_utils/input.py +7 -4
- jarvis/jarvis_utils/methodology.py +379 -7
- jarvis/jarvis_utils/output.py +5 -3
- jarvis/jarvis_utils/utils.py +62 -10
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/METADATA +3 -4
- jarvis_ai_assistant-0.1.132.dist-info/RECORD +82 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_c2rust/c2rust.yaml +0 -734
- jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
- jarvis/jarvis_codebase/__init__.py +0 -0
- jarvis/jarvis_codebase/main.py +0 -1011
- jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
- jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
- jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/top_level.txt +0 -0
|
@@ -12,7 +12,7 @@ from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
|
|
|
12
12
|
from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
|
|
13
13
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
14
14
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
15
|
-
from jarvis.jarvis_utils.utils import get_file_line_count, user_confirm
|
|
15
|
+
from jarvis.jarvis_utils.utils import ct, ot, get_file_line_count, user_confirm
|
|
16
16
|
|
|
17
17
|
class PatchOutputHandler(OutputHandler):
|
|
18
18
|
def name(self) -> str:
|
|
@@ -26,31 +26,35 @@ class PatchOutputHandler(OutputHandler):
|
|
|
26
26
|
return False
|
|
27
27
|
|
|
28
28
|
def prompt(self) -> str:
|
|
29
|
-
return """
|
|
29
|
+
return f"""
|
|
30
30
|
# 代码补丁规范
|
|
31
31
|
|
|
32
|
+
## 重要提示
|
|
33
|
+
我可以看到完整的代码,所以不需要生成完整的代码,只需要提供修改的代码片段即可。请尽量精简补丁内容,只包含必要的上下文和修改部分。
|
|
34
|
+
|
|
32
35
|
## 补丁格式定义
|
|
33
|
-
|
|
36
|
+
使用{ot("PATCH")}块来精确指定代码更改:
|
|
34
37
|
```
|
|
35
|
-
|
|
38
|
+
{ot("PATCH")}
|
|
36
39
|
File: [文件路径]
|
|
37
40
|
Reason: [修改原因]
|
|
38
41
|
[上下文代码片段]
|
|
39
|
-
|
|
42
|
+
{ct("PATCH")}
|
|
40
43
|
```
|
|
41
44
|
|
|
42
45
|
## 核心原则
|
|
43
46
|
1. **上下文完整性**:代码片段必须包含足够的上下文(修改前后各3行)
|
|
44
47
|
2. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
|
|
45
|
-
3.
|
|
48
|
+
3. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
|
|
49
|
+
4. **格式严格保持**:
|
|
46
50
|
- 严格保持原始代码的缩进方式(空格或制表符)
|
|
47
51
|
- 保持原始代码的空行数量和位置
|
|
48
52
|
- 保持原始代码的行尾空格处理方式
|
|
49
53
|
- 不改变原始代码的换行风格
|
|
50
|
-
|
|
54
|
+
5. **新旧区分**:
|
|
51
55
|
- 对于新文件:提供完整的代码内容
|
|
52
|
-
-
|
|
53
|
-
|
|
56
|
+
- 对于现有文件:只提供修改部分及必要上下文,不要提供整个文件
|
|
57
|
+
6. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
|
|
54
58
|
|
|
55
59
|
## 格式兼容性要求
|
|
56
60
|
1. **缩进一致性**:
|
|
@@ -65,7 +69,7 @@ Reason: [修改原因]
|
|
|
65
69
|
|
|
66
70
|
## 补丁示例
|
|
67
71
|
```
|
|
68
|
-
|
|
72
|
+
{ot("PATCH")}
|
|
69
73
|
File: src/utils/math.py
|
|
70
74
|
Reason: 修复除零错误,增加参数验证以提高函数健壮性
|
|
71
75
|
def safe_divide(a, b):
|
|
@@ -76,7 +80,7 @@ def safe_divide(a, b):
|
|
|
76
80
|
# 现有代码 ...
|
|
77
81
|
def add(a, b):
|
|
78
82
|
return a + b
|
|
79
|
-
|
|
83
|
+
{ct("PATCH")}
|
|
80
84
|
```
|
|
81
85
|
|
|
82
86
|
## 最佳实践
|
|
@@ -85,12 +89,13 @@ def add(a, b):
|
|
|
85
89
|
- 确保修改理由清晰明确,便于理解变更目的
|
|
86
90
|
- 保持代码风格一致性,遵循项目现有的编码规范
|
|
87
91
|
- 在修改前仔细分析原代码的格式风格,确保补丁与之完全兼容
|
|
92
|
+
- 绝不提供完整文件内容,除非是新建文件
|
|
88
93
|
"""
|
|
89
94
|
|
|
90
95
|
def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
91
96
|
"""解析新的上下文补丁格式"""
|
|
92
97
|
result = {}
|
|
93
|
-
patches = re.findall(r'
|
|
98
|
+
patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?'+ct("PATCH"), patch_str, re.DOTALL)
|
|
94
99
|
if patches:
|
|
95
100
|
for patch in patches:
|
|
96
101
|
first_line = patch.splitlines()[0]
|
|
@@ -99,7 +104,10 @@ def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
|
99
104
|
PrettyOutput.print("无效的补丁格式", OutputType.WARNING)
|
|
100
105
|
continue
|
|
101
106
|
filepath = sm.group(1).strip()
|
|
102
|
-
|
|
107
|
+
if filepath not in result:
|
|
108
|
+
result[filepath] = patch
|
|
109
|
+
else:
|
|
110
|
+
result[filepath] += "\n\n" + patch
|
|
103
111
|
return result
|
|
104
112
|
|
|
105
113
|
def apply_patch(output_str: str) -> str:
|
|
@@ -127,11 +135,7 @@ def apply_patch(output_str: str) -> str:
|
|
|
127
135
|
open(filepath, 'w', encoding='utf-8').close()
|
|
128
136
|
spinner.write("✅ 文件创建完成")
|
|
129
137
|
with spinner.hidden():
|
|
130
|
-
retry_count = 3
|
|
131
138
|
while not handle_code_operation(filepath, patch_content):
|
|
132
|
-
retry_count -= 1
|
|
133
|
-
if retry_count > 0:
|
|
134
|
-
continue
|
|
135
139
|
if user_confirm("补丁应用失败,是否重试?", default=True):
|
|
136
140
|
pass
|
|
137
141
|
else:
|
|
@@ -156,26 +160,41 @@ def apply_patch(output_str: str) -> str:
|
|
|
156
160
|
# 添加提交信息到final_ret
|
|
157
161
|
if commits:
|
|
158
162
|
final_ret += "✅ 补丁已应用\n"
|
|
159
|
-
final_ret += "提交信息:\n"
|
|
163
|
+
final_ret += "# 提交信息:\n"
|
|
160
164
|
for commit_hash, commit_message in commits:
|
|
161
165
|
final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
|
|
162
166
|
|
|
163
|
-
final_ret += f"应用补丁:\n{diff}"
|
|
167
|
+
final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
|
|
168
|
+
|
|
169
|
+
# 增加代码变更分析和错误提示
|
|
170
|
+
final_ret += "\n\n# 代码变更分析:\n"
|
|
171
|
+
final_ret += "1. 请仔细检查以上变更是否引入了潜在错误\n"
|
|
172
|
+
final_ret += "2. 如果发现代码错误,请立即提出修复方案\n"
|
|
173
|
+
final_ret += "3. 修复代码错误的优先级高于继续实现功能\n"
|
|
174
|
+
final_ret += "4. 常见错误类型:语法错误、逻辑错误、命名错误、路径错误等\n"
|
|
175
|
+
final_ret += "5. 确保修改后代码的一致性和完整性\n"
|
|
176
|
+
final_ret += "\n\n"
|
|
177
|
+
final_ret += "如果没有问题,请继续进行下一步修改,如果所有修改都已经完成,请终止"
|
|
164
178
|
|
|
165
179
|
else:
|
|
166
180
|
final_ret += "✅ 补丁已应用(没有新的提交)"
|
|
167
181
|
else:
|
|
168
|
-
final_ret += "❌
|
|
169
|
-
final_ret += "补丁预览:\n"
|
|
170
|
-
final_ret += diff
|
|
182
|
+
final_ret += "❌ 补丁应用被拒绝\n"
|
|
183
|
+
final_ret += f"# 补丁预览:\n```diff\n{diff}\n```"
|
|
171
184
|
else:
|
|
172
185
|
final_ret += "❌ 没有要提交的更改\n"
|
|
173
186
|
# 用户确认最终结果
|
|
174
|
-
PrettyOutput.print(final_ret, OutputType.USER)
|
|
175
187
|
with spinner.hidden():
|
|
188
|
+
PrettyOutput.print(final_ret, OutputType.USER, lang="markdown")
|
|
176
189
|
if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
|
|
177
190
|
return final_ret
|
|
178
|
-
|
|
191
|
+
custom_reply = get_multiline_input("请输入自定义回复")
|
|
192
|
+
if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
|
|
193
|
+
return ""
|
|
194
|
+
if not commited:
|
|
195
|
+
return final_ret + "\n\n" + custom_reply
|
|
196
|
+
else:
|
|
197
|
+
return custom_reply
|
|
179
198
|
|
|
180
199
|
def revert_file(filepath: str):
|
|
181
200
|
"""增强版git恢复,处理新文件"""
|
|
@@ -212,7 +231,7 @@ def get_diff() -> str:
|
|
|
212
231
|
check=True
|
|
213
232
|
)
|
|
214
233
|
ret = result.stdout
|
|
215
|
-
subprocess.run(['git', "reset", "--
|
|
234
|
+
subprocess.run(['git', "reset", "--mixed", "HEAD"], check=True)
|
|
216
235
|
return ret
|
|
217
236
|
except subprocess.CalledProcessError as e:
|
|
218
237
|
return f"获取差异失败: {str(e)}"
|
|
@@ -233,10 +252,15 @@ def handle_commit_workflow()->bool:
|
|
|
233
252
|
|
|
234
253
|
def handle_code_operation(filepath: str, patch_content: str) -> bool:
|
|
235
254
|
"""处理代码操作"""
|
|
236
|
-
if get_file_line_count(filepath) <
|
|
255
|
+
if get_file_line_count(filepath) < 100:
|
|
237
256
|
return handle_small_code_operation(filepath, patch_content)
|
|
238
257
|
else:
|
|
239
|
-
|
|
258
|
+
retry_count = 5
|
|
259
|
+
while retry_count > 0:
|
|
260
|
+
retry_count -= 1
|
|
261
|
+
if handle_large_code_operation(filepath, patch_content):
|
|
262
|
+
return True
|
|
263
|
+
return handle_small_code_operation(filepath, patch_content)
|
|
240
264
|
|
|
241
265
|
|
|
242
266
|
def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
@@ -273,16 +297,16 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
273
297
|
4. **上下文保留**:保持未修改部分的代码完全不变
|
|
274
298
|
|
|
275
299
|
## 输出格式规范
|
|
276
|
-
-
|
|
300
|
+
- 仅在{ot("MERGED_CODE")}标签内输出合并后的完整代码
|
|
277
301
|
- 每次最多输出300行代码
|
|
278
302
|
- 不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
|
|
279
303
|
- 除了合并后的代码,不要输出任何其他文本
|
|
280
|
-
-
|
|
304
|
+
- 所有代码输出完成后,输出{ot("!!!FINISHED!!!")}标记
|
|
281
305
|
|
|
282
306
|
## 输出模板
|
|
283
|
-
|
|
307
|
+
{ot("MERGED_CODE")}
|
|
284
308
|
[合并后的完整代码,包括所有空行和缩进]
|
|
285
|
-
|
|
309
|
+
{ct("MERGED_CODE")}
|
|
286
310
|
"""
|
|
287
311
|
model = PlatformRegistry().get_codegen_platform()
|
|
288
312
|
model.set_suppress_output(True)
|
|
@@ -295,9 +319,9 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
295
319
|
count -= 1
|
|
296
320
|
response = model.chat_until_success(prompt).splitlines()
|
|
297
321
|
try:
|
|
298
|
-
start_line = response.index("
|
|
322
|
+
start_line = response.index(ot("MERGED_CODE")) + 1
|
|
299
323
|
try:
|
|
300
|
-
end_line = response.index("
|
|
324
|
+
end_line = response.index(ct("MERGED_CODE"))
|
|
301
325
|
code = response[start_line:end_line]
|
|
302
326
|
except:
|
|
303
327
|
pass
|
|
@@ -305,7 +329,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
305
329
|
pass
|
|
306
330
|
|
|
307
331
|
try:
|
|
308
|
-
response.index("
|
|
332
|
+
response.index(ot("!!!FINISHED!!!"))
|
|
309
333
|
finished = True
|
|
310
334
|
break
|
|
311
335
|
except:
|
|
@@ -317,10 +341,10 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
317
341
|
|
|
318
342
|
## 要求
|
|
319
343
|
- 严格保留原始代码的格式、空行和缩进
|
|
320
|
-
-
|
|
344
|
+
- 仅在{ot("MERGED_CODE")}块中包含实际代码内容
|
|
321
345
|
- 不要使用markdown代码块(```)或反引号
|
|
322
346
|
- 除了合并后的代码,不要输出任何其他文本
|
|
323
|
-
-
|
|
347
|
+
- 所有代码输出完成后,输出{ot("!!!FINISHED!!!")}标记
|
|
324
348
|
"""
|
|
325
349
|
pass
|
|
326
350
|
if not finished:
|
|
@@ -329,7 +353,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
329
353
|
return False
|
|
330
354
|
# 写入合并后的代码
|
|
331
355
|
spinner.text = "写入合并后的代码..."
|
|
332
|
-
with open(filepath, 'w', encoding='utf-8') as f:
|
|
356
|
+
with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
|
|
333
357
|
f.write("\n".join(code)+"\n")
|
|
334
358
|
spinner.write("✅ 合并后的代码写入完成")
|
|
335
359
|
spinner.text = "代码修改完成"
|
|
@@ -353,7 +377,7 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
353
377
|
return False
|
|
354
378
|
|
|
355
379
|
model = PlatformRegistry().get_codegen_platform()
|
|
356
|
-
model.set_suppress_output(
|
|
380
|
+
model.set_suppress_output(True)
|
|
357
381
|
|
|
358
382
|
prompt = f"""
|
|
359
383
|
# 代码补丁生成专家指南
|
|
@@ -382,40 +406,39 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
382
406
|
4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
|
|
383
407
|
|
|
384
408
|
## 输出格式规范
|
|
385
|
-
-
|
|
386
|
-
-
|
|
409
|
+
- 使用{ot("DIFF")}块包围每个需要修改的代码段
|
|
410
|
+
- 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
|
|
387
411
|
- SEARCH部分是需要查找的原始代码
|
|
388
412
|
- REPLACE部分是替换后的新代码
|
|
389
413
|
- 确保SEARCH部分能在原文件中唯一匹配
|
|
390
|
-
-
|
|
414
|
+
- 如果修改较大,可以使用多个{ot("DIFF")}块
|
|
391
415
|
|
|
392
416
|
## 输出模板
|
|
393
|
-
|
|
417
|
+
{ot("DIFF")}
|
|
394
418
|
>>>>>> SEARCH
|
|
395
419
|
[需要查找的原始代码,包含足够上下文]
|
|
396
420
|
======
|
|
397
421
|
[替换后的新代码]
|
|
398
422
|
<<<<<< REPLACE
|
|
399
|
-
|
|
423
|
+
{ct("DIFF")}
|
|
400
424
|
|
|
401
|
-
|
|
425
|
+
{ot("DIFF")}
|
|
402
426
|
>>>>>> SEARCH
|
|
403
427
|
[另一处需要查找的原始代码]
|
|
404
428
|
======
|
|
405
429
|
[另一处替换后的新代码]
|
|
406
430
|
<<<<<< REPLACE
|
|
407
|
-
|
|
431
|
+
{ct("DIFF")}
|
|
408
432
|
"""
|
|
409
433
|
# 获取补丁内容
|
|
410
|
-
|
|
411
|
-
response = model.chat_until_success(prompt)
|
|
434
|
+
response = model.chat_until_success(prompt)
|
|
412
435
|
|
|
413
436
|
# 解析差异化补丁
|
|
414
|
-
diff_blocks = re.finditer(r'
|
|
437
|
+
diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
|
|
415
438
|
response, re.DOTALL)
|
|
416
439
|
|
|
417
440
|
# 读取原始文件内容
|
|
418
|
-
with open(filepath, 'r', encoding='utf-8') as f:
|
|
441
|
+
with open(filepath, 'r', encoding='utf-8', errors="ignore") as f:
|
|
419
442
|
file_content = f.read()
|
|
420
443
|
|
|
421
444
|
# 应用所有差异化补丁
|
|
@@ -425,25 +448,24 @@ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
425
448
|
for match in diff_blocks:
|
|
426
449
|
search_text = match.group(1).strip()
|
|
427
450
|
replace_text = match.group(2).strip()
|
|
428
|
-
|
|
451
|
+
patch_count += 1
|
|
429
452
|
# 检查搜索文本是否存在于文件中
|
|
430
453
|
if search_text in modified_content:
|
|
431
454
|
# 如果有多处,报错
|
|
432
455
|
if modified_content.count(search_text) > 1:
|
|
433
|
-
spinner.text = f"补丁 #{patch_count
|
|
456
|
+
spinner.text = f"补丁 #{patch_count} 应用失败:找到多个匹配的代码段"
|
|
434
457
|
spinner.fail("❌")
|
|
435
458
|
return False
|
|
436
459
|
# 应用替换
|
|
437
460
|
modified_content = modified_content.replace(search_text, replace_text)
|
|
438
|
-
patch_count
|
|
439
|
-
spinner.write(f"✅ 补丁 #{patch_count+1} 应用成功")
|
|
461
|
+
spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
|
|
440
462
|
else:
|
|
441
|
-
spinner.text = f"补丁 #{patch_count
|
|
463
|
+
spinner.text = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
|
|
442
464
|
spinner.fail("❌")
|
|
443
465
|
return False
|
|
444
466
|
|
|
445
467
|
# 写入修改后的内容
|
|
446
|
-
with open(filepath, 'w', encoding='utf-8') as f:
|
|
468
|
+
with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
|
|
447
469
|
f.write(modified_content)
|
|
448
470
|
|
|
449
471
|
spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, Tuple
|
|
4
4
|
|
|
5
|
-
from jarvis.jarvis_tools.execute_shell_script import ShellScriptTool
|
|
6
|
-
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
7
5
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
8
6
|
from jarvis.jarvis_utils.utils import user_confirm
|
|
9
7
|
|
|
@@ -17,6 +15,7 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
|
17
15
|
script = '\n'.join([c[1:] for c in cmdline])
|
|
18
16
|
PrettyOutput.print(script, OutputType.CODE, lang="bash")
|
|
19
17
|
if user_confirm(f"是否要执行以上shell脚本?", default=True):
|
|
18
|
+
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
20
19
|
output = ToolRegistry().handle_tool_calls({
|
|
21
20
|
"name": "execute_shell_script",
|
|
22
21
|
"arguments": {
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import re
|
|
2
1
|
import subprocess
|
|
3
2
|
import os
|
|
4
|
-
from typing import Any, Tuple
|
|
5
3
|
|
|
6
4
|
from yaspin import yaspin
|
|
7
5
|
|
|
8
6
|
from jarvis.jarvis_agent import Agent
|
|
9
|
-
from jarvis.
|
|
10
|
-
from jarvis.
|
|
11
|
-
from jarvis.
|
|
12
|
-
from jarvis.
|
|
7
|
+
from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
|
|
8
|
+
from jarvis.jarvis_agent.file_input_handler import file_input_handler
|
|
9
|
+
from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
|
|
10
|
+
from jarvis.jarvis_agent.patch import PatchOutputHandler
|
|
13
11
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
|
14
|
-
from jarvis.jarvis_tools.file_operation import FileOperationTool
|
|
15
12
|
from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
16
13
|
|
|
17
14
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
@@ -31,102 +28,112 @@ class CodeAgent:
|
|
|
31
28
|
tool_registry.use_tools(["execute_shell",
|
|
32
29
|
"execute_shell_script",
|
|
33
30
|
"search_web",
|
|
34
|
-
"create_code_agent",
|
|
35
31
|
"ask_user",
|
|
36
32
|
"ask_codebase",
|
|
37
|
-
"lsp_get_document_symbols",
|
|
38
33
|
"lsp_get_diagnostics",
|
|
39
34
|
"lsp_find_references",
|
|
40
|
-
"lsp_find_definition",
|
|
41
|
-
"
|
|
35
|
+
"lsp_find_definition",
|
|
36
|
+
"code_review", # 代码审查工具
|
|
37
|
+
"find_symbol", # 添加符号查找工具
|
|
38
|
+
"find_caller", # 添加函数调用者查找工具
|
|
39
|
+
"function_analyzer", # 添加函数分析工具
|
|
40
|
+
"project_analyzer", # 添加项目分析工具
|
|
41
|
+
"file_analyzer" # 添加单文件分析工具
|
|
42
42
|
])
|
|
43
43
|
code_system_prompt = """
|
|
44
44
|
# 专业代码工程师
|
|
45
45
|
|
|
46
|
-
##
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## 任务执行规范
|
|
46
|
+
## 身份与职责
|
|
47
|
+
- **角色**:精通代码修改的高级工程师
|
|
48
|
+
- **能力**:代码分析、精准重构和系统化验证
|
|
49
|
+
- **知识**:软件架构、设计模式和编程最佳实践
|
|
50
|
+
|
|
51
|
+
## 工作原则
|
|
52
|
+
- **准备工作**:在修改任何代码之前,必须已经阅读并充分理解相关代码
|
|
53
|
+
- **分析范围判断**:首先评估当前文件内容是否足够完成修改,避免不必要的项目分析
|
|
54
|
+
- **沟通**:清晰简洁的技术解释,提供决策依据
|
|
55
|
+
- **代码修改**:结构化展示变更及其上下文
|
|
56
|
+
- **问题处理**:遇到模糊请求时提出澄清问题,高风险变更时提出渐进式方法
|
|
57
|
+
- **修改效率**:对于变更不超过50行的代码,应一次性完成修改,避免分段处理
|
|
58
|
+
|
|
59
|
+
## 任务执行流程
|
|
62
60
|
### 分析阶段
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
61
|
+
- **范围确定**:首先判断是否仅需当前文件内容即可完成任务,避免不必要的分析
|
|
62
|
+
- 评估任务复杂度,只在必要时进行全面分析
|
|
63
|
+
- 简单修改可直接进行,无需复杂分析
|
|
64
|
+
- 使用代码分析工具理解依赖关系
|
|
65
|
+
- 识别潜在影响区域
|
|
66
|
+
- 确保在开始修改前,已经完全阅读和理解相关代码文件
|
|
67
67
|
|
|
68
68
|
### 实施阶段
|
|
69
|
-
-
|
|
69
|
+
- 进行最小范围更改,保持代码完整性
|
|
70
|
+
- 对于不超过50行的代码变更,应一次性完成修改
|
|
71
|
+
- 大型文件分段修改,确保每段修改后代码功能完整
|
|
70
72
|
- 保持一致的代码风格和格式
|
|
71
|
-
-
|
|
72
|
-
- 立即修复任何检测到的问题
|
|
73
|
+
- 修改后立即验证,优先修复错误
|
|
73
74
|
|
|
74
75
|
### 验证阶段
|
|
75
|
-
-
|
|
76
|
-
-
|
|
76
|
+
- 确认已充分理解所修改代码及其上下文
|
|
77
|
+
- 使用诊断工具检查问题
|
|
77
78
|
- 检查相关代码中的意外副作用
|
|
78
|
-
-
|
|
79
|
+
- 确保兼容性和功能正确性
|
|
80
|
+
- 验证修改是否符合原始设计意图和代码结构
|
|
79
81
|
|
|
80
82
|
### 文档阶段
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
- 记录任何假设或约束条件
|
|
83
|
+
- 提供清晰的修改理由和上下文
|
|
84
|
+
- 记录假设和约束条件
|
|
84
85
|
- 准备详细的提交信息
|
|
85
86
|
|
|
86
|
-
##
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
2. **修改实施**:
|
|
93
|
-
- 单一职责变更
|
|
94
|
-
- 严格的范围验证
|
|
95
|
-
- 接口兼容性检查
|
|
96
|
-
|
|
97
|
-
3. **验证清单**:
|
|
98
|
-
- 运行lsp_get_diagnostics确保零错误
|
|
99
|
-
- 使用lsp_find_references确认影响范围
|
|
100
|
-
- 用lsp_prepare_rename验证重命名安全性
|
|
87
|
+
## 工具使用指南
|
|
88
|
+
- **范围决策**:
|
|
89
|
+
- 先阅读当前文件,判断是否包含足够信息完成任务
|
|
90
|
+
- 只有在确认需要更多上下文时才扩展分析范围
|
|
91
|
+
- 明确告知用户当前文件是否足够,避免不必要分析
|
|
101
92
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
93
|
+
- **代码阅读与理解**:
|
|
94
|
+
- 在使用任何分析工具前,应首先完整阅读相关代码
|
|
95
|
+
- 使用分析工具补充而非替代直接阅读和理解代码
|
|
96
|
+
- 如有必要,可以使用`ask_codebase`工具来帮助理解复杂部分
|
|
106
97
|
|
|
107
|
-
## 工具使用指南
|
|
108
98
|
- **分析工具**:
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
99
|
+
- lsp_find_references:理解使用模式
|
|
100
|
+
- lsp_find_definition:追踪实现细节
|
|
101
|
+
- find_symbol:查找代码符号位置
|
|
102
|
+
- find_caller:查找函数调用位置
|
|
103
|
+
- function_analyzer:分析函数实现
|
|
104
|
+
- project_analyzer:分析项目架构(仅复杂任务使用)
|
|
105
|
+
- file_analyzer:分析单文件结构
|
|
112
106
|
|
|
113
107
|
- **验证工具**:
|
|
114
|
-
-
|
|
115
|
-
-
|
|
108
|
+
- lsp_get_diagnostics:检查修改问题
|
|
109
|
+
- code_review:代码审查
|
|
116
110
|
|
|
117
111
|
- **系统工具**:
|
|
118
|
-
- execute_shell
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
##
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
112
|
+
- execute_shell:执行系统命令
|
|
113
|
+
- search_web:查找技术参考
|
|
114
|
+
- ask_codebase:补充分析(优先使用直接分析工具)
|
|
115
|
+
|
|
116
|
+
## 代码分析策略
|
|
117
|
+
- **阅读优先**:在提出任何修改前,必须先阅读和理解相关代码
|
|
118
|
+
- **避免过度分析**:只分析与任务直接相关的代码
|
|
119
|
+
- **单文件优先**:优先考虑当前文件是否含有足够信息完成任务
|
|
120
|
+
- 当任务仅涉及语法、文本修改、逻辑优化等当前文件内部变更时,无需分析项目
|
|
121
|
+
- 当当前文件包含完整的模块/类/功能实现,且修改不影响外部接口时,无需额外分析
|
|
122
|
+
- 当用户明确指示仅修改当前文件时,无需扩大分析范围
|
|
123
|
+
- **任务分级**:
|
|
124
|
+
- 简单任务:先阅读相关代码,然后直接执行,尽量减少不必要的分析
|
|
125
|
+
- 中等任务:全面阅读相关文件并分析直接依赖,确保理解上下文
|
|
126
|
+
- 复杂任务:彻底阅读并进行全面项目分析,确保理解代码交互方式
|
|
127
|
+
- **长文件处理**:
|
|
128
|
+
- 完整阅读后再分段理解和修改
|
|
129
|
+
- 先处理核心部分,再扩展到相关代码
|
|
130
|
+
- 优先修改相对独立的部分
|
|
131
|
+
- **修改规模策略**:
|
|
132
|
+
- 对于不超过50行的代码变更,应尽量一次性完成
|
|
133
|
+
- 将相关逻辑变更放在同一个修改中,保持功能完整性
|
|
134
|
+
- 无需为小规模修改(≤50行)拆分多次提交,除非跨多个不相关模块
|
|
129
135
|
"""
|
|
136
|
+
# Dynamically add ask_codebase based on task complexity if really needed
|
|
130
137
|
self.agent = Agent(system_prompt=code_system_prompt,
|
|
131
138
|
name="CodeAgent",
|
|
132
139
|
auto_complete=False,
|
|
@@ -187,7 +194,7 @@ class CodeAgent:
|
|
|
187
194
|
if commits and user_confirm("是否接受以上提交记录?", True):
|
|
188
195
|
if len(commits) > 1 and user_confirm("是否要合并为一个更清晰的提交记录?", True):
|
|
189
196
|
# Reset to start commit
|
|
190
|
-
subprocess.run(["git", "reset", "--
|
|
197
|
+
subprocess.run(["git", "reset", "--mixed", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
191
198
|
# Create new commit
|
|
192
199
|
git_commiter = GitCommitTool()
|
|
193
200
|
git_commiter.execute({})
|
|
@@ -210,17 +217,15 @@ def main():
|
|
|
210
217
|
PrettyOutput.print(f"当前目录: {git_dir}", OutputType.INFO)
|
|
211
218
|
|
|
212
219
|
try:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
except Exception as e:
|
|
223
|
-
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
220
|
+
try:
|
|
221
|
+
user_input = get_multiline_input("请输入你的需求(输入空行退出):")
|
|
222
|
+
if not user_input:
|
|
223
|
+
return 0
|
|
224
|
+
agent = CodeAgent()
|
|
225
|
+
agent.run(user_input)
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
224
229
|
|
|
225
230
|
except Exception as e:
|
|
226
231
|
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|