auto-coder 0.1.263__py3-none-any.whl → 0.1.265__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 (58) hide show
  1. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/RECORD +58 -55
  3. autocoder/agent/planner.py +4 -4
  4. autocoder/auto_coder.py +26 -21
  5. autocoder/auto_coder_server.py +7 -7
  6. autocoder/chat_auto_coder.py +203 -98
  7. autocoder/commands/auto_command.py +81 -4
  8. autocoder/commands/tools.py +48 -50
  9. autocoder/common/__init__.py +6 -1
  10. autocoder/common/auto_coder_lang.py +41 -3
  11. autocoder/common/code_auto_generate.py +3 -3
  12. autocoder/common/code_auto_generate_diff.py +12 -15
  13. autocoder/common/code_auto_generate_editblock.py +3 -3
  14. autocoder/common/code_auto_generate_strict_diff.py +3 -3
  15. autocoder/common/code_auto_merge.py +23 -3
  16. autocoder/common/code_auto_merge_diff.py +29 -4
  17. autocoder/common/code_auto_merge_editblock.py +25 -5
  18. autocoder/common/code_auto_merge_strict_diff.py +26 -6
  19. autocoder/common/code_modification_ranker.py +65 -3
  20. autocoder/common/command_completer.py +3 -0
  21. autocoder/common/command_generator.py +24 -8
  22. autocoder/common/command_templates.py +2 -2
  23. autocoder/common/conf_import_export.py +105 -0
  24. autocoder/common/conf_validator.py +7 -1
  25. autocoder/common/context_pruner.py +305 -0
  26. autocoder/common/files.py +41 -2
  27. autocoder/common/image_to_page.py +11 -11
  28. autocoder/common/index_import_export.py +38 -18
  29. autocoder/common/mcp_hub.py +3 -3
  30. autocoder/common/mcp_server.py +2 -2
  31. autocoder/common/shells.py +254 -13
  32. autocoder/common/stats_panel.py +126 -0
  33. autocoder/dispacher/actions/action.py +6 -18
  34. autocoder/dispacher/actions/copilot.py +2 -2
  35. autocoder/dispacher/actions/plugins/action_regex_project.py +1 -3
  36. autocoder/dispacher/actions/plugins/action_translate.py +1 -1
  37. autocoder/index/entry.py +8 -2
  38. autocoder/index/filter/normal_filter.py +13 -2
  39. autocoder/index/filter/quick_filter.py +127 -13
  40. autocoder/index/index.py +8 -7
  41. autocoder/models.py +2 -2
  42. autocoder/pyproject/__init__.py +5 -5
  43. autocoder/rag/cache/byzer_storage_cache.py +4 -4
  44. autocoder/rag/cache/file_monitor_cache.py +2 -2
  45. autocoder/rag/cache/simple_cache.py +4 -4
  46. autocoder/rag/long_context_rag.py +2 -2
  47. autocoder/regexproject/__init__.py +3 -2
  48. autocoder/suffixproject/__init__.py +3 -2
  49. autocoder/tsproject/__init__.py +3 -2
  50. autocoder/utils/conversation_store.py +1 -1
  51. autocoder/utils/operate_config_api.py +3 -3
  52. autocoder/utils/project_structure.py +258 -3
  53. autocoder/utils/thread_utils.py +6 -1
  54. autocoder/version.py +1 -1
  55. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/LICENSE +0 -0
  56. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/WHEEL +0 -0
  57. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/entry_points.txt +0 -0
  58. {auto_coder-0.1.263.dist-info → auto_coder-0.1.265.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,264 @@
1
+ from collections import defaultdict
2
+ import os
3
+ import re
4
+ from dataclasses import dataclass
5
+ from typing import List, Pattern, Dict, Any, Set, Union
6
+ from concurrent.futures import ThreadPoolExecutor
7
+ import byzerllm
8
+ from pydantic import BaseModel
9
+ from rich.tree import Tree
10
+ from rich.console import Console
11
+ from loguru import logger
1
12
  from autocoder.pyproject import PyProject
2
13
  from autocoder.tsproject import TSProject
3
14
  from autocoder.suffixproject import SuffixProject
4
15
  from autocoder.common import AutoCoderArgs
5
- import byzerllm
6
- from typing import Union
16
+
17
+ @dataclass
18
+ class AnalysisConfig:
19
+ exclude_dirs: List[str] = None
20
+ exclude_file_patterns: List[Pattern] = None
21
+ exclude_extensions: List[str] = None
22
+ max_depth: int = -1
23
+ show_hidden: bool = False
24
+ parallel_processing: bool = True
25
+
26
+ class ExtentionResult(BaseModel):
27
+ code: List[str] = []
28
+ config: List[str] = []
29
+ data: List[str] = []
30
+ document: List[str] = []
31
+ other: List[str] = []
32
+
33
+ class EnhancedFileAnalyzer:
34
+ DEFAULT_EXCLUDE_DIRS = [".git", "node_modules", "__pycache__", "venv"]
35
+ DEFAULT_EXCLUDE_EXTS = [".log", ".tmp", ".bak", ".swp"]
36
+
37
+ def __init__(self, args: AutoCoderArgs, llm: Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM], config: AnalysisConfig = None,):
38
+ self.directory = os.path.abspath(args.source_dir)
39
+ self.config = config or self.default_config()
40
+ self.llm = llm
41
+ self.console = Console()
42
+ self.file_filter = EnhancedFileFilter(self.config)
43
+
44
+ @classmethod
45
+ def default_config(cls) -> AnalysisConfig:
46
+ return AnalysisConfig(
47
+ exclude_dirs=cls.DEFAULT_EXCLUDE_DIRS,
48
+ exclude_file_patterns=[re.compile(r'~$')], # 默认排除临时文件
49
+ exclude_extensions=cls.DEFAULT_EXCLUDE_EXTS
50
+ )
51
+
52
+ def analyze(self) -> Dict[str, Any]:
53
+ """执行完整分析流程"""
54
+ return {
55
+ "structure": self.get_tree_structure(),
56
+ "extensions": self.analyze_extensions(),
57
+ "stats": self.get_directory_stats()
58
+ }
59
+
60
+ def get_tree_structure(self) -> Dict:
61
+ """获取优化的树形结构"""
62
+ tree = {}
63
+ if self.config.parallel_processing:
64
+ return self._parallel_tree_build()
65
+ return self._sequential_tree_build()
66
+
67
+ def _sequential_tree_build(self) -> Dict:
68
+ """单线程构建目录树"""
69
+ tree = {}
70
+ for root, dirs, files in os.walk(self.directory):
71
+ dirs[:] = [d for d in dirs if not self.file_filter.should_ignore(d, True)]
72
+ relative_path = os.path.relpath(root, self.directory)
73
+ current = tree
74
+ for part in relative_path.split(os.sep):
75
+ current = current.setdefault(part, {})
76
+ current.update({f: None for f in files if not self.file_filter.should_ignore(f, False)})
77
+ return tree
78
+
79
+ def _parallel_tree_build(self) -> Dict:
80
+ """并行构建目录树"""
81
+ from concurrent.futures import ThreadPoolExecutor, as_completed
82
+ import threading
83
+
84
+ tree = {}
85
+ tree_lock = threading.Lock()
86
+
87
+ def process_directory(root: str, dirs: List[str], files: List[str]) -> Dict:
88
+ local_tree = {}
89
+ relative_path = os.path.relpath(root, self.directory)
90
+ current = local_tree
91
+ for part in relative_path.split(os.sep):
92
+ current = current.setdefault(part, {})
93
+ current.update({f: None for f in files if not self.file_filter.should_ignore(f, False)})
94
+ return local_tree
95
+
96
+ with ThreadPoolExecutor() as executor:
97
+ futures = []
98
+ for root, dirs, files in os.walk(self.directory):
99
+ dirs[:] = [d for d in dirs if not self.file_filter.should_ignore(d, True)]
100
+ futures.append(executor.submit(process_directory, root, dirs, files))
101
+
102
+ for future in as_completed(futures):
103
+ try:
104
+ local_tree = future.result()
105
+ with tree_lock:
106
+ self._merge_trees(tree, local_tree)
107
+ except Exception as e:
108
+ logger.error(f"Error processing directory: {e}")
109
+
110
+ return tree
111
+
112
+ def _merge_trees(self, base_tree: Dict, new_tree: Dict) -> None:
113
+ """递归合并两个目录树"""
114
+ for key, value in new_tree.items():
115
+ if key in base_tree:
116
+ if isinstance(value, dict) and isinstance(base_tree[key], dict):
117
+ self._merge_trees(base_tree[key], value)
118
+ else:
119
+ base_tree[key] = value
120
+
121
+ def analyze_extensions(self) -> Dict:
122
+ """增强版后缀分析"""
123
+ from collections import defaultdict
124
+ extensions = self._collect_extensions()
125
+ if self.llm:
126
+ return self._llm_enhanced_analysis.with_llm(self.llm).run(extensions)
127
+ return self._basic_analysis(extensions)
128
+
129
+ def _collect_extensions(self) -> Set[str]:
130
+ """带过滤的文件后缀收集"""
131
+ extensions = set()
132
+ for root, dirs, files in os.walk(self.directory):
133
+ dirs[:] = [d for d in dirs if not self.file_filter.should_ignore(d, True)]
134
+ for file in files:
135
+ if self.file_filter.should_ignore(file, False):
136
+ continue
137
+ ext = os.path.splitext(file)[1].lower()
138
+ if ext: # 排除无后缀文件
139
+ extensions.add(ext)
140
+ return extensions
141
+
142
+ @byzerllm.prompt()
143
+ def _llm_enhanced_analysis(self, extensions: List[str]) -> Dict:
144
+ """LLM增强分析"""
145
+ '''
146
+ 请根据以下文件后缀列表,按照以下规则进行分类:
147
+
148
+ 1. 代码文件:包含可编译代码、有语法结构的文件
149
+ 2. 配置文件:包含参数设置、环境配置的文件
150
+ 3. 数据文件:包含结构化或非结构化数据的文件
151
+ 4. 文档文件:包含文档、说明、笔记的文件
152
+ 5. 其他文件:无法明确分类的文件
153
+
154
+ 文件后缀列表:
155
+ {{ extensions | join(', ') }}
156
+
157
+ 请返回如下JSON格式:
158
+ {
159
+ "code": ["后缀1", "后缀2"],
160
+ "config": ["后缀3", "后缀4"],
161
+ "data": ["后缀5", "后缀6"],
162
+ "document": ["后缀7", "后缀8"],
163
+ "other": ["后缀9", "后缀10"]
164
+ }
165
+ '''
166
+ return {
167
+ "extensions": extensions
168
+ }
169
+
170
+ def _basic_analysis(self, extensions: Set[str]) -> Dict:
171
+ """基于规则的基础分析"""
172
+ CODE_EXTS = {'.py', '.js', '.ts', '.java', '.c', '.cpp'}
173
+ CONFIG_EXTS = {'.yml', '.yaml', '.json', '.toml', '.ini'}
174
+
175
+ return {
176
+ "code": [ext for ext in extensions if ext in CODE_EXTS],
177
+ "config": [ext for ext in extensions if ext in CONFIG_EXTS],
178
+ "unknown": [ext for ext in extensions if ext not in CODE_EXTS | CONFIG_EXTS]
179
+ }
180
+
181
+ def get_directory_stats(self) -> Dict:
182
+ """获取目录统计信息"""
183
+ stats = {
184
+ 'total_files': 0,
185
+ 'total_dirs': 0,
186
+ 'by_extension': defaultdict(int),
187
+ 'file_types': {
188
+ 'code': 0,
189
+ 'config': 0,
190
+ 'data': 0,
191
+ 'document': 0,
192
+ 'other': 0
193
+ }
194
+ }
195
+ for root, dirs, files in os.walk(self.directory):
196
+ dirs[:] = [d for d in dirs if not self.file_filter.should_ignore(d, True)]
197
+ stats['total_dirs'] += len(dirs)
198
+ for file in files:
199
+ if self.file_filter.should_ignore(file, False):
200
+ continue
201
+ stats['total_files'] += 1
202
+ ext = os.path.splitext(file)[1].lower()
203
+ stats['by_extension'][ext] += 1
204
+
205
+ # 根据扩展名分类
206
+ if ext in ['.py', '.js', '.ts', '.java', '.c', '.cpp']:
207
+ stats['file_types']['code'] += 1
208
+ elif ext in ['.yml', '.yaml', '.json', '.toml', '.ini']:
209
+ stats['file_types']['config'] += 1
210
+ else:
211
+ stats['file_types']['other'] += 1
212
+ return stats
213
+
214
+ def interactive_display(self):
215
+ """交互式可视化展示"""
216
+ tree = self.build_interactive_tree(self.directory, self.config)
217
+ self.console.print(tree)
218
+ self.console.print("\n[bold]Statistical Summary:[/]")
219
+ stats = self.get_directory_stats()
220
+
221
+ from rich.table import Table
222
+ table = Table(title="Directory Statistics", show_header=True, header_style="bold magenta")
223
+ table.add_column("Metric", style="cyan")
224
+ table.add_column("Value", style="green")
225
+
226
+ table.add_row("Total Files", str(stats['total_files']))
227
+ table.add_row("Total Directories", str(stats['total_dirs']))
228
+ table.add_row("Code Files", str(stats['file_types']['code']))
229
+ table.add_row("Config Files", str(stats['file_types']['config']))
230
+ self.console.print(table)
231
+
232
+ class EnhancedFileFilter:
233
+ """增强版文件过滤器"""
234
+ def __init__(self, config: AnalysisConfig):
235
+ self.config = config
236
+
237
+ def should_ignore(self, path: str, is_dir: bool) -> bool:
238
+ """综合判断是否应忽略路径"""
239
+ base_name = os.path.basename(path)
240
+
241
+ # 隐藏文件处理
242
+ if not self.config.show_hidden and base_name.startswith('.'):
243
+ return True
244
+
245
+ # 目录排除
246
+ if is_dir and base_name in self.config.exclude_dirs:
247
+ return True
248
+
249
+ # 文件扩展名排除
250
+ if not is_dir:
251
+ ext = os.path.splitext(path)[1].lower()
252
+ if ext in self.config.exclude_extensions:
253
+ return True
254
+
255
+ # 正则匹配排除
256
+ full_path = os.path.abspath(path)
257
+ for pattern in self.config.exclude_file_patterns:
258
+ if pattern.search(full_path):
259
+ return True
260
+
261
+ return False
7
262
 
8
263
  def get_project_structure(args:AutoCoderArgs, llm:Union[byzerllm.ByzerLLM, byzerllm.SimpleByzerLLM]):
9
264
  if args.project_type == "ts":
@@ -12,4 +267,4 @@ def get_project_structure(args:AutoCoderArgs, llm:Union[byzerllm.ByzerLLM, byzer
12
267
  pp = PyProject(args=args, llm=llm)
13
268
  else:
14
269
  pp = SuffixProject(args=args, llm=llm, file_filter=None)
15
- return pp.get_tree_like_directory_structure()
270
+ return pp.get_tree_like_directory_structure()
@@ -176,7 +176,12 @@ def run_in_raw_thread():
176
176
  exception = []
177
177
  def worker():
178
178
  try:
179
- # global_cancel.reset()
179
+ # 如果刚开始就遇到了,可能是用户中断的还没有释放
180
+ # 等待五秒后强行释放
181
+ if global_cancel.requested:
182
+ time.sleep(5)
183
+ global_cancel.reset()
184
+
180
185
  ret = func(*args, **kwargs)
181
186
  result.append(ret)
182
187
  global_cancel.reset()
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.263"
1
+ __version__ = "0.1.265"