auto-coder 0.1.315__py3-none-any.whl → 0.1.317__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.315.dist-info → auto_coder-0.1.317.dist-info}/METADATA +1 -1
- {auto_coder-0.1.315.dist-info → auto_coder-0.1.317.dist-info}/RECORD +42 -21
- autocoder/auto_coder_runner.py +1 -2
- autocoder/commands/tools.py +30 -19
- autocoder/common/__init__.py +3 -0
- autocoder/common/auto_coder_lang.py +24 -0
- autocoder/common/code_auto_merge_editblock.py +2 -42
- autocoder/common/git_utils.py +2 -2
- autocoder/common/token_cost_caculate.py +103 -42
- autocoder/common/v2/__init__.py +0 -0
- autocoder/common/v2/code_auto_generate.py +199 -0
- autocoder/common/v2/code_auto_generate_diff.py +361 -0
- autocoder/common/v2/code_auto_generate_editblock.py +380 -0
- autocoder/common/v2/code_auto_generate_strict_diff.py +269 -0
- autocoder/common/v2/code_auto_merge.py +211 -0
- autocoder/common/v2/code_auto_merge_diff.py +354 -0
- autocoder/common/v2/code_auto_merge_editblock.py +523 -0
- autocoder/common/v2/code_auto_merge_strict_diff.py +259 -0
- autocoder/common/v2/code_diff_manager.py +266 -0
- autocoder/common/v2/code_editblock_manager.py +275 -0
- autocoder/common/v2/code_manager.py +238 -0
- autocoder/common/v2/code_strict_diff_manager.py +241 -0
- autocoder/dispacher/actions/action.py +16 -0
- autocoder/dispacher/actions/plugins/action_regex_project.py +6 -0
- autocoder/events/event_manager_singleton.py +2 -2
- autocoder/helper/__init__.py +0 -0
- autocoder/helper/project_creator.py +570 -0
- autocoder/linters/linter_factory.py +44 -25
- autocoder/linters/models.py +220 -0
- autocoder/linters/python_linter.py +1 -7
- autocoder/linters/reactjs_linter.py +580 -0
- autocoder/linters/shadow_linter.py +390 -0
- autocoder/linters/vue_linter.py +576 -0
- autocoder/memory/active_context_manager.py +0 -4
- autocoder/memory/active_package.py +12 -12
- autocoder/shadows/__init__.py +0 -0
- autocoder/shadows/shadow_manager.py +235 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.315.dist-info → auto_coder-0.1.317.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.315.dist-info → auto_coder-0.1.317.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.315.dist-info → auto_coder-0.1.317.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.315.dist-info → auto_coder-0.1.317.dist-info}/top_level.txt +0 -0
|
@@ -6,7 +6,8 @@ import os
|
|
|
6
6
|
from typing import Optional, Dict, Any, List
|
|
7
7
|
|
|
8
8
|
from autocoder.linters.base_linter import BaseLinter
|
|
9
|
-
from autocoder.linters.
|
|
9
|
+
from autocoder.linters.reactjs_linter import ReactJSLinter
|
|
10
|
+
from autocoder.linters.vue_linter import VueLinter
|
|
10
11
|
from autocoder.linters.python_linter import PythonLinter
|
|
11
12
|
|
|
12
13
|
class LinterFactory:
|
|
@@ -40,25 +41,26 @@ class LinterFactory:
|
|
|
40
41
|
# Map language to linter class
|
|
41
42
|
linter_map = {
|
|
42
43
|
'python': PythonLinter,
|
|
43
|
-
'javascript':
|
|
44
|
-
'typescript':
|
|
45
|
-
'js':
|
|
46
|
-
'ts':
|
|
47
|
-
'jsx':
|
|
48
|
-
'tsx':
|
|
49
|
-
'react':
|
|
50
|
-
'
|
|
44
|
+
'javascript': ReactJSLinter,
|
|
45
|
+
'typescript': ReactJSLinter,
|
|
46
|
+
'js': ReactJSLinter,
|
|
47
|
+
'ts': ReactJSLinter,
|
|
48
|
+
'jsx': ReactJSLinter,
|
|
49
|
+
'tsx': ReactJSLinter,
|
|
50
|
+
'react': ReactJSLinter,
|
|
51
|
+
'reactjs': ReactJSLinter,
|
|
52
|
+
'vue': VueLinter,
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
linter_class = linter_map.get(language.lower() if language else None)
|
|
54
56
|
|
|
55
57
|
if linter_class is None:
|
|
56
|
-
|
|
58
|
+
return None
|
|
57
59
|
|
|
58
60
|
return linter_class(verbose=verbose)
|
|
59
61
|
|
|
60
62
|
@classmethod
|
|
61
|
-
def _detect_language_from_file(cls, file_path: str) -> str:
|
|
63
|
+
def _detect_language_from_file(cls, file_path: str) -> Optional[str]:
|
|
62
64
|
"""
|
|
63
65
|
Detect the programming language based on file extension.
|
|
64
66
|
|
|
@@ -72,7 +74,7 @@ class LinterFactory:
|
|
|
72
74
|
ValueError: If the file extension is not recognized.
|
|
73
75
|
"""
|
|
74
76
|
if not os.path.exists(file_path):
|
|
75
|
-
|
|
77
|
+
return None
|
|
76
78
|
|
|
77
79
|
_, ext = os.path.splitext(file_path)
|
|
78
80
|
ext = ext.lower()
|
|
@@ -87,10 +89,7 @@ class LinterFactory:
|
|
|
87
89
|
'.vue': 'vue',
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
language = extension_map.get(ext)
|
|
91
|
-
if language is None:
|
|
92
|
-
raise ValueError(f"Unsupported file extension: {ext}")
|
|
93
|
-
|
|
92
|
+
language = extension_map.get(ext)
|
|
94
93
|
return language
|
|
95
94
|
|
|
96
95
|
@classmethod
|
|
@@ -117,6 +116,8 @@ class LinterFactory:
|
|
|
117
116
|
Dict[str, Any]: Lint results.
|
|
118
117
|
"""
|
|
119
118
|
linter = cls.create_linter(file_path=file_path, verbose=verbose)
|
|
119
|
+
if linter is None:
|
|
120
|
+
return None
|
|
120
121
|
return linter.lint_file(file_path, fix=fix)
|
|
121
122
|
|
|
122
123
|
@classmethod
|
|
@@ -136,13 +137,31 @@ class LinterFactory:
|
|
|
136
137
|
"""
|
|
137
138
|
# If language not specified, try to detect from project contents
|
|
138
139
|
if language is None:
|
|
139
|
-
# First check for package.json (JavaScript/TypeScript)
|
|
140
|
+
# First check for package.json (JavaScript/TypeScript/React/Vue)
|
|
140
141
|
if os.path.exists(os.path.join(project_path, 'package.json')):
|
|
141
|
-
|
|
142
|
+
# Check if it's a React or Vue project
|
|
143
|
+
try:
|
|
144
|
+
with open(os.path.join(project_path, 'package.json'), 'r') as f:
|
|
145
|
+
import json
|
|
146
|
+
package_data = json.load(f)
|
|
147
|
+
|
|
148
|
+
dependencies = {
|
|
149
|
+
**package_data.get('dependencies', {}),
|
|
150
|
+
**package_data.get('devDependencies', {})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if 'react' in dependencies:
|
|
154
|
+
language = 'react'
|
|
155
|
+
elif 'vue' in dependencies:
|
|
156
|
+
language = 'vue'
|
|
157
|
+
else:
|
|
158
|
+
language = 'javascript'
|
|
159
|
+
except:
|
|
160
|
+
language = 'javascript' # Default to JavaScript
|
|
142
161
|
# Check for setup.py or requirements.txt (Python)
|
|
143
162
|
elif (os.path.exists(os.path.join(project_path, 'setup.py')) or
|
|
144
163
|
os.path.exists(os.path.join(project_path, 'requirements.txt'))):
|
|
145
|
-
|
|
164
|
+
language = 'python'
|
|
146
165
|
else:
|
|
147
166
|
# Count file extensions to guess the dominant language
|
|
148
167
|
language_counts = {}
|
|
@@ -166,10 +185,10 @@ class LinterFactory:
|
|
|
166
185
|
raise ValueError(f"Could not detect project language in {project_path}")
|
|
167
186
|
|
|
168
187
|
language = cls._detect_language_from_file(f"dummy{most_common}")
|
|
169
|
-
linter = cls.create_linter(language=language, verbose=verbose)
|
|
170
|
-
else:
|
|
171
|
-
linter = cls.create_linter(language=language, verbose=verbose)
|
|
172
188
|
|
|
189
|
+
linter = cls.create_linter(language=language, verbose=verbose)
|
|
190
|
+
if linter is None:
|
|
191
|
+
return None
|
|
173
192
|
return linter.lint_project(project_path, fix=fix)
|
|
174
193
|
|
|
175
194
|
@classmethod
|
|
@@ -185,12 +204,12 @@ class LinterFactory:
|
|
|
185
204
|
Returns:
|
|
186
205
|
str: A formatted string representation of the lint results.
|
|
187
206
|
"""
|
|
188
|
-
# Try to infer language from lint_result
|
|
207
|
+
# Try to infer language/framework from lint_result
|
|
189
208
|
if language is None:
|
|
190
209
|
if 'language' in lint_result:
|
|
191
210
|
language = lint_result['language']
|
|
192
|
-
elif '
|
|
193
|
-
language = lint_result['
|
|
211
|
+
elif 'framework' in lint_result:
|
|
212
|
+
language = lint_result['framework']
|
|
194
213
|
elif 'file_type' in lint_result:
|
|
195
214
|
language = lint_result['file_type']
|
|
196
215
|
else:
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
用于表示代码检查结果的 Pydantic 模型。
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from typing import List, Dict, Any, Optional, Union
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
class IssueSeverity(str, Enum):
|
|
11
|
+
"""问题严重性的枚举。"""
|
|
12
|
+
ERROR = "error" # 错误
|
|
13
|
+
WARNING = "warning" # 警告
|
|
14
|
+
INFO = "info" # 信息
|
|
15
|
+
HINT = "hint" # 提示
|
|
16
|
+
|
|
17
|
+
class IssuePosition(BaseModel):
|
|
18
|
+
"""文件中问题的位置。"""
|
|
19
|
+
line: int = Field(..., description="发现问题的行号(从1开始)")
|
|
20
|
+
column: Optional[int] = Field(None, description="发现问题的列号(从1开始)")
|
|
21
|
+
end_line: Optional[int] = Field(None, description="问题的结束行号")
|
|
22
|
+
end_column: Optional[int] = Field(None, description="问题的结束列号")
|
|
23
|
+
|
|
24
|
+
def to_str(self) -> str:
|
|
25
|
+
"""将位置转换为人类可读的字符串。"""
|
|
26
|
+
result = f"第 {self.line} 行"
|
|
27
|
+
if self.column is not None:
|
|
28
|
+
result += f",第 {self.column} 列"
|
|
29
|
+
if self.end_line is not None and (self.end_line != self.line or self.end_column is not None):
|
|
30
|
+
result += f" 到第 {self.end_line} 行"
|
|
31
|
+
if self.end_column is not None:
|
|
32
|
+
result += f",第 {self.end_column} 列"
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
class LintIssue(BaseModel):
|
|
36
|
+
"""单个lint问题的表示。"""
|
|
37
|
+
code: str = Field(..., description="问题代码或标识符")
|
|
38
|
+
message: str = Field(..., description="问题的人类可读描述")
|
|
39
|
+
severity: IssueSeverity = Field(..., description="问题的严重性级别")
|
|
40
|
+
position: IssuePosition = Field(..., description="问题在文件中的位置")
|
|
41
|
+
file_path: str = Field(..., description="发现问题的文件路径")
|
|
42
|
+
rule_name: Optional[str] = Field(None, description="违反的lint规则名称")
|
|
43
|
+
source: Optional[str] = Field(None, description="发现问题的源代码片段")
|
|
44
|
+
fix_available: bool = Field(False, description="是否有可用的自动修复")
|
|
45
|
+
fix_description: Optional[str] = Field(None, description="可用修复的描述")
|
|
46
|
+
|
|
47
|
+
def to_str(self, show_file_path: bool = True) -> str:
|
|
48
|
+
"""
|
|
49
|
+
将问题转换为人类可读的字符串。
|
|
50
|
+
|
|
51
|
+
参数:
|
|
52
|
+
show_file_path: 是否在输出中包含文件路径
|
|
53
|
+
|
|
54
|
+
返回:
|
|
55
|
+
问题的格式化字符串表示
|
|
56
|
+
"""
|
|
57
|
+
parts = []
|
|
58
|
+
|
|
59
|
+
if show_file_path:
|
|
60
|
+
parts.append(f"{self.file_path}:{self.position.line}")
|
|
61
|
+
|
|
62
|
+
parts.append(f"[{self.severity.value.upper()}] {self.message}")
|
|
63
|
+
|
|
64
|
+
if self.code:
|
|
65
|
+
parts.append(f"({self.code})")
|
|
66
|
+
|
|
67
|
+
if self.rule_name:
|
|
68
|
+
parts.append(f"[规则: {self.rule_name}]")
|
|
69
|
+
|
|
70
|
+
result = " ".join(parts)
|
|
71
|
+
|
|
72
|
+
if self.source:
|
|
73
|
+
# 为了更好的可读性,缩进源代码
|
|
74
|
+
indented_source = "\n ".join(self.source.splitlines())
|
|
75
|
+
result += f"\n {indented_source}"
|
|
76
|
+
|
|
77
|
+
if self.fix_available and self.fix_description:
|
|
78
|
+
result += f"\n 修复: {self.fix_description}"
|
|
79
|
+
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
class FileLintResult(BaseModel):
|
|
83
|
+
"""单个文件的lint结果。"""
|
|
84
|
+
file_path: str = Field(..., description="被检查的文件路径")
|
|
85
|
+
success: bool = Field(True, description="lint是否成功完成")
|
|
86
|
+
language: str = Field(..., description="被检查的语言/文件类型")
|
|
87
|
+
issues: List[LintIssue] = Field(default_factory=list, description="发现的lint问题列表")
|
|
88
|
+
error: Optional[str] = Field(None, description="如果lint失败,错误消息")
|
|
89
|
+
warning_count: int = Field(0, description="发现的警告数量")
|
|
90
|
+
error_count: int = Field(0, description="发现的错误数量")
|
|
91
|
+
info_count: int = Field(0, description="发现的信息性问题数量")
|
|
92
|
+
execution_time_ms: Optional[int] = Field(None, description="lint文件所花费的时间(毫秒)")
|
|
93
|
+
fixed_issues_count: Optional[int] = Field(None, description="自动修复的问题数量")
|
|
94
|
+
|
|
95
|
+
def to_str(self, show_file_path_in_issues: bool = False) -> str:
|
|
96
|
+
"""
|
|
97
|
+
将文件lint结果转换为人类可读的字符串。
|
|
98
|
+
|
|
99
|
+
参数:
|
|
100
|
+
show_file_path_in_issues: 是否在单个问题中包含文件路径
|
|
101
|
+
|
|
102
|
+
返回:
|
|
103
|
+
文件lint结果的格式化字符串表示
|
|
104
|
+
"""
|
|
105
|
+
if not self.success:
|
|
106
|
+
return f"检查 {self.file_path} 时出错: {self.error or '未知错误'}"
|
|
107
|
+
|
|
108
|
+
result = [f"{self.file_path} 的检查结果 ({self.language}):"]
|
|
109
|
+
|
|
110
|
+
# 摘要信息
|
|
111
|
+
summary = []
|
|
112
|
+
if self.error_count > 0:
|
|
113
|
+
summary.append(f"{self.error_count} 个错误")
|
|
114
|
+
if self.warning_count > 0:
|
|
115
|
+
summary.append(f"{self.warning_count} 个警告")
|
|
116
|
+
if self.info_count > 0:
|
|
117
|
+
summary.append(f"{self.info_count} 个信息")
|
|
118
|
+
|
|
119
|
+
if summary:
|
|
120
|
+
result.append("发现 " + ",".join(summary))
|
|
121
|
+
else:
|
|
122
|
+
result.append("未发现问题")
|
|
123
|
+
|
|
124
|
+
if self.execution_time_ms is not None:
|
|
125
|
+
result.append(f"执行时间: {self.execution_time_ms}毫秒")
|
|
126
|
+
|
|
127
|
+
if self.fixed_issues_count:
|
|
128
|
+
result.append(f"已修复 {self.fixed_issues_count} 个问题")
|
|
129
|
+
|
|
130
|
+
# 添加单个问题
|
|
131
|
+
if self.issues:
|
|
132
|
+
result.append("\n问题:")
|
|
133
|
+
for issue in self.issues:
|
|
134
|
+
issue_str = issue.to_str(show_file_path=show_file_path_in_issues)
|
|
135
|
+
# 缩进问题描述的每一行
|
|
136
|
+
indented_issue = "\n ".join(issue_str.splitlines())
|
|
137
|
+
result.append(f" {indented_issue}")
|
|
138
|
+
|
|
139
|
+
return "\n".join(result)
|
|
140
|
+
|
|
141
|
+
class ProjectLintResult(BaseModel):
|
|
142
|
+
"""整个项目或一组文件的lint结果。"""
|
|
143
|
+
project_path: Optional[str] = Field(None, description="被检查的项目路径")
|
|
144
|
+
file_results: Dict[str, FileLintResult] = Field(..., description="文件路径到其lint结果的映射")
|
|
145
|
+
total_files: int = Field(..., description="检查的文件总数")
|
|
146
|
+
files_with_issues: int = Field(0, description="至少有一个问题的文件数量")
|
|
147
|
+
total_issues: int = Field(0, description="所有文件中发现的问题总数")
|
|
148
|
+
total_errors: int = Field(0, description="所有文件中的错误总数")
|
|
149
|
+
total_warnings: int = Field(0, description="所有文件中的警告总数")
|
|
150
|
+
total_infos: int = Field(0, description="所有文件中的信息性问题总数")
|
|
151
|
+
success: bool = Field(True, description="所有文件的lint过程是否成功完成")
|
|
152
|
+
error: Optional[str] = Field(None, description="如果lint失败,错误消息")
|
|
153
|
+
fixed_issues_count: Optional[int] = Field(None, description="自动修复的问题数量")
|
|
154
|
+
|
|
155
|
+
def to_str(self, include_all_files: bool = True, include_issues: bool = True) -> str:
|
|
156
|
+
"""
|
|
157
|
+
将项目lint结果转换为人类可读的字符串。
|
|
158
|
+
|
|
159
|
+
参数:
|
|
160
|
+
include_all_files: 是否包含没有问题的文件的结果
|
|
161
|
+
include_issues: 是否包含单个问题的详细信息
|
|
162
|
+
|
|
163
|
+
返回:
|
|
164
|
+
项目lint结果的格式化字符串表示
|
|
165
|
+
"""
|
|
166
|
+
result = [f"项目代码检查结果 ({self.timestamp.strftime('%Y-%m-%d %H:%M:%S')})"]
|
|
167
|
+
|
|
168
|
+
if self.project_path:
|
|
169
|
+
result.append(f"项目: {self.project_path}")
|
|
170
|
+
|
|
171
|
+
if not self.success:
|
|
172
|
+
result.append(f"错误: {self.error or '未知错误'}")
|
|
173
|
+
return "\n".join(result)
|
|
174
|
+
|
|
175
|
+
# 摘要信息
|
|
176
|
+
result.append(f"检查的文件: {self.total_files}")
|
|
177
|
+
result.append(f"有问题的文件: {self.files_with_issues}")
|
|
178
|
+
result.append(f"总问题数: {self.total_issues} ({self.total_errors} 个错误, {self.total_warnings} 个警告, {self.total_infos} 个信息)")
|
|
179
|
+
|
|
180
|
+
if self.fixed_issues_count:
|
|
181
|
+
result.append(f"已修复的问题: {self.fixed_issues_count}")
|
|
182
|
+
|
|
183
|
+
# 添加每个文件的结果
|
|
184
|
+
if self.file_results:
|
|
185
|
+
result.append("\n按文件的结果:")
|
|
186
|
+
|
|
187
|
+
for file_path, file_result in sorted(self.file_results.items()):
|
|
188
|
+
# 如果请求,跳过没有问题的文件
|
|
189
|
+
if not include_all_files and not (file_result.error_count + file_result.warning_count + file_result.info_count) > 0:
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
if include_issues:
|
|
193
|
+
file_str = file_result.to_str(show_file_path_in_issues=False)
|
|
194
|
+
# 缩进文件结果的每一行
|
|
195
|
+
indented_file = "\n ".join(file_str.splitlines())
|
|
196
|
+
result.append(f" {indented_file}")
|
|
197
|
+
else:
|
|
198
|
+
# 每个文件只有一行摘要
|
|
199
|
+
issue_count = file_result.error_count + file_result.warning_count + file_result.info_count
|
|
200
|
+
if issue_count > 0:
|
|
201
|
+
result.append(f" {file_path}: {issue_count} 个问题 ({file_result.error_count} 个错误, {file_result.warning_count} 个警告)")
|
|
202
|
+
else:
|
|
203
|
+
result.append(f" {file_path}: 无问题")
|
|
204
|
+
|
|
205
|
+
# 在文件之间添加分隔符以提高可读性
|
|
206
|
+
result.append("")
|
|
207
|
+
|
|
208
|
+
return "\n".join(result)
|
|
209
|
+
|
|
210
|
+
def get_file_result(self, file_path: str) -> Optional[FileLintResult]:
|
|
211
|
+
"""
|
|
212
|
+
获取特定文件的lint结果。
|
|
213
|
+
|
|
214
|
+
参数:
|
|
215
|
+
file_path: 文件路径
|
|
216
|
+
|
|
217
|
+
返回:
|
|
218
|
+
文件的lint结果,如果文件未被检查则返回None
|
|
219
|
+
"""
|
|
220
|
+
return self.file_results.get(file_path)
|
|
@@ -102,13 +102,7 @@ class PythonLinter(BaseLinter):
|
|
|
102
102
|
subprocess.run([sys.executable, "-m", "flake8", "--version"],
|
|
103
103
|
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
104
104
|
except (subprocess.SubprocessError, FileNotFoundError):
|
|
105
|
-
dependencies_to_install.append("flake8")
|
|
106
|
-
|
|
107
|
-
try:
|
|
108
|
-
subprocess.run([sys.executable, "-m", "black", "--version"],
|
|
109
|
-
check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
110
|
-
except (subprocess.SubprocessError, FileNotFoundError):
|
|
111
|
-
dependencies_to_install.append("black")
|
|
105
|
+
dependencies_to_install.append("flake8")
|
|
112
106
|
|
|
113
107
|
# Install missing dependencies
|
|
114
108
|
if dependencies_to_install:
|