jarvis-ai-assistant 0.1.129__py3-none-any.whl → 0.1.130__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.

@@ -41,89 +41,91 @@ class CodeAgent:
41
41
  "lsp_prepare_rename"
42
42
  ])
43
43
  code_system_prompt = """
44
- # 角色:高级代码工程师
45
- 精通安全、精确的代码修改,具有严格的验证流程。
46
-
47
- ## 核心原则
48
- 1. 安全第一:绝不破坏现有功能
49
- 2. 精准工程:最小化、针对性修改
50
- 3. 完整可追溯:记录所有决策
51
- 4. 验证驱动:在每个阶段进行验证
52
-
53
- ## 工具使用协议
54
- 1. 分析工具:
55
- - lsp_get_document_symbols:映射代码结构
56
- - lsp_find_references:理解使用模式
57
- - lsp_find_definition:追踪实现细节
58
-
59
- 2. 验证工具:
60
- - lsp_prepare_rename:安全重构检查
61
- - lsp_get_diagnostics:修改后检查
62
-
63
- 3. 系统工具:
64
- - execute_shell:用于git操作和grep搜索
65
- - ask_codebase:查询代码知识库
66
- - search_web:技术参考查找
67
-
68
- ## 工作流程(PDCA循环)
69
- 1. 计划:
70
- - 使用ask_user分析需求
71
- - 使用LSP工具映射现有代码
72
- - 使用find_references识别影响区域
73
- - 使用git创建回滚计划
74
-
75
- 2. 执行:
76
- - 在受保护的块中进行原子修改
77
- - 每次更改后自动运行lsp_get_diagnostics
78
- - 如果发现错误,使用lsp_find_references和lsp_find_definition进行即时修复
79
- - 每次更改后使用LSP验证语法
80
-
81
- 3. 检查:
82
- - 强制使用lsp_get_diagnostics进行完整诊断报告
83
- - 使用lsp_preprepare_rename验证所有重命名
84
- - 如果检测到错误,进入修复循环直到所有检查通过
85
-
86
- 4. 行动:
87
- - 使用git提交详细消息
88
- - 准备回滚脚本(如果需要)
89
- - 进行实施后审查
90
-
91
- ## 代码修改标准
92
- 1. 修改前要求:
44
+ # 专业代码工程师
45
+
46
+ ## 身份与能力范围
47
+ - **角色定位**:精通精确、安全代码修改的高级代码工程师
48
+ - **核心能力**:代码分析、精准重构和系统化验证
49
+ - **知识领域**:软件架构、设计模式和特定语言的最佳实践
50
+ - **局限性**:复杂系统和特定领域知识需要明确的上下文支持
51
+
52
+ ## 交互原则与策略
53
+ - **沟通风格**:清晰简洁的技术解释,为所有决策提供合理依据
54
+ - **呈现格式**:使用补丁格式展示结构化的代码变更及其上下文
55
+ - **主动引导**:在有益的情况下,提出超出即时需求的改进建议
56
+ - **特殊场景应对**:
57
+ - 对于模糊请求:在继续前提出澄清问题
58
+ - 对于高风险变更:提出带有验证步骤的渐进式方法
59
+ - 对于性能问题:平衡即时修复与长期架构考量
60
+
61
+ ## 任务执行规范
62
+ ### 分析阶段
63
+ - 使用lsp_get_document_symbols映射代码结构
64
+ - 通过lsp_find_references和lsp_find_definition追踪依赖关系
65
+ - 在进行更改前识别潜在影响区域
66
+ - 为安全创建回滚计划
67
+
68
+ ### 实施阶段
69
+ - 进行原子化、聚焦的最小范围更改
70
+ - 保持一致的代码风格和格式
71
+ - 每次重大更改后运行lsp_get_diagnostics
72
+ - 立即修复任何检测到的问题
73
+
74
+ ### 验证阶段
75
+ - 使用lsp_get_diagnostics进行全面诊断
76
+ - 用lsp_prepare_rename验证所有重命名元素
77
+ - 检查相关代码中的意外副作用
78
+ - 确保必要的向后兼容性
79
+
80
+ ### 文档阶段
81
+ - 为每项修改提供清晰的理由
82
+ - 在所有补丁描述中包含上下文
83
+ - 记录任何假设或约束条件
84
+ - 准备详细的提交信息
85
+
86
+ ## 代码修改协议
87
+ 1. **修改前要求**:
93
88
  - 完整的代码分析报告
94
- - 影响评估矩阵
95
- - 回滚程序文档
89
+ - 影响评估
90
+ - 验证策略
96
91
 
97
- 2. 修改实施:
98
- - 单一职责修改
99
- - 严格的代码范围验证(±3行缓冲区)
92
+ 2. **修改实施**:
93
+ - 单一职责变更
94
+ - 严格的范围验证
100
95
  - 接口兼容性检查
101
96
 
102
- 3. 验证清单:
103
- [ ] 执行lsp_get_diagnostics并确保零错误
104
- [ ] 使用lsp_find_references确认影响范围
105
- [ ] 使用lsp_prepare_rename验证重命名安全性
97
+ 3. **验证清单**:
98
+ - 运行lsp_get_diagnostics确保零错误
99
+ - 使用lsp_find_references确认影响范围
100
+ - lsp_prepare_rename验证重命名安全性
106
101
 
107
- 4. 修改后:
102
+ 4. **修改后流程**:
108
103
  - 代码审查模拟
109
104
  - 版本控制审计
110
- - 更新变更日志
111
-
112
- ## 关键要求
113
- 1. 强制分析:
114
- - 修改前完整符号追踪
115
- - 跨文件影响分析
116
- - 依赖关系映射
117
-
118
- 2. 禁止操作:
119
- - 未通过lsp_get_diagnostics检查继续操作
120
- - 多个功能组合修改
121
- - 未经测试的接口修改
122
-
123
- 3. 紧急协议:
124
- - lsp_get_diagnostics出现错误时立即停止并回滚
125
- - 出现意外行为时通知用户
126
- - 对任何回归进行事后分析
105
+ - 更新变更文档
106
+
107
+ ## 工具使用指南
108
+ - **分析工具**:
109
+ - lsp_get_document_symbols:用于映射代码结构
110
+ - lsp_find_references:用于理解使用模式
111
+ - lsp_find_definition:用于追踪实现细节
112
+
113
+ - **验证工具**:
114
+ - lsp_prepare_rename:用于安全重构检查
115
+ - lsp_get_diagnostics:用于修改后检查
116
+
117
+ - **系统工具**:
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. 为每项更改包含清晰的理由说明
127
129
  """
128
130
  self.agent = Agent(system_prompt=code_system_prompt,
129
131
  name="CodeAgent",
@@ -147,8 +149,7 @@ class CodeAgent:
147
149
  with spinner.hidden():
148
150
  git_commiter = GitCommitTool()
149
151
  git_commiter.execute({})
150
- else:
151
- spinner.text = "环境初始化完成"
152
+ spinner.text = "环境初始化完成"
152
153
  spinner.ok("✅")
153
154
 
154
155
 
@@ -7,14 +7,12 @@ from yaspin import yaspin
7
7
  from jarvis.jarvis_agent.output_handler import OutputHandler
8
8
  from jarvis.jarvis_platform.registry import PlatformRegistry
9
9
  from jarvis.jarvis_tools.git_commiter import GitCommitTool
10
- from jarvis.jarvis_tools.execute_shell_script import ShellScriptTool
11
10
  from jarvis.jarvis_tools.file_operation import FileOperationTool
12
- from jarvis.jarvis_tools.read_code import ReadCodeTool
13
11
  from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
14
12
  from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
15
13
  from jarvis.jarvis_utils.input import get_multiline_input
16
14
  from jarvis.jarvis_utils.output import OutputType, PrettyOutput
17
- from jarvis.jarvis_utils.utils import user_confirm
15
+ from jarvis.jarvis_utils.utils import get_file_line_count, user_confirm
18
16
 
19
17
  class PatchOutputHandler(OutputHandler):
20
18
  def name(self) -> str:
@@ -29,25 +27,47 @@ class PatchOutputHandler(OutputHandler):
29
27
 
30
28
  def prompt(self) -> str:
31
29
  return """
32
- # 🛠️ 上下文代码补丁规范
33
- 使用<PATCH>块来指定代码更改:
34
- --------------------------------
30
+ # 代码补丁规范
31
+
32
+ ## 补丁格式定义
33
+ 使用<PATCH>块来精确指定代码更改:
34
+ ```
35
35
  <PATCH>
36
36
  File: [文件路径]
37
37
  Reason: [修改原因]
38
38
  [上下文代码片段]
39
39
  </PATCH>
40
- --------------------------------
41
- 规则:
42
- 1. 代码片段必须包含足够的上下文(前后各3行)
43
- 2. 我可以看到完整代码,所以只需显示修改的代码部分,不需要将整个文件内容都显示出来
44
- 3. 保留原始缩进和格式
45
- 4. 对于新文件,提供完整代码
46
- 5. 修改现有文件时,保留周围未更改的代码
47
- 示例:
40
+ ```
41
+
42
+ ## 核心原则
43
+ 1. **上下文完整性**:代码片段必须包含足够的上下文(修改前后各3行)
44
+ 2. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
45
+ 3. **格式严格保持**:
46
+ - 严格保持原始代码的缩进方式(空格或制表符)
47
+ - 保持原始代码的空行数量和位置
48
+ - 保持原始代码的行尾空格处理方式
49
+ - 不改变原始代码的换行风格
50
+ 4. **新旧区分**:
51
+ - 对于新文件:提供完整的代码内容
52
+ - 对于现有文件:保留周围未更改的代码,突出显示变更部分
53
+ 5. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
54
+
55
+ ## 格式兼容性要求
56
+ 1. **缩进一致性**:
57
+ - 如果原代码使用4个空格缩进,补丁也必须使用4个空格缩进
58
+ - 如果原代码使用制表符缩进,补丁也必须使用制表符缩进
59
+ 2. **空行保留**:
60
+ - 如果原代码在函数之间有两个空行,补丁也必须保留这两个空行
61
+ - 如果原代码在类方法之间有一个空行,补丁也必须保留这一个空行
62
+ 3. **行尾处理**:
63
+ - 如果原代码行尾没有空格,补丁也不应添加行尾空格
64
+ - 如果原代码使用特定的行尾注释风格,补丁也应保持该风格
65
+
66
+ ## 补丁示例
67
+ ```
48
68
  <PATCH>
49
69
  File: src/utils/math.py
50
- Reason: 修复除零处理
70
+ Reason: 修复除零错误,增加参数验证以提高函数健壮性
51
71
  def safe_divide(a, b):
52
72
  # 添加参数验证
53
73
  if b == 0:
@@ -57,6 +77,14 @@ def safe_divide(a, b):
57
77
  def add(a, b):
58
78
  return a + b
59
79
  </PATCH>
80
+ ```
81
+
82
+ ## 最佳实践
83
+ - 每个补丁专注于单一职责的修改
84
+ - 提供足够的上下文,但避免包含过多无关代码
85
+ - 确保修改理由清晰明确,便于理解变更目的
86
+ - 保持代码风格一致性,遵循项目现有的编码规范
87
+ - 在修改前仔细分析原代码的格式风格,确保补丁与之完全兼容
60
88
  """
61
89
 
62
90
  def _parse_patch(patch_str: str) -> Dict[str, str]:
@@ -92,8 +120,22 @@ def apply_patch(output_str: str) -> str:
92
120
  for filepath, patch_content in patches.items():
93
121
  try:
94
122
  spinner.text = f"正在处理文件: {filepath}"
123
+ if not os.path.exists(filepath):
124
+ # 新建文件
125
+ spinner.text = "文件不存在,正在创建文件..."
126
+ os.makedirs(os.path.dirname(filepath), exist_ok=True)
127
+ open(filepath, 'w', encoding='utf-8').close()
128
+ spinner.write("✅ 文件创建完成")
95
129
  with spinner.hidden():
96
- handle_code_operation(filepath, patch_content)
130
+ retry_count = 3
131
+ while not handle_code_operation(filepath, patch_content):
132
+ retry_count -= 1
133
+ if retry_count > 0:
134
+ continue
135
+ if user_confirm("补丁应用失败,是否重试?", default=True):
136
+ pass
137
+ else:
138
+ raise Exception("补丁应用失败")
97
139
  spinner.write(f"✅ 文件 {filepath} 处理完成")
98
140
  except Exception as e:
99
141
  spinner.text = f"文件 {filepath} 处理失败: {str(e)}, 回滚文件"
@@ -123,16 +165,18 @@ def apply_patch(output_str: str) -> str:
123
165
  else:
124
166
  final_ret += "✅ 补丁已应用(没有新的提交)"
125
167
  else:
126
- final_ret += "❌ 我不想提交代码\n"
168
+ final_ret += "❌ 我拒绝应用此补丁\n"
127
169
  final_ret += "补丁预览:\n"
128
170
  final_ret += diff
129
171
  else:
130
172
  final_ret += "❌ 没有要提交的更改\n"
131
173
  # 用户确认最终结果
132
174
  PrettyOutput.print(final_ret, OutputType.USER)
133
- if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
134
- return final_ret
135
- return get_multiline_input("请输入自定义回复")
175
+ with spinner.hidden():
176
+ if not is_confirm_before_apply_patch() or user_confirm("是否使用此回复?", default=True):
177
+ return final_ret
178
+ return get_multiline_input("请输入自定义回复")
179
+
136
180
  def revert_file(filepath: str):
137
181
  """增强版git恢复,处理新文件"""
138
182
  import subprocess
@@ -172,6 +216,7 @@ def get_diff() -> str:
172
216
  return ret
173
217
  except subprocess.CalledProcessError as e:
174
218
  return f"获取差异失败: {str(e)}"
219
+
175
220
  def handle_commit_workflow()->bool:
176
221
  """Handle the git commit workflow and return the commit details.
177
222
 
@@ -187,15 +232,17 @@ def handle_commit_workflow()->bool:
187
232
 
188
233
 
189
234
  def handle_code_operation(filepath: str, patch_content: str) -> bool:
235
+ """处理代码操作"""
236
+ if get_file_line_count(filepath) < 30:
237
+ return handle_small_code_operation(filepath, patch_content)
238
+ else:
239
+ return handle_large_code_operation(filepath, patch_content)
240
+
241
+
242
+ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
190
243
  """处理基于上下文的代码片段"""
191
244
  with yaspin(text=f"正在修改文件 {filepath}...", color="cyan") as spinner:
192
245
  try:
193
- if not os.path.exists(filepath):
194
- # 新建文件
195
- spinner.text = "文件不存在,正在创建文件..."
196
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
197
- open(filepath, 'w', encoding='utf-8').close()
198
- spinner.write("✅ 文件创建完成")
199
246
  with spinner.hidden():
200
247
  old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
201
248
  if not old_file_content["success"]:
@@ -203,27 +250,40 @@ def handle_code_operation(filepath: str, patch_content: str) -> bool:
203
250
  return False
204
251
 
205
252
  prompt = f"""
206
- 你是一个代码审查员,请审查以下代码并将其与上下文合并。
207
- 原始代码:
208
- {old_file_content["stdout"]}
209
- 补丁内容:
210
- {patch_content}
211
- """
212
- prompt += f"""
213
- 请将代码与上下文合并并返回完整的合并代码,每次最多输出300行代码。
214
-
215
- 要求:
216
- 1. 严格保留原始代码的格式、空行和缩进
217
- 2. 仅在<MERGED_CODE>块中包含实际代码内容,包括空行和缩进
218
- 3. 绝对不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
219
- 4. 除了合并后的代码,不要输出任何其他文本
220
- 5. 所有代码输出完成后,输出<!!!FINISHED!!!>
221
-
222
- 输出格式:
223
- <MERGED_CODE>
224
- [merged_code]
225
- </MERGED_CODE>
226
- """
253
+ # 代码合并专家指南
254
+
255
+ ## 任务描述
256
+ 你是一位精确的代码审查与合并专家,需要将补丁内容与原始代码智能合并。
257
+
258
+ ## 输入资料
259
+ ### 原始代码
260
+ ```
261
+ {old_file_content["stdout"]}
262
+ ```
263
+
264
+ ### 补丁内容
265
+ ```
266
+ {patch_content}
267
+ ```
268
+
269
+ ## 合并要求
270
+ 1. **精确性**:严格按照补丁的意图修改代码
271
+ 2. **完整性**:确保所有需要的更改都被应用
272
+ 3. **一致性**:严格保留原始代码的格式、空行和缩进风格
273
+ 4. **上下文保留**:保持未修改部分的代码完全不变
274
+
275
+ ## 输出格式规范
276
+ - 仅在<MERGED_CODE>标签内输出合并后的完整代码
277
+ - 每次最多输出300行代码
278
+ - 不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
279
+ - 除了合并后的代码,不要输出任何其他文本
280
+ - 所有代码输出完成后,输出<!!!FINISHED!!!>标记
281
+
282
+ ## 输出模板
283
+ <MERGED_CODE>
284
+ [合并后的完整代码,包括所有空行和缩进]
285
+ </MERGED_CODE>
286
+ """
227
287
  model = PlatformRegistry().get_codegen_platform()
228
288
  model.set_suppress_output(True)
229
289
  count = 30
@@ -249,14 +309,19 @@ def handle_code_operation(filepath: str, patch_content: str) -> bool:
249
309
  finished = True
250
310
  break
251
311
  except:
252
- prompt += f"""继续输出接下来的300行代码
253
- 要求:
254
- 1. 严格保留原始代码的格式、空行和缩进
255
- 2. 仅在<MERGED_CODE>块中包含实际代码内容,包括空行和缩进
256
- 3. 绝对不要使用markdown代码块(```)或反引号,除非修改的是markdown文件
257
- 4. 除了合并后的代码,不要输出任何其他文本
258
- 5. 所有代码输出完成后,输出<!!!FINISHED!!!>
259
- """
312
+ prompt += f"""
313
+ # 继续输出
314
+
315
+ ## 说明
316
+ 请继续输出接下来的300行代码
317
+
318
+ ## 要求
319
+ - 严格保留原始代码的格式、空行和缩进
320
+ - 仅在<MERGED_CODE>块中包含实际代码内容
321
+ - 不要使用markdown代码块(```)或反引号
322
+ - 除了合并后的代码,不要输出任何其他文本
323
+ - 所有代码输出完成后,输出<!!!FINISHED!!!>标记
324
+ """
260
325
  pass
261
326
  if not finished:
262
327
  spinner.text = "生成代码失败"
@@ -274,3 +339,119 @@ def handle_code_operation(filepath: str, patch_content: str) -> bool:
274
339
  spinner.text = "代码修改失败"
275
340
  spinner.fail("❌")
276
341
  return False
342
+
343
+
344
+ def handle_large_code_operation(filepath: str, patch_content: str) -> bool:
345
+ """处理大型代码文件的补丁操作,使用差异化补丁格式"""
346
+ with yaspin(text=f"正在处理文件 {filepath}...", color="cyan") as spinner:
347
+ try:
348
+ # 读取原始文件内容
349
+ old_file_content = FileOperationTool().execute({"operation": "read", "files": [{"path": filepath}]})
350
+ if not old_file_content["success"]:
351
+ spinner.text = "文件读取失败"
352
+ spinner.fail("❌")
353
+ return False
354
+
355
+ model = PlatformRegistry().get_codegen_platform()
356
+ model.set_suppress_output(False)
357
+
358
+ prompt = f"""
359
+ # 代码补丁生成专家指南
360
+
361
+ ## 任务描述
362
+ 你是一位精确的代码补丁生成专家,需要根据补丁描述生成精确的代码差异。
363
+
364
+ ## 输入资料
365
+ ### 原始代码
366
+ ```
367
+ {old_file_content["stdout"]}
368
+ ```
369
+
370
+ ### 补丁内容
371
+ ```
372
+ {patch_content}
373
+ ```
374
+
375
+ ## 补丁生成要求
376
+ 1. **精确性**:严格按照补丁的意图修改代码
377
+ 2. **格式一致性**:严格保持原始代码的格式风格
378
+ - 缩进方式(空格或制表符)必须与原代码保持一致
379
+ - 空行数量和位置必须与原代码风格匹配
380
+ - 行尾空格处理必须与原代码一致
381
+ 3. **最小化修改**:只修改必要的代码部分,保持其他部分不变
382
+ 4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
383
+
384
+ ## 输出格式规范
385
+ - 使用<DIFF>块包围每个需要修改的代码段
386
+ - 每个<DIFF>块必须包含SEARCH部分和REPLACE部分
387
+ - SEARCH部分是需要查找的原始代码
388
+ - REPLACE部分是替换后的新代码
389
+ - 确保SEARCH部分能在原文件中唯一匹配
390
+ - 如果修改较大,可以使用多个<DIFF>块
391
+
392
+ ## 输出模板
393
+ <DIFF>
394
+ >>>>>> SEARCH
395
+ [需要查找的原始代码,包含足够上下文]
396
+ ======
397
+ [替换后的新代码]
398
+ <<<<<< REPLACE
399
+ </DIFF>
400
+
401
+ <DIFF>
402
+ >>>>>> SEARCH
403
+ [另一处需要查找的原始代码]
404
+ ======
405
+ [另一处替换后的新代码]
406
+ <<<<<< REPLACE
407
+ </DIFF>
408
+ """
409
+ # 获取补丁内容
410
+ with spinner.hidden():
411
+ response = model.chat_until_success(prompt)
412
+
413
+ # 解析差异化补丁
414
+ diff_blocks = re.finditer(r'<DIFF>\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?</DIFF>',
415
+ response, re.DOTALL)
416
+
417
+ # 读取原始文件内容
418
+ with open(filepath, 'r', encoding='utf-8') as f:
419
+ file_content = f.read()
420
+
421
+ # 应用所有差异化补丁
422
+ modified_content = file_content
423
+ patch_count = 0
424
+
425
+ for match in diff_blocks:
426
+ search_text = match.group(1).strip()
427
+ replace_text = match.group(2).strip()
428
+
429
+ # 检查搜索文本是否存在于文件中
430
+ if search_text in modified_content:
431
+ # 如果有多处,报错
432
+ if modified_content.count(search_text) > 1:
433
+ spinner.text = f"补丁 #{patch_count+1} 应用失败:找到多个匹配的代码段"
434
+ spinner.fail("❌")
435
+ return False
436
+ # 应用替换
437
+ modified_content = modified_content.replace(search_text, replace_text)
438
+ patch_count += 1
439
+ spinner.write(f"✅ 补丁 #{patch_count+1} 应用成功")
440
+ else:
441
+ spinner.text = f"补丁 #{patch_count+1} 应用失败:无法找到匹配的代码段"
442
+ spinner.fail("❌")
443
+ return False
444
+
445
+ # 写入修改后的内容
446
+ with open(filepath, 'w', encoding='utf-8') as f:
447
+ f.write(modified_content)
448
+
449
+ spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
450
+ spinner.ok("✅")
451
+ return True
452
+
453
+ except Exception as e:
454
+ spinner.text = f"文件修改失败: {str(e)}"
455
+ spinner.fail("❌")
456
+ return False
457
+