auto-coder 0.1.316__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.

Files changed (41) hide show
  1. {auto_coder-0.1.316.dist-info → auto_coder-0.1.317.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.316.dist-info → auto_coder-0.1.317.dist-info}/RECORD +41 -20
  3. autocoder/auto_coder_runner.py +1 -2
  4. autocoder/common/__init__.py +3 -0
  5. autocoder/common/auto_coder_lang.py +24 -0
  6. autocoder/common/code_auto_merge_editblock.py +2 -42
  7. autocoder/common/git_utils.py +2 -2
  8. autocoder/common/token_cost_caculate.py +103 -42
  9. autocoder/common/v2/__init__.py +0 -0
  10. autocoder/common/v2/code_auto_generate.py +199 -0
  11. autocoder/common/v2/code_auto_generate_diff.py +361 -0
  12. autocoder/common/v2/code_auto_generate_editblock.py +380 -0
  13. autocoder/common/v2/code_auto_generate_strict_diff.py +269 -0
  14. autocoder/common/v2/code_auto_merge.py +211 -0
  15. autocoder/common/v2/code_auto_merge_diff.py +354 -0
  16. autocoder/common/v2/code_auto_merge_editblock.py +523 -0
  17. autocoder/common/v2/code_auto_merge_strict_diff.py +259 -0
  18. autocoder/common/v2/code_diff_manager.py +266 -0
  19. autocoder/common/v2/code_editblock_manager.py +275 -0
  20. autocoder/common/v2/code_manager.py +238 -0
  21. autocoder/common/v2/code_strict_diff_manager.py +241 -0
  22. autocoder/dispacher/actions/action.py +16 -0
  23. autocoder/dispacher/actions/plugins/action_regex_project.py +6 -0
  24. autocoder/events/event_manager_singleton.py +2 -2
  25. autocoder/helper/__init__.py +0 -0
  26. autocoder/helper/project_creator.py +570 -0
  27. autocoder/linters/linter_factory.py +44 -25
  28. autocoder/linters/models.py +220 -0
  29. autocoder/linters/python_linter.py +1 -7
  30. autocoder/linters/reactjs_linter.py +580 -0
  31. autocoder/linters/shadow_linter.py +390 -0
  32. autocoder/linters/vue_linter.py +576 -0
  33. autocoder/memory/active_context_manager.py +0 -4
  34. autocoder/memory/active_package.py +12 -12
  35. autocoder/shadows/__init__.py +0 -0
  36. autocoder/shadows/shadow_manager.py +235 -0
  37. autocoder/version.py +1 -1
  38. {auto_coder-0.1.316.dist-info → auto_coder-0.1.317.dist-info}/LICENSE +0 -0
  39. {auto_coder-0.1.316.dist-info → auto_coder-0.1.317.dist-info}/WHEEL +0 -0
  40. {auto_coder-0.1.316.dist-info → auto_coder-0.1.317.dist-info}/entry_points.txt +0 -0
  41. {auto_coder-0.1.316.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.code_linter import FrontendLinter
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': FrontendLinter,
44
- 'typescript': FrontendLinter,
45
- 'js': FrontendLinter,
46
- 'ts': FrontendLinter,
47
- 'jsx': FrontendLinter,
48
- 'tsx': FrontendLinter,
49
- 'react': FrontendLinter,
50
- 'vue': FrontendLinter,
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
- raise ValueError(f"Unsupported language: {language}")
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
- raise ValueError(f"File does not exist: {file_path}")
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
- linter = cls.create_linter(language='javascript', verbose=verbose)
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
- linter = cls.create_linter(language='python', verbose=verbose)
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 'project_type' in lint_result:
193
- language = lint_result['project_type']
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: