auto-coder 0.1.362__py3-none-any.whl → 0.1.363__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.
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/METADATA +1 -1
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/RECORD +46 -18
- autocoder/agent/base_agentic/__init__.py +0 -0
- autocoder/agent/base_agentic/agent_hub.py +169 -0
- autocoder/agent/base_agentic/agentic_lang.py +112 -0
- autocoder/agent/base_agentic/agentic_tool_display.py +180 -0
- autocoder/agent/base_agentic/base_agent.py +1582 -0
- autocoder/agent/base_agentic/default_tools.py +683 -0
- autocoder/agent/base_agentic/test_base_agent.py +82 -0
- autocoder/agent/base_agentic/tool_registry.py +425 -0
- autocoder/agent/base_agentic/tools/__init__.py +12 -0
- autocoder/agent/base_agentic/tools/ask_followup_question_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/attempt_completion_tool_resolver.py +37 -0
- autocoder/agent/base_agentic/tools/base_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/example_tool_resolver.py +46 -0
- autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +72 -0
- autocoder/agent/base_agentic/tools/list_files_tool_resolver.py +110 -0
- autocoder/agent/base_agentic/tools/plan_mode_respond_tool_resolver.py +35 -0
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +54 -0
- autocoder/agent/base_agentic/tools/replace_in_file_tool_resolver.py +156 -0
- autocoder/agent/base_agentic/tools/search_files_tool_resolver.py +134 -0
- autocoder/agent/base_agentic/tools/talk_to_group_tool_resolver.py +96 -0
- autocoder/agent/base_agentic/tools/talk_to_tool_resolver.py +79 -0
- autocoder/agent/base_agentic/tools/use_mcp_tool_resolver.py +44 -0
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +58 -0
- autocoder/agent/base_agentic/types.py +189 -0
- autocoder/agent/base_agentic/utils.py +100 -0
- autocoder/auto_coder_runner.py +4 -4
- autocoder/chat/conf_command.py +11 -10
- autocoder/common/rulefiles/autocoderrules_utils.py +24 -0
- autocoder/common/save_formatted_log.py +1 -1
- autocoder/common/v2/agent/agentic_edit.py +21 -19
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +73 -1
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +132 -4
- autocoder/common/v2/agent/agentic_edit_types.py +1 -2
- autocoder/common/v2/agent/agentic_tool_display.py +2 -3
- autocoder/rag/long_context_rag.py +424 -397
- autocoder/rag/test_doc_filter.py +393 -0
- autocoder/rag/test_long_context_rag.py +473 -0
- autocoder/rag/test_token_limiter.py +342 -0
- autocoder/shadows/shadow_manager.py +1 -3
- autocoder/version.py +1 -1
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.362.dist-info → auto_coder-0.1.363.dist-info}/top_level.txt +0 -0
|
@@ -72,7 +72,6 @@ from autocoder.common.v2.agent.agentic_edit_types import (AgenticEditRequest, To
|
|
|
72
72
|
AskFollowupQuestionTool, UseMcpTool, AttemptCompletionTool
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
|
|
76
75
|
# Map Pydantic Tool Models to their Resolver Classes
|
|
77
76
|
TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
78
77
|
ExecuteCommandTool: ExecuteCommandToolResolver,
|
|
@@ -86,7 +85,7 @@ TOOL_RESOLVER_MAP: Dict[Type[BaseTool], Type[BaseToolResolver]] = {
|
|
|
86
85
|
AskFollowupQuestionTool: AskFollowupQuestionToolResolver,
|
|
87
86
|
AttemptCompletionTool: AttemptCompletionToolResolver, # Will stop the loop anyway
|
|
88
87
|
PlanModeRespondTool: PlanModeRespondToolResolver,
|
|
89
|
-
UseMcpTool: UseMcpToolResolver
|
|
88
|
+
UseMcpTool: UseMcpToolResolver
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
|
|
@@ -233,7 +232,7 @@ class AgenticEdit:
|
|
|
233
232
|
# Tools
|
|
234
233
|
|
|
235
234
|
## execute_command
|
|
236
|
-
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory:
|
|
235
|
+
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: {{current_project}}
|
|
237
236
|
Parameters:
|
|
238
237
|
- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
|
|
239
238
|
- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations.
|
|
@@ -255,7 +254,7 @@ class AgenticEdit:
|
|
|
255
254
|
## read_file
|
|
256
255
|
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.
|
|
257
256
|
Parameters:
|
|
258
|
-
- path: (required) The path of the file to read (relative to the current working directory
|
|
257
|
+
- path: (required) The path of the file to read (relative to the current working directory {{ current_project }})
|
|
259
258
|
Usage:
|
|
260
259
|
<read_file>
|
|
261
260
|
<path>File path here</path>
|
|
@@ -264,7 +263,7 @@ class AgenticEdit:
|
|
|
264
263
|
## write_to_file
|
|
265
264
|
Description: Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.
|
|
266
265
|
Parameters:
|
|
267
|
-
- path: (required) The path of the file to write to (relative to the current working directory
|
|
266
|
+
- path: (required) The path of the file to write to (relative to the current working directory {{ current_project }})
|
|
268
267
|
- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified.
|
|
269
268
|
Usage:
|
|
270
269
|
<write_to_file>
|
|
@@ -277,15 +276,15 @@ class AgenticEdit:
|
|
|
277
276
|
## replace_in_file
|
|
278
277
|
Description: Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.
|
|
279
278
|
Parameters:
|
|
280
|
-
- path: (required) The path of the file to modify (relative to the current working directory
|
|
279
|
+
- path: (required) The path of the file to modify (relative to the current working directory {{ current_project }})
|
|
281
280
|
- diff: (required) One or more SEARCH/REPLACE blocks following this exact format:
|
|
282
|
-
|
|
281
|
+
```
|
|
283
282
|
<<<<<<< SEARCH
|
|
284
283
|
[exact content to find]
|
|
285
284
|
=======
|
|
286
285
|
[new content to replace with]
|
|
287
286
|
>>>>>>> REPLACE
|
|
288
|
-
|
|
287
|
+
```
|
|
289
288
|
Critical rules:
|
|
290
289
|
1. SEARCH content must match the associated file section to find EXACTLY:
|
|
291
290
|
* Match character-for-character including whitespace, indentation, line endings
|
|
@@ -313,7 +312,7 @@ class AgenticEdit:
|
|
|
313
312
|
## search_files
|
|
314
313
|
Description: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.
|
|
315
314
|
Parameters:
|
|
316
|
-
- path: (required) The path of the directory to search in (relative to the current working directory
|
|
315
|
+
- path: (required) The path of the directory to search in (relative to the current working directory {{ current_project }}). This directory will be recursively searched.
|
|
317
316
|
- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.
|
|
318
317
|
- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).
|
|
319
318
|
Usage:
|
|
@@ -326,7 +325,7 @@ class AgenticEdit:
|
|
|
326
325
|
## list_files
|
|
327
326
|
Description: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.
|
|
328
327
|
Parameters:
|
|
329
|
-
- path: (required) The path of the directory to list contents for (relative to the current working directory
|
|
328
|
+
- path: (required) The path of the directory to list contents for (relative to the current working directory {{ current_project }})
|
|
330
329
|
- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.
|
|
331
330
|
Usage:
|
|
332
331
|
<list_files>
|
|
@@ -337,7 +336,7 @@ class AgenticEdit:
|
|
|
337
336
|
## list_code_definition_names
|
|
338
337
|
Description: Request to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.
|
|
339
338
|
Parameters:
|
|
340
|
-
- path: (required) The path of the directory (relative to the current working directory
|
|
339
|
+
- path: (required) The path of the directory (relative to the current working directory {{ current_project }}) to list top level source code definitions for.
|
|
341
340
|
Usage:
|
|
342
341
|
<list_code_definition_names>
|
|
343
342
|
<path>Directory path here</path>
|
|
@@ -636,7 +635,7 @@ class AgenticEdit:
|
|
|
636
635
|
- Your current working directory is: {{current_project}}
|
|
637
636
|
- You cannot \`cd\` into a different directory to complete a task. You are stuck operating from '{{ current_project }}', so be sure to pass in the correct 'path' parameter when using tools that require a path.
|
|
638
637
|
- Do not use the ~ character or $HOME to refer to the home directory.
|
|
639
|
-
- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '
|
|
638
|
+
- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '{{ current_project }}', and if so prepend with \`cd\`'ing into that directory && then executing the command (as one command since you are stuck operating from '{{ current_project }}'). For example, if you needed to run \`npm install\` in a project outside of '{{ current_project }}', you would need to prepend with a \`cd\` i.e. pseudocode for this would be \`cd (path to project) && (command, in this case npm install)\`.
|
|
640
639
|
- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using replace_in_file to make informed changes.
|
|
641
640
|
- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when creating files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.
|
|
642
641
|
- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.
|
|
@@ -813,17 +812,18 @@ class AgenticEdit:
|
|
|
813
812
|
|
|
814
813
|
assistant_buffer = ""
|
|
815
814
|
logger.info("Initializing stream chat with LLM")
|
|
815
|
+
|
|
816
|
+
## 实际请求大模型
|
|
816
817
|
llm_response_gen = stream_chat_with_continue(
|
|
817
818
|
llm=self.llm,
|
|
818
819
|
conversations=conversations,
|
|
819
820
|
llm_config={}, # Placeholder for future LLM configs
|
|
820
821
|
args=self.args
|
|
821
822
|
)
|
|
822
|
-
|
|
823
|
-
meta_holder = byzerllm.MetaHolder()
|
|
823
|
+
|
|
824
824
|
logger.info("Starting to parse LLM response stream")
|
|
825
825
|
parsed_events = self.stream_and_parse_llm_response(
|
|
826
|
-
llm_response_gen
|
|
826
|
+
llm_response_gen)
|
|
827
827
|
|
|
828
828
|
event_count = 0
|
|
829
829
|
for event in parsed_events:
|
|
@@ -942,8 +942,9 @@ class AgenticEdit:
|
|
|
942
942
|
# logger.error("Stopping analyze loop due to parsing error.")
|
|
943
943
|
# return
|
|
944
944
|
|
|
945
|
-
|
|
946
|
-
|
|
945
|
+
elif isinstance(event, TokenUsageEvent):
|
|
946
|
+
logger.info("Yielding token usage event")
|
|
947
|
+
yield event
|
|
947
948
|
|
|
948
949
|
if not tool_executed:
|
|
949
950
|
# No tool executed in this LLM response cycle
|
|
@@ -973,7 +974,7 @@ class AgenticEdit:
|
|
|
973
974
|
logger.info(f"AgenticEdit analyze loop finished after {iteration_count} iterations.")
|
|
974
975
|
|
|
975
976
|
def stream_and_parse_llm_response(
|
|
976
|
-
self, generator: Generator[Tuple[str, Any], None, None]
|
|
977
|
+
self, generator: Generator[Tuple[str, Any], None, None]
|
|
977
978
|
) -> Generator[Union[LLMOutputEvent, LLMThinkingEvent, ToolCallEvent, ErrorEvent], None, None]:
|
|
978
979
|
"""
|
|
979
980
|
Streamingly parses the LLM response generator, distinguishing between
|
|
@@ -1050,7 +1051,8 @@ class AgenticEdit:
|
|
|
1050
1051
|
logger.exception(
|
|
1051
1052
|
f"Failed to parse tool XML for <{tool_tag}>: {e}\nXML:\n{tool_xml}")
|
|
1052
1053
|
return None
|
|
1053
|
-
|
|
1054
|
+
|
|
1055
|
+
meta_holder = byzerllm.MetaHolder()
|
|
1054
1056
|
for content_chunk, metadata in generator:
|
|
1055
1057
|
global_cancel.check_and_raise(token=self.args.event_file)
|
|
1056
1058
|
meta_holder.meta = metadata
|
|
@@ -14,7 +14,9 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
14
14
|
def __init__(self, agent: Optional['AgenticEdit'], tool: ReplaceInFileTool, args: AutoCoderArgs):
|
|
15
15
|
super().__init__(agent, tool, args)
|
|
16
16
|
self.tool: ReplaceInFileTool = tool # For type hinting
|
|
17
|
+
self.args = args
|
|
17
18
|
self.shadow_manager = self.agent.shadow_manager if self.agent else None
|
|
19
|
+
self.shadow_linter = self.agent.shadow_linter if self.agent else None
|
|
18
20
|
|
|
19
21
|
def parse_diff(self, diff_content: str) -> List[Tuple[str, str]]:
|
|
20
22
|
"""
|
|
@@ -58,6 +60,30 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
58
60
|
logger.warning(f"Could not parse any SEARCH/REPLACE blocks from diff: {diff_content}")
|
|
59
61
|
return blocks
|
|
60
62
|
|
|
63
|
+
def _format_lint_issues(self, lint_result):
|
|
64
|
+
"""
|
|
65
|
+
将 lint 结果格式化为可读的文本格式
|
|
66
|
+
|
|
67
|
+
参数:
|
|
68
|
+
lint_result: 单个文件的 lint 结果对象
|
|
69
|
+
|
|
70
|
+
返回:
|
|
71
|
+
str: 格式化的问题描述
|
|
72
|
+
"""
|
|
73
|
+
formatted_issues = []
|
|
74
|
+
|
|
75
|
+
for issue in lint_result.issues:
|
|
76
|
+
severity = "错误" if issue.severity.value == 3 else "警告" if issue.severity.value == 2 else "信息"
|
|
77
|
+
line_info = f"第{issue.position.line}行"
|
|
78
|
+
if issue.position.column:
|
|
79
|
+
line_info += f", 第{issue.position.column}列"
|
|
80
|
+
|
|
81
|
+
formatted_issues.append(
|
|
82
|
+
f" - [{severity}] {line_info}: {issue.message} (规则: {issue.code})"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return "\n".join(formatted_issues)
|
|
86
|
+
|
|
61
87
|
def resolve(self) -> ToolResult:
|
|
62
88
|
file_path = self.tool.path
|
|
63
89
|
diff_content = self.tool.diff
|
|
@@ -130,6 +156,36 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
130
156
|
f.write(current_content)
|
|
131
157
|
logger.info(f"Successfully applied {applied_count}/{len(parsed_blocks)} changes to file: {file_path}")
|
|
132
158
|
|
|
159
|
+
# 新增:执行代码质量检查
|
|
160
|
+
lint_results = None
|
|
161
|
+
lint_message = ""
|
|
162
|
+
formatted_issues = ""
|
|
163
|
+
has_lint_issues = False
|
|
164
|
+
|
|
165
|
+
# 检查是否启用了Lint功能
|
|
166
|
+
enable_lint = self.args.enable_auto_fix_lint
|
|
167
|
+
|
|
168
|
+
if enable_lint:
|
|
169
|
+
try:
|
|
170
|
+
if self.shadow_linter and self.shadow_manager:
|
|
171
|
+
# 对修改后的文件进行 lint 检查
|
|
172
|
+
shadow_path = target_path # 已经是影子路径
|
|
173
|
+
lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
|
|
174
|
+
|
|
175
|
+
if lint_results and lint_results.issues:
|
|
176
|
+
has_lint_issues = True
|
|
177
|
+
# 格式化 lint 问题
|
|
178
|
+
formatted_issues = self._format_lint_issues(lint_results)
|
|
179
|
+
lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
|
|
180
|
+
else:
|
|
181
|
+
lint_message = "\n\n代码质量检查通过,未发现问题。"
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Lint 检查失败: {str(e)}")
|
|
184
|
+
lint_message = "\n\n尝试进行代码质量检查时出错。"
|
|
185
|
+
else:
|
|
186
|
+
logger.info("代码质量检查已禁用")
|
|
187
|
+
|
|
188
|
+
# 构建包含 lint 结果的返回消息
|
|
133
189
|
if errors:
|
|
134
190
|
message = get_message_with_format("replace_in_file.apply_success_with_warnings",
|
|
135
191
|
applied=applied_count,
|
|
@@ -141,13 +197,29 @@ class ReplaceInFileToolResolver(BaseToolResolver):
|
|
|
141
197
|
applied=applied_count,
|
|
142
198
|
total=len(parsed_blocks),
|
|
143
199
|
file_path=file_path)
|
|
200
|
+
|
|
201
|
+
# 将 lint 消息添加到结果中,如果启用了Lint
|
|
202
|
+
if enable_lint:
|
|
203
|
+
message += lint_message
|
|
144
204
|
|
|
145
205
|
# 变更跟踪,回调AgenticEdit
|
|
146
206
|
if self.agent:
|
|
147
207
|
rel_path = os.path.relpath(abs_file_path, abs_project_dir)
|
|
148
208
|
self.agent.record_file_change(rel_path, "modified", diff=diff_content, content=current_content)
|
|
149
209
|
|
|
150
|
-
|
|
210
|
+
# 附加 lint 结果到返回内容
|
|
211
|
+
result_content = {
|
|
212
|
+
"content": current_content,
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# 只有在启用Lint时才添加Lint结果
|
|
216
|
+
if enable_lint:
|
|
217
|
+
result_content["lint_results"] = {
|
|
218
|
+
"has_issues": has_lint_issues,
|
|
219
|
+
"issues": formatted_issues if has_lint_issues else None
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return ToolResult(success=True, message=message, content=result_content)
|
|
151
223
|
except Exception as e:
|
|
152
224
|
logger.error(f"Error writing replaced content to file '{file_path}': {str(e)}")
|
|
153
225
|
return ToolResult(success=False, message=get_message_with_format("replace_in_file.write_error", error=str(e)))
|
|
@@ -4,6 +4,7 @@ from autocoder.common.v2.agent.agentic_edit_types import WriteToFileTool, ToolRe
|
|
|
4
4
|
from autocoder.common.v2.agent.agentic_edit_tools.base_tool_resolver import BaseToolResolver
|
|
5
5
|
from loguru import logger
|
|
6
6
|
from autocoder.common import AutoCoderArgs
|
|
7
|
+
from autocoder.common.auto_coder_lang import get_message_with_format
|
|
7
8
|
import typing
|
|
8
9
|
|
|
9
10
|
if typing.TYPE_CHECKING:
|
|
@@ -13,7 +14,33 @@ class WriteToFileToolResolver(BaseToolResolver):
|
|
|
13
14
|
def __init__(self, agent: Optional['AgenticEdit'], tool: WriteToFileTool, args: AutoCoderArgs):
|
|
14
15
|
super().__init__(agent, tool, args)
|
|
15
16
|
self.tool: WriteToFileTool = tool # For type hinting
|
|
17
|
+
self.args = args
|
|
16
18
|
self.shadow_manager = self.agent.shadow_manager if self.agent else None
|
|
19
|
+
self.shadow_linter = self.agent.shadow_linter if self.agent else None
|
|
20
|
+
|
|
21
|
+
def _format_lint_issues(self, lint_result):
|
|
22
|
+
"""
|
|
23
|
+
将 lint 结果格式化为可读的文本格式
|
|
24
|
+
|
|
25
|
+
参数:
|
|
26
|
+
lint_result: 单个文件的 lint 结果对象
|
|
27
|
+
|
|
28
|
+
返回:
|
|
29
|
+
str: 格式化的问题描述
|
|
30
|
+
"""
|
|
31
|
+
formatted_issues = []
|
|
32
|
+
|
|
33
|
+
for issue in lint_result.issues:
|
|
34
|
+
severity = "错误" if issue.severity.value == 3 else "警告" if issue.severity.value == 2 else "信息"
|
|
35
|
+
line_info = f"第{issue.position.line}行"
|
|
36
|
+
if issue.position.column:
|
|
37
|
+
line_info += f", 第{issue.position.column}列"
|
|
38
|
+
|
|
39
|
+
formatted_issues.append(
|
|
40
|
+
f" - [{severity}] {line_info}: {issue.message} (规则: {issue.code})"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return "\n".join(formatted_issues)
|
|
17
44
|
|
|
18
45
|
def resolve(self) -> ToolResult:
|
|
19
46
|
file_path = self.tool.path
|
|
@@ -39,8 +66,56 @@ class WriteToFileToolResolver(BaseToolResolver):
|
|
|
39
66
|
if self.agent:
|
|
40
67
|
rel_path = os.path.relpath(abs_file_path, abs_project_dir)
|
|
41
68
|
self.agent.record_file_change(rel_path, "added", diff=None, content=content)
|
|
42
|
-
|
|
43
|
-
|
|
69
|
+
|
|
70
|
+
# 新增:执行代码质量检查
|
|
71
|
+
lint_results = None
|
|
72
|
+
lint_message = ""
|
|
73
|
+
formatted_issues = ""
|
|
74
|
+
has_lint_issues = False
|
|
75
|
+
|
|
76
|
+
# 检查是否启用了Lint功能
|
|
77
|
+
enable_lint = self.args.enable_auto_fix_lint
|
|
78
|
+
|
|
79
|
+
if enable_lint:
|
|
80
|
+
try:
|
|
81
|
+
if self.shadow_linter and self.shadow_manager:
|
|
82
|
+
# 对新创建的文件进行 lint 检查
|
|
83
|
+
shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
|
|
84
|
+
lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
|
|
85
|
+
|
|
86
|
+
if lint_results and lint_results.issues:
|
|
87
|
+
has_lint_issues = True
|
|
88
|
+
# 格式化 lint 问题
|
|
89
|
+
formatted_issues = self._format_lint_issues(lint_results)
|
|
90
|
+
lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
|
|
91
|
+
else:
|
|
92
|
+
lint_message = "\n\n代码质量检查通过,未发现问题。"
|
|
93
|
+
except Exception as e:
|
|
94
|
+
logger.error(f"Lint 检查失败: {str(e)}")
|
|
95
|
+
lint_message = "\n\n尝试进行代码质量检查时出错。"
|
|
96
|
+
else:
|
|
97
|
+
logger.info("代码质量检查已禁用")
|
|
98
|
+
|
|
99
|
+
# 构建包含 lint 结果的返回消息
|
|
100
|
+
message = f"Successfully wrote to file (shadow): {file_path}"
|
|
101
|
+
|
|
102
|
+
# 将 lint 消息添加到结果中,如果启用了Lint
|
|
103
|
+
if enable_lint:
|
|
104
|
+
message += lint_message
|
|
105
|
+
|
|
106
|
+
# 附加 lint 结果到返回内容
|
|
107
|
+
result_content = {
|
|
108
|
+
"content": content,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# 只有在启用Lint时才添加Lint结果
|
|
112
|
+
if enable_lint:
|
|
113
|
+
result_content["lint_results"] = {
|
|
114
|
+
"has_issues": has_lint_issues,
|
|
115
|
+
"issues": formatted_issues if has_lint_issues else None
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return ToolResult(success=True, message=message, content=result_content)
|
|
44
119
|
else:
|
|
45
120
|
# No shadow manager fallback to original file
|
|
46
121
|
os.makedirs(os.path.dirname(abs_file_path), exist_ok=True)
|
|
@@ -51,8 +126,61 @@ class WriteToFileToolResolver(BaseToolResolver):
|
|
|
51
126
|
if self.agent:
|
|
52
127
|
rel_path = os.path.relpath(abs_file_path, abs_project_dir)
|
|
53
128
|
self.agent.record_file_change(rel_path, "added", diff=None, content=content)
|
|
54
|
-
|
|
55
|
-
|
|
129
|
+
|
|
130
|
+
# 新增:执行代码质量检查
|
|
131
|
+
lint_results = None
|
|
132
|
+
lint_message = ""
|
|
133
|
+
formatted_issues = ""
|
|
134
|
+
has_lint_issues = False
|
|
135
|
+
|
|
136
|
+
# 检查是否启用了Lint功能
|
|
137
|
+
enable_lint = self.args.enable_auto_fix_lint
|
|
138
|
+
|
|
139
|
+
if enable_lint:
|
|
140
|
+
try:
|
|
141
|
+
if self.shadow_linter and self.shadow_manager:
|
|
142
|
+
# 对新创建的文件进行 lint 检查
|
|
143
|
+
# 由于没有shadow系统,需要先创建shadow文件
|
|
144
|
+
shadow_path = self.shadow_manager.to_shadow_path(abs_file_path)
|
|
145
|
+
os.makedirs(os.path.dirname(shadow_path), exist_ok=True)
|
|
146
|
+
with open(shadow_path, 'w', encoding='utf-8') as f:
|
|
147
|
+
f.write(content)
|
|
148
|
+
|
|
149
|
+
lint_results = self.shadow_linter.lint_shadow_file(shadow_path)
|
|
150
|
+
|
|
151
|
+
if lint_results and lint_results.issues:
|
|
152
|
+
has_lint_issues = True
|
|
153
|
+
# 格式化 lint 问题
|
|
154
|
+
formatted_issues = self._format_lint_issues(lint_results)
|
|
155
|
+
lint_message = f"\n\n代码质量检查发现 {len(lint_results.issues)} 个问题:\n{formatted_issues}"
|
|
156
|
+
else:
|
|
157
|
+
lint_message = "\n\n代码质量检查通过,未发现问题。"
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.error(f"Lint 检查失败: {str(e)}")
|
|
160
|
+
lint_message = "\n\n尝试进行代码质量检查时出错。"
|
|
161
|
+
else:
|
|
162
|
+
logger.info("代码质量检查已禁用")
|
|
163
|
+
|
|
164
|
+
# 构建包含 lint 结果的返回消息
|
|
165
|
+
message = f"Successfully wrote to file: {file_path}"
|
|
166
|
+
|
|
167
|
+
# 将 lint 消息添加到结果中,如果启用了Lint
|
|
168
|
+
if enable_lint:
|
|
169
|
+
message += lint_message
|
|
170
|
+
|
|
171
|
+
# 附加 lint 结果到返回内容
|
|
172
|
+
result_content = {
|
|
173
|
+
"content": content,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
# 只有在启用Lint时才添加Lint结果
|
|
177
|
+
if enable_lint:
|
|
178
|
+
result_content["lint_results"] = {
|
|
179
|
+
"has_issues": has_lint_issues,
|
|
180
|
+
"issues": formatted_issues if has_lint_issues else None
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return ToolResult(success=True, message=message, content=result_content)
|
|
56
184
|
except Exception as e:
|
|
57
185
|
logger.error(f"Error writing to file '{file_path}': {str(e)}")
|
|
58
186
|
return ToolResult(success=False, message=f"An error occurred while writing to the file: {str(e)}")
|
|
@@ -2,7 +2,6 @@ from pydantic import BaseModel
|
|
|
2
2
|
from typing import List, Dict, Any, Callable, Optional, Type
|
|
3
3
|
from pydantic import SkipValidation
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
# Result class used by Tool Resolvers
|
|
7
6
|
class ToolResult(BaseModel):
|
|
8
7
|
success: bool
|
|
@@ -115,7 +114,7 @@ TOOL_MODEL_MAP: Dict[str, Type[BaseTool]] = {
|
|
|
115
114
|
"attempt_completion": AttemptCompletionTool,
|
|
116
115
|
"plan_mode_respond": PlanModeRespondTool,
|
|
117
116
|
"use_mcp_tool": UseMcpTool,
|
|
118
|
-
"list_package_info": ListPackageInfoTool,
|
|
117
|
+
"list_package_info": ListPackageInfoTool,
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
class FileChangeEntry(BaseModel):
|
|
@@ -93,8 +93,7 @@ TOOL_DISPLAY_MESSAGES: Dict[Type[BaseTool], Dict[str, str]] = {
|
|
|
93
93
|
"[dim]工具:[/dim] [blue]{{ tool_name }}[/]\n"
|
|
94
94
|
"[dim]参数:[/dim] {{ arguments_snippet }}{{ ellipsis }}"
|
|
95
95
|
)
|
|
96
|
-
}
|
|
97
|
-
# AttemptCompletionTool is handled separately in the display loop
|
|
96
|
+
}
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
def get_tool_display_message(tool: BaseTool) -> str:
|
|
@@ -172,7 +171,7 @@ def get_tool_display_message(tool: BaseTool) -> str:
|
|
|
172
171
|
"tool_name": tool.tool_name,
|
|
173
172
|
"arguments_snippet": snippet,
|
|
174
173
|
"ellipsis": '...' if len(args_str) > 100 else ''
|
|
175
|
-
}
|
|
174
|
+
}
|
|
176
175
|
else:
|
|
177
176
|
# Generic context for tools not specifically handled above
|
|
178
177
|
context = tool.model_dump()
|