auto-coder 0.1.373__py3-none-any.whl → 0.1.374__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 auto-coder might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.373
3
+ Version: 0.1.374
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
14
14
  autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
15
15
  autocoder/models.py,sha256=Gu50IATQtZtgEir1PpCfwgH6o4ygw6XqqbQRj3lx5dU,13798
16
16
  autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
17
- autocoder/version.py,sha256=n_1OgRVIzzmD8JoAnBN8M2OwEcNk7LZF2lwEiwlrXcY,25
17
+ autocoder/version.py,sha256=myTr8pn1VpwBIskg5kgvqwLhCe4h-7rz4I7ovJf4BXM,25
18
18
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
20
20
  autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
@@ -207,12 +207,12 @@ autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py,sha256=
207
207
  autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py,sha256=dIdV12VuczHpHuHgx2B1j_3BZYc9PL0jfHCuBk9ryk8,2005
208
208
  autocoder/common/v2/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py,sha256=lGT4_QYJK6Fa9f6HVSGo0cSsGK7qCsDYgJGUowNxPzk,1499
209
209
  autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py,sha256=6JhjM3VXV3BGelh1dNcdr-M5FoVPoqLkls1-y8ND8_c,6721
210
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=J-GOlaLMwEu83v9VuHIstTpRR_vgPMR9wP_AzoK173s,18009
210
+ autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py,sha256=ZTdImTBK7KTYH98JVUioBNtIz-dqSfhkmNEBhajt7hk,12686
211
211
  autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py,sha256=VtDRDFJ2qzACy2jpD1ZNhywgCT3_iYUmGoUhHHmMX3o,9581
212
212
  autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py,sha256=9eBo3WLkrr77iNotwIwVmH1ZL3UY0JQgLpdAIc9wTTM,6127
213
213
  autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py,sha256=ZWRPsJny_My4UMzovrB8J2_x5N0rEW-xx3DVI-kDRFI,15870
214
214
  autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py,sha256=wM2Xy4bcnD0TSLEmcM8rvvyyWenN5_KQnJMO6hJ8lTE,1716
215
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=ISJ2jNz0Dfv1W7P2A_mYSM0vPdN0yW1w7Qqt6QjTk98,11170
215
+ autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py,sha256=lPESPLoe9P_bEXXNqqHAkVlqMrIH0XziJ6XeILIejUo,8084
216
216
  autocoder/compilers/__init__.py,sha256=C0HOms70QA747XD0uZEMmGtRFcIPenohyqECNStv0Bw,1647
217
217
  autocoder/compilers/base_compiler.py,sha256=dsTzMO4H_RoqWfE-SntIk2B52hWuvSlWVLtkdCbHgGs,3244
218
218
  autocoder/compilers/compiler_config_api.py,sha256=QRSwWm_EX7jSeZ3dtQqM9HI__x5aZ7U0c3fHIW_2N48,13839
@@ -252,7 +252,7 @@ autocoder/ignorefiles/test_ignore_file_utils.py,sha256=961_5ilCgzyo09Luj457A4694
252
252
  autocoder/index/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
253
253
  autocoder/index/entry.py,sha256=Eb0SmwrgshQte3_IGL4GAB4_HXC2jbatrgrd2uPxQng,15306
254
254
  autocoder/index/for_command.py,sha256=BFvljE4t6VaMBGboZAuhUCzVK0EitCy_n5D_7FEnihw,3204
255
- autocoder/index/index.py,sha256=IoPNsM7q-bbO94qbSwj27KPBwFkhuaA0f3sWqv_-ME0,33246
255
+ autocoder/index/index.py,sha256=xHm9jrAGy3qErS6GlUli3uFaEzHu_PZLjda8Sp0i0hw,33234
256
256
  autocoder/index/symbols_utils.py,sha256=_EP7E_qWXxluAxq3FGZLlLfdrfwx3FmxCdulI8VGuac,2244
257
257
  autocoder/index/types.py,sha256=a2s_KV5FJlq7jqA2ELSo9E1sjuLwDB-JJYMhSpzBAhU,596
258
258
  autocoder/index/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -261,10 +261,10 @@ autocoder/index/filter/quick_filter.py,sha256=ozESEgy506FQ5ecjOumyo4D_KMrterB1QL
261
261
  autocoder/linters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
262
262
  autocoder/linters/base_linter.py,sha256=1_0DPESnSyF3ZcQhoFkBYJylT5w-B61Rx-3A9uhuPlg,3066
263
263
  autocoder/linters/code_linter.py,sha256=JylTj-Mj4jl9-XSH3PVlbQ4l55Y6E1FG-glv860CGSs,22462
264
- autocoder/linters/linter_factory.py,sha256=rsDzaY0jFnmEsbY8Co_xxpJe9lXgfs07Nnrht8_WWtY,10273
264
+ autocoder/linters/linter_factory.py,sha256=BgGeXPdli7BgiN9BifWoosyn9BGeJnRwSqX0G1R8qvU,10471
265
265
  autocoder/linters/models.py,sha256=GBdayu_p50KBxoRms4X68zrDK-OsKDEKKjo926FevwE,9838
266
- autocoder/linters/normal_linter.py,sha256=5Yavs24XNMOryl0ZDpr1UbVboCZwaIs4x9mFqoC1ZYk,13022
267
- autocoder/linters/python_linter.py,sha256=CurQa4pYeyPv_e4FxgVBHW-pHOzQTOftARLgY97AKBw,22511
266
+ autocoder/linters/normal_linter.py,sha256=ezToVW33psvBXsGhE7y1ng7ucf7yT_1YuIULns6TXYM,13011
267
+ autocoder/linters/python_linter.py,sha256=vs6cyN77q4HeiBvFbVBzMpL9H75wwskJk5WXjgIPw64,18963
268
268
  autocoder/linters/reactjs_linter.py,sha256=LDCXmAI15LUq8nNPyYko3oryzCc2Su05ob63jEeDdIs,20815
269
269
  autocoder/linters/shadow_linter.py,sha256=SKgRNVnTavNUviFC9osYMz18nGWCVOPOHx9LavEbnmc,15047
270
270
  autocoder/linters/vue_linter.py,sha256=ZyvoxT0kSizFh_UkR7UZYO5DV9edbvDQZaibEF9W95I,20905
@@ -359,9 +359,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
359
359
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
360
360
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=t902pKxQ5xM7zgIHiAOsTPLwxhE6VuvXAqPy751S7fg,14096
361
361
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
362
- auto_coder-0.1.373.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
363
- auto_coder-0.1.373.dist-info/METADATA,sha256=mL168k1XbVSt5qulwcxGwiUHRisywbZcR6s3FHRxXm8,2775
364
- auto_coder-0.1.373.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
365
- auto_coder-0.1.373.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
366
- auto_coder-0.1.373.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
367
- auto_coder-0.1.373.dist-info/RECORD,,
362
+ auto_coder-0.1.374.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
363
+ auto_coder-0.1.374.dist-info/METADATA,sha256=jnsshouyIn8HqIoTFFx93VtDWl2KrRv_hYJS565hHrw,2775
364
+ auto_coder-0.1.374.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
365
+ auto_coder-0.1.374.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
366
+ auto_coder-0.1.374.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
367
+ auto_coder-0.1.374.dist-info/RECORD,,
@@ -7,6 +7,7 @@ from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import Base
7
7
  from autocoder.common.v2.agent.agentic_edit_types import ReplaceInFileTool, ToolResult
8
8
  from autocoder.common.file_checkpoint.models import FileChange as CheckpointFileChange
9
9
  from autocoder.common.file_checkpoint.manager import FileChangeManager as CheckpointFileChangeManager
10
+ from autocoder.linters.models import IssueSeverity, FileLintResult
10
11
  from loguru import logger
11
12
  from autocoder.common.auto_coder_lang import get_message_with_format
12
13
  if typing.TYPE_CHECKING:
@@ -62,7 +63,38 @@ class ReplaceInFileToolResolver(BaseToolResolver):
62
63
  logger.warning(f"Could not parse any SEARCH/REPLACE blocks from diff: {diff_content}")
63
64
  return blocks
64
65
 
65
- def _format_lint_issues(self, lint_result):
66
+ def _filter_lint_issues(self, lint_result:FileLintResult, levels: List[IssueSeverity] = [IssueSeverity.ERROR, IssueSeverity.WARNING]):
67
+ """
68
+ 过滤 lint 结果,只保留指定级别的问题
69
+
70
+ 参数:
71
+ lint_result: 单个文件的 lint 结果对象
72
+ levels: 要保留的问题级别列表,默认保留 ERROR 和 WARNING 级别
73
+
74
+ 返回:
75
+ 过滤后的 lint 结果对象(原对象的副本)
76
+ """
77
+ if not lint_result or not lint_result.issues:
78
+ return lint_result
79
+
80
+ # 创建一个新的 issues 列表,只包含指定级别的问题
81
+ filtered_issues = []
82
+ for issue in lint_result.issues:
83
+ if issue.severity in levels:
84
+ filtered_issues.append(issue)
85
+
86
+ # 更新 lint_result 的副本
87
+ filtered_result = lint_result
88
+ filtered_result.issues = filtered_issues
89
+
90
+ # 更新计数
91
+ filtered_result.error_count = sum(1 for issue in filtered_result.issues if issue.severity == IssueSeverity.ERROR)
92
+ filtered_result.warning_count = sum(1 for issue in filtered_result.issues if issue.severity == IssueSeverity.WARNING)
93
+ filtered_result.info_count = sum(1 for issue in filtered_result.issues if issue.severity == IssueSeverity.INFO)
94
+
95
+ return filtered_result
96
+
97
+ def _format_lint_issues(self, lint_result:FileLintResult):
66
98
  """
67
99
  将 lint 结果格式化为可读的文本格式
68
100
 
@@ -86,130 +118,7 @@ class ReplaceInFileToolResolver(BaseToolResolver):
86
118
 
87
119
  return "\n".join(formatted_issues)
88
120
 
89
- def replace_in_file_with_shadow(self, file_path: str, diff_content: str, source_dir: str, abs_project_dir: str, abs_file_path: str) -> ToolResult:
90
- """Replace content in file using shadow manager for path translation"""
91
- try:
92
- # Determine target path: shadow file
93
- target_path = self.shadow_manager.to_shadow_path(abs_file_path)
94
-
95
- # Read original content
96
- if os.path.exists(target_path) and os.path.isfile(target_path):
97
- with open(target_path, 'r', encoding='utf-8', errors='replace') as f:
98
- original_content = f.read()
99
- elif os.path.exists(abs_file_path) and os.path.isfile(abs_file_path):
100
- # If shadow doesn't exist, but original exists, read original content
101
- with open(abs_file_path, 'r', encoding='utf-8', errors='replace') as f:
102
- original_content = f.read()
103
- # create parent dirs of shadow if needed
104
- os.makedirs(os.path.dirname(target_path), exist_ok=True)
105
- # write original content into shadow file as baseline
106
- with open(target_path, 'w', encoding='utf-8') as f:
107
- f.write(original_content)
108
- logger.info(f"[Shadow] Initialized shadow file from original: {target_path}")
109
- else:
110
- return ToolResult(success=False, message=get_message_with_format("replace_in_file.file_not_found", file_path=file_path))
111
-
112
- parsed_blocks = self.parse_diff(diff_content)
113
- if not parsed_blocks:
114
- return ToolResult(success=False, message=get_message_with_format("replace_in_file.no_valid_blocks"))
115
-
116
- current_content = original_content
117
- applied_count = 0
118
- errors = []
119
-
120
- # Apply blocks sequentially
121
- for i, (search_block, replace_block) in enumerate(parsed_blocks):
122
- start_index = current_content.find(search_block)
123
-
124
- if start_index != -1:
125
- current_content = current_content[:start_index] + replace_block + current_content[start_index + len(search_block):]
126
- applied_count += 1
127
- logger.info(f"Applied SEARCH/REPLACE block {i+1} in file {file_path}")
128
- else:
129
- error_message = f"SEARCH block {i+1} not found in the current file content. Content to search:\n---\n{search_block}\n---"
130
- logger.warning(error_message)
131
- context_start = max(0, original_content.find(search_block[:20]) - 100)
132
- context_end = min(len(original_content), context_start + 200 + len(search_block[:20]))
133
- logger.warning(f"Approximate context in file:\n---\n{original_content[context_start:context_end]}\n---")
134
- errors.append(error_message)
135
-
136
- if applied_count == 0 and errors:
137
- return ToolResult(success=False, message=get_message_with_format("replace_in_file.apply_failed", errors="\n".join(errors)))
138
-
139
- # Write the modified content back to shadow file
140
- os.makedirs(os.path.dirname(target_path), exist_ok=True)
141
- with open(target_path, 'w', encoding='utf-8') as f:
142
- f.write(current_content)
143
-
144
- logger.info(f"Successfully applied {applied_count}/{len(parsed_blocks)} changes to shadow file: {file_path}")
145
-
146
- # 新增:执行代码质量检查
147
- lint_results = None
148
- lint_message = ""
149
- formatted_issues = ""
150
- has_lint_issues = False
151
-
152
- # 检查是否启用了Lint功能
153
- enable_lint = self.args.enable_auto_fix_lint
154
-
155
- if enable_lint:
156
- try:
157
- if self.shadow_linter and self.shadow_manager:
158
- # 对修改后的文件进行 lint 检查
159
- shadow_path = target_path # 已经是影子路径
160
- lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
161
-
162
- if lint_results and lint_results.issues:
163
- has_lint_issues = True
164
- # 格式化 lint 问题
165
- formatted_issues = self._format_lint_issues(lint_results)
166
- lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
167
- else:
168
- lint_message = "\n\n代码质量检查通过,未发现问题。"
169
- except Exception as e:
170
- logger.error(f"Lint 检查失败: {str(e)}")
171
- lint_message = "\n\n尝试进行代码质量检查时出错。"
172
- else:
173
- logger.info("代码质量检查已禁用")
174
-
175
- # 构建包含 lint 结果的返回消息
176
- if errors:
177
- message = get_message_with_format("replace_in_file.apply_success_with_warnings",
178
- applied=applied_count,
179
- total=len(parsed_blocks),
180
- file_path=file_path,
181
- errors="\n".join(errors))
182
- else:
183
- message = get_message_with_format("replace_in_file.apply_success",
184
- applied=applied_count,
185
- total=len(parsed_blocks),
186
- file_path=file_path)
187
-
188
- # 将 lint 消息添加到结果中,如果启用了Lint
189
- if enable_lint:
190
- message += lint_message
191
-
192
- # 变更跟踪,回调AgenticEdit
193
- if self.agent:
194
- rel_path = os.path.relpath(abs_file_path, abs_project_dir)
195
- self.agent.record_file_change(rel_path, "modified", diff=diff_content, content=current_content)
196
-
197
- # 附加 lint 结果到返回内容
198
- result_content = {
199
- "content": current_content,
200
- }
201
-
202
- # 只有在启用Lint时才添加Lint结果
203
- if enable_lint:
204
- result_content["lint_results"] = {
205
- "has_issues": has_lint_issues,
206
- "issues": formatted_issues if has_lint_issues else None
207
- }
208
-
209
- return ToolResult(success=True, message=message, content=result_content)
210
- except Exception as e:
211
- logger.error(f"Error writing replaced content to shadow file '{file_path}': {str(e)}")
212
- return ToolResult(success=False, message=get_message_with_format("replace_in_file.write_error", error=str(e)))
121
+
213
122
 
214
123
  def replace_in_file_normal(self, file_path: str, diff_content: str, source_dir: str, abs_project_dir: str, abs_file_path: str) -> ToolResult:
215
124
  """Replace content in file directly without using shadow manager"""
@@ -282,16 +191,20 @@ class ReplaceInFileToolResolver(BaseToolResolver):
282
191
 
283
192
  # 检查是否启用了Lint功能
284
193
  enable_lint = self.args.enable_auto_fix_lint
194
+ logger.info(f"检查Lint功能状态: enable_lint={enable_lint}")
285
195
 
286
196
  if enable_lint:
287
197
  try:
288
198
  if self.agent.linter:
289
199
  lint_results = self.agent.linter.lint_file(file_path)
290
200
  if lint_results and lint_results.issues:
291
- has_lint_issues = True
292
- # 格式化 lint 问题
293
- formatted_issues = self._format_lint_issues(lint_results)
294
- lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
201
+ # 过滤 lint 结果,只保留 ERROR 和 WARNING 级别的问题
202
+ filtered_results = self._filter_lint_issues(lint_results)
203
+ if filtered_results.issues:
204
+ has_lint_issues = True
205
+ # 格式化 lint 问题
206
+ formatted_issues = self._format_lint_issues(filtered_results)
207
+ lint_message = f"\n\n代码质量检查发现 {len(filtered_results.issues)} 个问题"
295
208
  except Exception as e:
296
209
  logger.error(f"Lint 检查失败: {str(e)}")
297
210
  lint_message = "\n\n尝试进行代码质量检查时出错。"
@@ -309,17 +222,13 @@ class ReplaceInFileToolResolver(BaseToolResolver):
309
222
  message = get_message_with_format("replace_in_file.apply_success",
310
223
  applied=applied_count,
311
224
  total=len(parsed_blocks),
312
- file_path=file_path)
313
-
314
- # 将 lint 消息添加到结果中,如果启用了Lint
315
- if enable_lint:
316
- message += lint_message
225
+ file_path=file_path)
317
226
 
318
227
  # 变更跟踪,回调AgenticEdit
319
228
  if self.agent:
320
229
  rel_path = os.path.relpath(abs_file_path, abs_project_dir)
321
230
  self.agent.record_file_change(rel_path, "modified", diff=diff_content, content=current_content)
322
-
231
+
323
232
  # 附加 lint 结果到返回内容
324
233
  result_content = {
325
234
  "content": current_content,
@@ -327,6 +236,7 @@ class ReplaceInFileToolResolver(BaseToolResolver):
327
236
 
328
237
  # 只有在启用Lint时才添加Lint结果
329
238
  if enable_lint:
239
+ message = message + "\n" + lint_message
330
240
  result_content["lint_results"] = {
331
241
  "has_issues": has_lint_issues,
332
242
  "issues": formatted_issues if has_lint_issues else None
@@ -348,9 +258,5 @@ class ReplaceInFileToolResolver(BaseToolResolver):
348
258
  # Security check
349
259
  if not abs_file_path.startswith(abs_project_dir):
350
260
  return ToolResult(success=False, message=get_message_with_format("replace_in_file.access_denied", file_path=file_path))
351
-
352
- # Choose the appropriate implementation based on whether shadow_manager is available
353
- if self.shadow_manager:
354
- return self.replace_in_file_with_shadow(file_path, diff_content, source_dir, abs_project_dir, abs_file_path)
355
- else:
356
- return self.replace_in_file_normal(file_path, diff_content, source_dir, abs_project_dir, abs_file_path)
261
+
262
+ return self.replace_in_file_normal(file_path, diff_content, source_dir, abs_project_dir, abs_file_path)
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import Dict, Any, Optional
2
+ from typing import Dict, Any, Optional, List
3
3
  from autocoder.common.v2.agent.agentic_edit_types import WriteToFileTool, ToolResult
4
4
  from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
5
5
  from loguru import logger
@@ -7,6 +7,7 @@ from autocoder.common import AutoCoderArgs
7
7
  from autocoder.common.auto_coder_lang import get_message_with_format
8
8
  from autocoder.common.file_checkpoint.models import FileChange as CheckpointFileChange
9
9
  from autocoder.common.file_checkpoint.manager import FileChangeManager as CheckpointFileChangeManager
10
+ from autocoder.linters.models import IssueSeverity, FileLintResult
10
11
  import typing
11
12
 
12
13
  if typing.TYPE_CHECKING:
@@ -20,7 +21,38 @@ class WriteToFileToolResolver(BaseToolResolver):
20
21
  self.shadow_manager = self.agent.shadow_manager if self.agent else None
21
22
  self.shadow_linter = self.agent.shadow_linter if self.agent else None
22
23
 
23
- def _format_lint_issues(self, lint_result):
24
+ def _filter_lint_issues(self, lint_result:FileLintResult, levels: List[IssueSeverity] = [IssueSeverity.ERROR, IssueSeverity.WARNING]):
25
+ """
26
+ 过滤 lint 结果,只保留指定级别的问题
27
+
28
+ 参数:
29
+ lint_result: 单个文件的 lint 结果对象
30
+ levels: 要保留的问题级别列表,默认保留 ERROR 和 WARNING 级别
31
+
32
+ 返回:
33
+ 过滤后的 lint 结果对象(原对象的副本)
34
+ """
35
+ if not lint_result or not lint_result.issues:
36
+ return lint_result
37
+
38
+ # 创建一个新的 issues 列表,只包含指定级别的问题
39
+ filtered_issues = []
40
+ for issue in lint_result.issues:
41
+ if issue.severity in levels:
42
+ filtered_issues.append(issue)
43
+
44
+ # 更新 lint_result 的副本
45
+ filtered_result = lint_result
46
+ filtered_result.issues = filtered_issues
47
+
48
+ # 更新计数
49
+ filtered_result.error_count = sum(1 for issue in filtered_issues if issue.severity == IssueSeverity.ERROR)
50
+ filtered_result.warning_count = sum(1 for issue in filtered_issues if issue.severity == IssueSeverity.WARNING)
51
+ filtered_result.info_count = sum(1 for issue in filtered_issues if issue.severity == IssueSeverity.INFO)
52
+
53
+ return filtered_result
54
+
55
+ def _format_lint_issues(self, lint_result:FileLintResult):
24
56
  """
25
57
  将 lint 结果格式化为可读的文本格式
26
58
 
@@ -43,74 +75,7 @@ class WriteToFileToolResolver(BaseToolResolver):
43
75
  )
44
76
 
45
77
  return "\n".join(formatted_issues)
46
-
47
- def write_file_with_shadow(self, file_path: str, content: str, source_dir: str, abs_project_dir: str, abs_file_path: str) -> ToolResult:
48
- """Write file using shadow manager for path translation"""
49
- try:
50
- shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
51
- # Ensure shadow directory exists
52
- os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
53
- with open(shadow_path, 'w', encoding='utf-8') as f:
54
- f.write(content)
55
- logger.info(f"[Shadow] Successfully wrote shadow file: {shadow_path}")
56
-
57
- # 回调AgenticEdit,记录变更
58
- if self.agent:
59
- rel_path = os.path.relpath(abs_file_path, abs_project_dir)
60
- self.agent.record_file_change(rel_path, "added", diff=None, content=content)
61
-
62
- # 新增:执行代码质量检查
63
- lint_results = None
64
- lint_message = ""
65
- formatted_issues = ""
66
- has_lint_issues = False
67
-
68
- # 检查是否启用了Lint功能
69
- enable_lint = self.args.enable_auto_fix_lint
70
-
71
- if enable_lint:
72
- try:
73
- if self.shadow_linter and self.shadow_manager:
74
- # 对新创建的文件进行 lint 检查
75
- shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
76
- lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
77
-
78
- if lint_results and lint_results.issues:
79
- has_lint_issues = True
80
- # 格式化 lint 问题
81
- formatted_issues = self._format_lint_issues(lint_results)
82
- lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
83
- else:
84
- lint_message = "\n\n代码质量检查通过,未发现问题。"
85
- except Exception as e:
86
- logger.error(f"Lint 检查失败: {str(e)}")
87
- lint_message = "\n\n尝试进行代码质量检查时出错。"
88
- else:
89
- logger.info("代码质量检查已禁用")
90
-
91
- # 构建包含 lint 结果的返回消息
92
- message = f"Successfully wrote to file (shadow): {file_path}"
93
-
94
- # 将 lint 消息添加到结果中,如果启用了Lint
95
- if enable_lint:
96
- message += lint_message
97
-
98
- # 附加 lint 结果到返回内容
99
- result_content = {
100
- "content": content,
101
- }
102
-
103
- # 只有在启用Lint时才添加Lint结果
104
- if enable_lint:
105
- result_content["lint_results"] = {
106
- "has_issues": has_lint_issues,
107
- "issues": formatted_issues if has_lint_issues else None
108
- }
109
-
110
- return ToolResult(success=True, message=message, content=result_content)
111
- except Exception as e:
112
- logger.error(f"Error writing to shadow file '{file_path}': {str(e)}")
113
- return ToolResult(success=False, message=f"An error occurred while writing to the shadow file: {str(e)}")
78
+
114
79
 
115
80
  def write_file_normal(self, file_path: str, content: str, source_dir: str, abs_project_dir: str, abs_file_path: str) -> ToolResult:
116
81
  """Write file directly without using shadow manager"""
@@ -153,31 +118,17 @@ class WriteToFileToolResolver(BaseToolResolver):
153
118
  enable_lint = self.args.enable_auto_fix_lint
154
119
 
155
120
  if enable_lint:
156
- try:
157
- if self.shadow_linter and self.shadow_manager:
158
- # 对新创建的文件进行 lint 检查
159
- # 由于没有shadow系统,需要先创建shadow文件
160
- shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
161
- os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
162
- with open(shadow_path, 'w', encoding='utf-8') as f:
163
- f.write(content)
164
-
165
- lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
166
-
167
- if lint_results and lint_results.issues:
168
- has_lint_issues = True
169
- # 格式化 lint 问题
170
- formatted_issues = self._format_lint_issues(lint_results)
171
- lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
172
- else:
173
- lint_message = "\n\n代码质量检查通过,未发现问题。"
121
+ try:
174
122
  if self.agent.linter:
175
123
  lint_results = self.agent.linter.lint_file(file_path)
176
124
  if lint_results and lint_results.issues:
177
- has_lint_issues = True
178
- # 格式化 lint 问题
179
- formatted_issues = self._format_lint_issues(lint_results)
180
- lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
125
+ # 过滤 lint 结果,只保留 ERROR 和 WARNING 级别的问题
126
+ filtered_results = self._filter_lint_issues(lint_results)
127
+ if filtered_results.issues:
128
+ has_lint_issues = True
129
+ # 格式化 lint 问题
130
+ formatted_issues = self._format_lint_issues(filtered_results)
131
+ lint_message = f"\n\n代码质量检查发现 {len(filtered_results.issues)} 个问题"
181
132
  except Exception as e:
182
133
  logger.error(f"Lint 检查失败: {str(e)}")
183
134
  lint_message = "\n\n尝试进行代码质量检查时出错。"
@@ -186,10 +137,7 @@ class WriteToFileToolResolver(BaseToolResolver):
186
137
 
187
138
  # 构建包含 lint 结果的返回消息
188
139
  message = f"{file_path}"
189
-
190
- # 将 lint 消息添加到结果中,如果启用了Lint
191
- if enable_lint:
192
- message += lint_message
140
+
193
141
 
194
142
  # 附加 lint 结果到返回内容
195
143
  result_content = {
@@ -198,6 +146,7 @@ class WriteToFileToolResolver(BaseToolResolver):
198
146
 
199
147
  # 只有在启用Lint时才添加Lint结果
200
148
  if enable_lint:
149
+ message = message + "\n" + lint_message
201
150
  result_content["lint_results"] = {
202
151
  "has_issues": has_lint_issues,
203
152
  "issues": formatted_issues if has_lint_issues else None
@@ -219,9 +168,5 @@ class WriteToFileToolResolver(BaseToolResolver):
219
168
  # Security check: ensure the path is within the source directory
220
169
  if not abs_file_path.startswith(abs_project_dir):
221
170
  return ToolResult(success=False, message=f"Error: Access denied. Attempted to write file outside the project directory: {file_path}")
222
-
223
- # Choose the appropriate implementation based on whether shadow_manager is available
224
- if self.shadow_manager:
225
- return self.write_file_with_shadow(file_path, content, source_dir, abs_project_dir, abs_file_path)
226
- else:
227
- return self.write_file_normal(file_path, content, source_dir, abs_project_dir, abs_file_path)
171
+
172
+ return self.write_file_normal(file_path, content, source_dir, abs_project_dir, abs_file_path)
autocoder/index/index.py CHANGED
@@ -665,7 +665,7 @@ class IndexManager:
665
665
  md5=data["md5"],
666
666
  )
667
667
  index_items.append(index_item)
668
- except (KeyError, TypeError) as e:
668
+ except Exception as e:
669
669
  logger.warning(f"处理索引条目 {module_name} 时出错: {str(e)}")
670
670
  logger.exception(e)
671
671
  continue
@@ -9,6 +9,7 @@ from autocoder.linters.base_linter import BaseLinter
9
9
  from autocoder.linters.reactjs_linter import ReactJSLinter
10
10
  from autocoder.linters.vue_linter import VueLinter
11
11
  from autocoder.linters.python_linter import PythonLinter
12
+ from loguru import logger
12
13
 
13
14
  class LinterFactory:
14
15
  """
@@ -115,9 +116,11 @@ class LinterFactory:
115
116
  Returns:
116
117
  Dict[str, Any]: Lint results.
117
118
  """
119
+ logger.info(f"根据文件路径{file_path}自动查找 linter")
118
120
  linter = cls.create_linter(file_path=file_path, verbose=verbose)
121
+ logger.info(f"Linting file: {file_path} with linter: {linter.__class__.__name__}")
119
122
  if linter is None:
120
- return None
123
+ return None
121
124
  return linter.lint_file(file_path, fix=fix)
122
125
 
123
126
  @classmethod
@@ -72,10 +72,8 @@ class NormalLinter:
72
72
 
73
73
  # 将原始结果转换为Pydantic模型
74
74
  return self._convert_raw_lint_result(raw_lint_result, file_path)
75
- except Exception as e:
76
- if self.verbose:
77
- print(f"检查 {file_path} 时出错: {str(e)}")
78
-
75
+ except Exception as e:
76
+ self.logger.exception(f"检查 {file_path} 时出错: {e}")
79
77
  language = self._detect_language(file_path)
80
78
  return FileLintResult(
81
79
  file_path=file_path,
@@ -1,8 +1,3 @@
1
- """
2
- Module for linting Python code.
3
- This module provides functionality to analyze Python code for quality and best practices.
4
- """
5
-
6
1
  import os
7
2
  import sys
8
3
  import json
@@ -25,8 +20,7 @@ class PythonLinter(BaseLinter):
25
20
  Args:
26
21
  verbose (bool): Whether to display verbose output.
27
22
  """
28
- super().__init__(verbose)
29
- BaseLinter.tt()
23
+ super().__init__(verbose)
30
24
 
31
25
  def get_supported_extensions(self) -> List[str]:
32
26
  """
@@ -50,8 +44,7 @@ class PythonLinter(BaseLinter):
50
44
 
51
45
  # Check for pylint or flake8
52
46
  has_pylint = False
53
- has_flake8 = False
54
- has_black = False
47
+ has_flake8 = False
55
48
 
56
49
  try:
57
50
  subprocess.run([sys.executable, "-m", "pylint", "--version"],
@@ -67,18 +60,10 @@ class PythonLinter(BaseLinter):
67
60
  has_flake8 = True
68
61
  except (subprocess.SubprocessError, FileNotFoundError):
69
62
  if self.verbose:
70
- print("Flake8 not found.")
71
-
72
- try:
73
- subprocess.run([sys.executable, "-m", "black", "--version"],
74
- check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
75
- has_black = True
76
- except (subprocess.SubprocessError, FileNotFoundError):
77
- if self.verbose:
78
- print("Black not found.")
63
+ print("Flake8 not found.")
79
64
 
80
65
  # Need at least one linter
81
- return has_pylint or has_flake8 or has_black
66
+ return has_pylint or has_flake8
82
67
 
83
68
  except (subprocess.SubprocessError, FileNotFoundError):
84
69
  return False
@@ -173,12 +158,15 @@ class PythonLinter(BaseLinter):
173
158
 
174
159
  # Process pylint issues
175
160
  for message in pylint_output:
176
- severity = "warning"
161
+ severity = "info"
177
162
  if message.get('type') in ['error', 'fatal']:
178
163
  severity = "error"
179
164
  result['error_count'] += 1
180
- else:
165
+ elif message.get('type') in ['warning']:
166
+ severity = "warning"
181
167
  result['warning_count'] += 1
168
+ else:
169
+ result['info_count'] += 1
182
170
 
183
171
  issue = {
184
172
  'file': message.get('path', ''),
@@ -265,13 +253,16 @@ class PythonLinter(BaseLinter):
265
253
  message = code_message
266
254
 
267
255
  # Determine severity based on error code
268
- severity = "warning"
256
+ severity = "info"
269
257
  # E errors are generally more serious than F warnings
270
- if code.startswith('E'):
258
+ if code.startswith('F'):
271
259
  severity = "error"
272
260
  result['error_count'] += 1
273
- else:
261
+ elif code.startswith('E'):
262
+ severity = "warning"
274
263
  result['warning_count'] += 1
264
+ else:
265
+ result['info_count'] += 1
275
266
 
276
267
  issue = {
277
268
  'file': file_path,
@@ -294,76 +285,7 @@ class PythonLinter(BaseLinter):
294
285
 
295
286
  return result
296
287
 
297
- def _run_black(self, target: str, fix: bool) -> Dict[str, Any]:
298
- """
299
- Run black on the target file or directory to check formatting or fix it.
300
-
301
- Args:
302
- target (str): Path to the file or directory to format.
303
- fix (bool): Whether to automatically fix formatting issues.
304
-
305
- Returns:
306
- Dict[str, Any]: The black results.
307
- """
308
- result = {
309
- 'error_count': 0,
310
- 'warning_count': 0,
311
- 'issues': []
312
- }
313
-
314
- try:
315
- # Build command
316
- cmd = [
317
- sys.executable,
318
- "-m",
319
- "black",
320
- ]
321
-
322
- # Check-only mode if not fixing
323
- if not fix:
324
- cmd.append("--check")
325
-
326
- # Add target
327
- cmd.append(target)
328
-
329
- process = subprocess.run(
330
- cmd,
331
- stdout=subprocess.PIPE,
332
- stderr=subprocess.PIPE,
333
- text=True
334
- )
335
-
336
- # Black exit code is 0 if no changes, 1 if changes were needed
337
- if process.returncode == 1 and not fix:
338
- # Parse output to find which files would be reformatted
339
- for line in process.stdout.splitlines():
340
- if line.startswith("would reformat"):
341
- file_path = line.replace("would reformat ", "").strip()
342
-
343
- result['warning_count'] += 1
344
-
345
- issue = {
346
- 'file': file_path,
347
- 'line': 0, # Black doesn't provide line numbers
348
- 'column': 0,
349
- 'severity': "warning",
350
- 'message': "Code formatting doesn't match Black style",
351
- 'rule': "formatting",
352
- 'tool': 'black'
353
- }
354
-
355
- result['issues'].append(issue)
356
-
357
- # If auto-fixing and Black reports changes
358
- if fix and process.returncode == 0 and "reformatted" in process.stderr:
359
- # This is good - it means Black fixed some issues
360
- pass
361
-
362
- except Exception as e:
363
- if self.verbose:
364
- print(f"Error running black: {str(e)}")
365
-
366
- return result
288
+
367
289
 
368
290
  def lint_file(self, file_path: str, fix: bool = False) -> Dict[str, Any]:
369
291
  """
@@ -400,17 +322,7 @@ class PythonLinter(BaseLinter):
400
322
  # Try to install dependencies
401
323
  if not self._install_dependencies_if_needed():
402
324
  result['error'] = "Required dependencies are not installed and could not be installed automatically"
403
- return result
404
-
405
- # Run black first (to format if fix=True)
406
- try:
407
- black_result = self._run_black(file_path, fix)
408
- result['issues'].extend(black_result['issues'])
409
- result['error_count'] += black_result['error_count']
410
- result['warning_count'] += black_result['warning_count']
411
- except Exception as e:
412
- if self.verbose:
413
- print(f"Error running black: {str(e)}")
325
+ return result
414
326
 
415
327
  # Run pylint
416
328
  try:
@@ -477,16 +389,7 @@ class PythonLinter(BaseLinter):
477
389
  python_files.append(os.path.join(root, file))
478
390
 
479
391
  result['files_analyzed'] = len(python_files)
480
-
481
- # Run black first (to format if fix=True)
482
- try:
483
- black_result = self._run_black(project_path, fix)
484
- result['issues'].extend(black_result['issues'])
485
- result['error_count'] += black_result['error_count']
486
- result['warning_count'] += black_result['warning_count']
487
- except Exception as e:
488
- if self.verbose:
489
- print(f"Error running black: {str(e)}")
392
+
490
393
 
491
394
  # Run pylint
492
395
  try:
autocoder/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
 
2
- __version__ = "0.1.373"
2
+ __version__ = "0.1.374"