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.
Files changed (72) hide show
  1. jarvis/__init__.py +1 -1
  2. jarvis/jarvis_agent/__init__.py +71 -38
  3. jarvis/jarvis_agent/builtin_input_handler.py +73 -0
  4. jarvis/{jarvis_code_agent → jarvis_agent}/file_input_handler.py +1 -1
  5. jarvis/jarvis_agent/main.py +1 -1
  6. jarvis/{jarvis_code_agent → jarvis_agent}/patch.py +77 -55
  7. jarvis/{jarvis_code_agent → jarvis_agent}/shell_input_handler.py +1 -2
  8. jarvis/jarvis_code_agent/code_agent.py +93 -88
  9. jarvis/jarvis_dev/main.py +335 -626
  10. jarvis/jarvis_git_squash/main.py +11 -32
  11. jarvis/jarvis_lsp/base.py +2 -26
  12. jarvis/jarvis_lsp/cpp.py +2 -14
  13. jarvis/jarvis_lsp/go.py +0 -13
  14. jarvis/jarvis_lsp/python.py +1 -30
  15. jarvis/jarvis_lsp/registry.py +10 -14
  16. jarvis/jarvis_lsp/rust.py +0 -12
  17. jarvis/jarvis_multi_agent/__init__.py +20 -29
  18. jarvis/jarvis_platform/ai8.py +7 -32
  19. jarvis/jarvis_platform/base.py +2 -7
  20. jarvis/jarvis_platform/kimi.py +3 -144
  21. jarvis/jarvis_platform/ollama.py +54 -68
  22. jarvis/jarvis_platform/openai.py +0 -4
  23. jarvis/jarvis_platform/oyi.py +0 -75
  24. jarvis/jarvis_platform/registry.py +1 -1
  25. jarvis/jarvis_platform/yuanbao.py +264 -0
  26. jarvis/jarvis_platform_manager/main.py +3 -3
  27. jarvis/jarvis_rag/file_processors.py +138 -0
  28. jarvis/jarvis_rag/main.py +1305 -425
  29. jarvis/jarvis_tools/ask_codebase.py +227 -41
  30. jarvis/jarvis_tools/code_review.py +229 -166
  31. jarvis/jarvis_tools/create_code_agent.py +76 -72
  32. jarvis/jarvis_tools/create_sub_agent.py +32 -15
  33. jarvis/jarvis_tools/execute_python_script.py +58 -0
  34. jarvis/jarvis_tools/execute_shell.py +15 -28
  35. jarvis/jarvis_tools/execute_shell_script.py +2 -2
  36. jarvis/jarvis_tools/file_analyzer.py +271 -0
  37. jarvis/jarvis_tools/file_operation.py +3 -3
  38. jarvis/jarvis_tools/find_caller.py +213 -0
  39. jarvis/jarvis_tools/find_symbol.py +211 -0
  40. jarvis/jarvis_tools/function_analyzer.py +248 -0
  41. jarvis/jarvis_tools/git_commiter.py +89 -70
  42. jarvis/jarvis_tools/lsp_find_definition.py +83 -67
  43. jarvis/jarvis_tools/lsp_find_references.py +62 -46
  44. jarvis/jarvis_tools/lsp_get_diagnostics.py +90 -74
  45. jarvis/jarvis_tools/methodology.py +89 -48
  46. jarvis/jarvis_tools/project_analyzer.py +220 -0
  47. jarvis/jarvis_tools/read_code.py +24 -3
  48. jarvis/jarvis_tools/read_webpage.py +195 -81
  49. jarvis/jarvis_tools/registry.py +132 -11
  50. jarvis/jarvis_tools/search_web.py +73 -30
  51. jarvis/jarvis_tools/tool_generator.py +7 -9
  52. jarvis/jarvis_utils/__init__.py +1 -0
  53. jarvis/jarvis_utils/config.py +67 -3
  54. jarvis/jarvis_utils/embedding.py +344 -45
  55. jarvis/jarvis_utils/git_utils.py +18 -2
  56. jarvis/jarvis_utils/input.py +7 -4
  57. jarvis/jarvis_utils/methodology.py +379 -7
  58. jarvis/jarvis_utils/output.py +5 -3
  59. jarvis/jarvis_utils/utils.py +62 -10
  60. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/METADATA +3 -4
  61. jarvis_ai_assistant-0.1.132.dist-info/RECORD +82 -0
  62. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/entry_points.txt +2 -0
  63. jarvis/jarvis_c2rust/c2rust.yaml +0 -734
  64. jarvis/jarvis_code_agent/builtin_input_handler.py +0 -43
  65. jarvis/jarvis_codebase/__init__.py +0 -0
  66. jarvis/jarvis_codebase/main.py +0 -1011
  67. jarvis/jarvis_tools/lsp_get_document_symbols.py +0 -87
  68. jarvis/jarvis_tools/lsp_prepare_rename.py +0 -130
  69. jarvis_ai_assistant-0.1.130.dist-info/RECORD +0 -79
  70. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/LICENSE +0 -0
  71. {jarvis_ai_assistant-0.1.130.dist-info → jarvis_ai_assistant-0.1.132.dist-info}/WHEEL +0 -0
  72. {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
- 使用<PATCH>块来精确指定代码更改:
36
+ 使用{ot("PATCH")}块来精确指定代码更改:
34
37
  ```
35
- <PATCH>
38
+ {ot("PATCH")}
36
39
  File: [文件路径]
37
40
  Reason: [修改原因]
38
41
  [上下文代码片段]
39
- </PATCH>
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
- 4. **新旧区分**:
54
+ 5. **新旧区分**:
51
55
  - 对于新文件:提供完整的代码内容
52
- - 对于现有文件:保留周围未更改的代码,突出显示变更部分
53
- 5. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
56
+ - 对于现有文件:只提供修改部分及必要上下文,不要提供整个文件
57
+ 6. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
54
58
 
55
59
  ## 格式兼容性要求
56
60
  1. **缩进一致性**:
@@ -65,7 +69,7 @@ Reason: [修改原因]
65
69
 
66
70
  ## 补丁示例
67
71
  ```
68
- <PATCH>
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
- </PATCH>
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'<PATCH>\n?(.*?)\n?</PATCH>', patch_str, re.DOTALL)
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
- result[filepath] = patch
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 += "❌ 我拒绝应用此补丁\n"
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
- return get_multiline_input("请输入自定义回复")
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", "--soft", "HEAD"], check=True)
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) < 30:
255
+ if get_file_line_count(filepath) < 100:
237
256
  return handle_small_code_operation(filepath, patch_content)
238
257
  else:
239
- return handle_large_code_operation(filepath, patch_content)
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
- - 仅在<MERGED_CODE>标签内输出合并后的完整代码
300
+ - 仅在{ot("MERGED_CODE")}标签内输出合并后的完整代码
277
301
  - 每次最多输出300行代码
278
302
  - 不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
279
303
  - 除了合并后的代码,不要输出任何其他文本
280
- - 所有代码输出完成后,输出<!!!FINISHED!!!>标记
304
+ - 所有代码输出完成后,输出{ot("!!!FINISHED!!!")}标记
281
305
 
282
306
  ## 输出模板
283
- <MERGED_CODE>
307
+ {ot("MERGED_CODE")}
284
308
  [合并后的完整代码,包括所有空行和缩进]
285
- </MERGED_CODE>
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("<MERGED_CODE>") + 1
322
+ start_line = response.index(ot("MERGED_CODE")) + 1
299
323
  try:
300
- end_line = response.index("</MERGED_CODE>")
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("<!!!FINISHED!!!>")
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
- - 仅在<MERGED_CODE>块中包含实际代码内容
344
+ - 仅在{ot("MERGED_CODE")}块中包含实际代码内容
321
345
  - 不要使用markdown代码块(```)或反引号
322
346
  - 除了合并后的代码,不要输出任何其他文本
323
- - 所有代码输出完成后,输出<!!!FINISHED!!!>标记
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(False)
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
- - 使用<DIFF>块包围每个需要修改的代码段
386
- - 每个<DIFF>块必须包含SEARCH部分和REPLACE部分
409
+ - 使用{ot("DIFF")}块包围每个需要修改的代码段
410
+ - 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
387
411
  - SEARCH部分是需要查找的原始代码
388
412
  - REPLACE部分是替换后的新代码
389
413
  - 确保SEARCH部分能在原文件中唯一匹配
390
- - 如果修改较大,可以使用多个<DIFF>块
414
+ - 如果修改较大,可以使用多个{ot("DIFF")}块
391
415
 
392
416
  ## 输出模板
393
- <DIFF>
417
+ {ot("DIFF")}
394
418
  >>>>>> SEARCH
395
419
  [需要查找的原始代码,包含足够上下文]
396
420
  ======
397
421
  [替换后的新代码]
398
422
  <<<<<< REPLACE
399
- </DIFF>
423
+ {ct("DIFF")}
400
424
 
401
- <DIFF>
425
+ {ot("DIFF")}
402
426
  >>>>>> SEARCH
403
427
  [另一处需要查找的原始代码]
404
428
  ======
405
429
  [另一处替换后的新代码]
406
430
  <<<<<< REPLACE
407
- </DIFF>
431
+ {ct("DIFF")}
408
432
  """
409
433
  # 获取补丁内容
410
- with spinner.hidden():
411
- response = model.chat_until_success(prompt)
434
+ response = model.chat_until_success(prompt)
412
435
 
413
436
  # 解析差异化补丁
414
- diff_blocks = re.finditer(r'<DIFF>\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?</DIFF>',
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+1} 应用失败:找到多个匹配的代码段"
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 += 1
439
- spinner.write(f"✅ 补丁 #{patch_count+1} 应用成功")
461
+ spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
440
462
  else:
441
- spinner.text = f"补丁 #{patch_count+1} 应用失败:无法找到匹配的代码段"
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.jarvis_code_agent.builtin_input_handler import builtin_input_handler
10
- from jarvis.jarvis_code_agent.file_input_handler import file_input_handler
11
- from jarvis.jarvis_code_agent.shell_input_handler import shell_input_handler
12
- from jarvis.jarvis_code_agent.patch import PatchOutputHandler
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
- "lsp_prepare_rename"
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
- - 使用lsp_get_document_symbols映射代码结构
64
- - 通过lsp_find_references和lsp_find_definition追踪依赖关系
65
- - 在进行更改前识别潜在影响区域
66
- - 为安全创建回滚计划
61
+ - **范围确定**:首先判断是否仅需当前文件内容即可完成任务,避免不必要的分析
62
+ - 评估任务复杂度,只在必要时进行全面分析
63
+ - 简单修改可直接进行,无需复杂分析
64
+ - 使用代码分析工具理解依赖关系
65
+ - 识别潜在影响区域
66
+ - 确保在开始修改前,已经完全阅读和理解相关代码文件
67
67
 
68
68
  ### 实施阶段
69
- - 进行原子化、聚焦的最小范围更改
69
+ - 进行最小范围更改,保持代码完整性
70
+ - 对于不超过50行的代码变更,应一次性完成修改
71
+ - 大型文件分段修改,确保每段修改后代码功能完整
70
72
  - 保持一致的代码风格和格式
71
- - 每次重大更改后运行lsp_get_diagnostics
72
- - 立即修复任何检测到的问题
73
+ - 修改后立即验证,优先修复错误
73
74
 
74
75
  ### 验证阶段
75
- - 使用lsp_get_diagnostics进行全面诊断
76
- - 用lsp_prepare_rename验证所有重命名元素
76
+ - 确认已充分理解所修改代码及其上下文
77
+ - 使用诊断工具检查问题
77
78
  - 检查相关代码中的意外副作用
78
- - 确保必要的向后兼容性
79
+ - 确保兼容性和功能正确性
80
+ - 验证修改是否符合原始设计意图和代码结构
79
81
 
80
82
  ### 文档阶段
81
- - 为每项修改提供清晰的理由
82
- - 在所有补丁描述中包含上下文
83
- - 记录任何假设或约束条件
83
+ - 提供清晰的修改理由和上下文
84
+ - 记录假设和约束条件
84
85
  - 准备详细的提交信息
85
86
 
86
- ## 代码修改协议
87
- 1. **修改前要求**:
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
- 4. **修改后流程**:
103
- - 代码审查模拟
104
- - 版本控制审计
105
- - 更新变更文档
93
+ - **代码阅读与理解**:
94
+ - 在使用任何分析工具前,应首先完整阅读相关代码
95
+ - 使用分析工具补充而非替代直接阅读和理解代码
96
+ - 如有必要,可以使用`ask_codebase`工具来帮助理解复杂部分
106
97
 
107
- ## 工具使用指南
108
98
  - **分析工具**:
109
- - lsp_get_document_symbols:用于映射代码结构
110
- - lsp_find_references:用于理解使用模式
111
- - lsp_find_definition:用于追踪实现细节
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
- - lsp_prepare_rename:用于安全重构检查
115
- - lsp_get_diagnostics:用于修改后检查
108
+ - lsp_get_diagnostics:检查修改问题
109
+ - code_review:代码审查
116
110
 
117
111
  - **系统工具**:
118
- - execute_shell:用于git操作和grep搜索
119
- - ask_codebase:用于查询代码知识库
120
- - search_web:用于技术参考查找
121
-
122
- ## 补丁格式要求
123
- 创建代码补丁时:
124
- 1. 包含足够的上下文(更改前后各3行)
125
- 2. 保留原始缩进和格式
126
- 3. 为新文件提供完整代码
127
- 4. 对于修改,保留周围未更改的代码
128
- 5. 为每项更改包含清晰的理由说明
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", "--soft", start_commit], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
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
- # Interactive mode
214
- while True:
215
- try:
216
- user_input = get_multiline_input("请输入你的需求(输入空行退出):")
217
- if not user_input:
218
- break
219
- agent = CodeAgent()
220
- agent.run(user_input)
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)