auto-coder 0.1.363__py3-none-any.whl → 0.1.365__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.363.dist-info → auto_coder-0.1.365.dist-info}/METADATA +2 -2
- {auto_coder-0.1.363.dist-info → auto_coder-0.1.365.dist-info}/RECORD +39 -23
- autocoder/agent/base_agentic/tools/execute_command_tool_resolver.py +1 -1
- autocoder/auto_coder.py +46 -2
- autocoder/auto_coder_runner.py +2 -0
- autocoder/common/__init__.py +5 -0
- autocoder/common/file_checkpoint/__init__.py +21 -0
- autocoder/common/file_checkpoint/backup.py +264 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +182 -0
- autocoder/common/file_checkpoint/examples.py +217 -0
- autocoder/common/file_checkpoint/manager.py +611 -0
- autocoder/common/file_checkpoint/models.py +156 -0
- autocoder/common/file_checkpoint/store.py +383 -0
- autocoder/common/file_checkpoint/test_backup.py +242 -0
- autocoder/common/file_checkpoint/test_manager.py +570 -0
- autocoder/common/file_checkpoint/test_models.py +360 -0
- autocoder/common/file_checkpoint/test_store.py +327 -0
- autocoder/common/file_checkpoint/test_utils.py +297 -0
- autocoder/common/file_checkpoint/utils.py +119 -0
- autocoder/common/rulefiles/autocoderrules_utils.py +114 -55
- autocoder/common/save_formatted_log.py +76 -5
- autocoder/common/utils_code_auto_generate.py +2 -1
- autocoder/common/v2/agent/agentic_edit.py +545 -225
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +83 -43
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +116 -29
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +179 -48
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +101 -56
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +322 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +173 -132
- autocoder/common/v2/agent/agentic_edit_types.py +4 -0
- autocoder/compilers/normal_compiler.py +64 -0
- autocoder/events/event_manager_singleton.py +133 -4
- autocoder/linters/normal_linter.py +373 -0
- autocoder/linters/python_linter.py +4 -2
- autocoder/version.py +1 -1
- {auto_coder-0.1.363.dist-info → auto_coder-0.1.365.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.363.dist-info → auto_coder-0.1.365.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.363.dist-info → auto_coder-0.1.365.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.363.dist-info → auto_coder-0.1.365.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
import uuid
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConversationCheckpoint(BaseModel):
|
|
11
|
+
"""对话检查点,用于保存特定时刻的对话状态"""
|
|
12
|
+
|
|
13
|
+
checkpoint_id: str # 检查点ID,与变更组ID对应
|
|
14
|
+
timestamp: float # 创建时间戳
|
|
15
|
+
conversations: List[Dict[str, Any]] # 对话历史
|
|
16
|
+
metadata: Optional[Dict[str, Any]] = None # 元数据,可包含额外信息
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConversationCheckpointStore:
|
|
20
|
+
"""对话检查点存储管理器"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, store_dir: Optional[str] = None, max_history: int = 50):
|
|
23
|
+
"""
|
|
24
|
+
初始化对话检查点存储
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
store_dir: 存储目录,默认为用户主目录下的.autocoder/conversation_checkpoints
|
|
28
|
+
max_history: 最大保存的历史版本数量
|
|
29
|
+
"""
|
|
30
|
+
if store_dir is None:
|
|
31
|
+
home_dir = os.path.expanduser("~")
|
|
32
|
+
store_dir = os.path.join(home_dir, ".autocoder", "conversation_checkpoints")
|
|
33
|
+
|
|
34
|
+
self.store_dir = os.path.abspath(store_dir)
|
|
35
|
+
self.max_history = max_history
|
|
36
|
+
|
|
37
|
+
# 确保存储目录存在
|
|
38
|
+
os.makedirs(self.store_dir, exist_ok=True)
|
|
39
|
+
logger.info(f"对话检查点存储目录: {self.store_dir}")
|
|
40
|
+
|
|
41
|
+
def save_checkpoint(self, checkpoint: ConversationCheckpoint) -> str:
|
|
42
|
+
"""
|
|
43
|
+
保存对话检查点
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
checkpoint: 对话检查点对象
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
str: 检查点ID
|
|
50
|
+
"""
|
|
51
|
+
# 确保检查点有ID
|
|
52
|
+
if not checkpoint.checkpoint_id:
|
|
53
|
+
checkpoint.checkpoint_id = str(uuid.uuid4())
|
|
54
|
+
|
|
55
|
+
# 确保时间戳存在
|
|
56
|
+
if not checkpoint.timestamp:
|
|
57
|
+
checkpoint.timestamp = time.time()
|
|
58
|
+
|
|
59
|
+
# 构建文件路径
|
|
60
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint.checkpoint_id}.json")
|
|
61
|
+
|
|
62
|
+
# 保存为JSON文件
|
|
63
|
+
try:
|
|
64
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
65
|
+
f.write(checkpoint.json(ensure_ascii=False, indent=2))
|
|
66
|
+
|
|
67
|
+
logger.info(f"已保存对话检查点: {checkpoint.checkpoint_id}")
|
|
68
|
+
|
|
69
|
+
# 检查并清理过期的检查点
|
|
70
|
+
self._cleanup_old_checkpoints()
|
|
71
|
+
|
|
72
|
+
return checkpoint.checkpoint_id
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"保存对话检查点失败: {str(e)}")
|
|
75
|
+
return ""
|
|
76
|
+
|
|
77
|
+
def get_checkpoint(self, checkpoint_id: str) -> Optional[ConversationCheckpoint]:
|
|
78
|
+
"""
|
|
79
|
+
获取指定ID的对话检查点
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
checkpoint_id: 检查点ID
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Optional[ConversationCheckpoint]: 对话检查点对象,如果不存在则返回None
|
|
86
|
+
"""
|
|
87
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint_id}.json")
|
|
88
|
+
|
|
89
|
+
if not os.path.exists(file_path):
|
|
90
|
+
logger.warning(f"对话检查点不存在: {checkpoint_id}")
|
|
91
|
+
return None
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
95
|
+
data = json.load(f)
|
|
96
|
+
|
|
97
|
+
return ConversationCheckpoint(**data)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
logger.error(f"读取对话检查点失败: {str(e)}")
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
def get_latest_checkpoint(self) -> Optional[ConversationCheckpoint]:
|
|
103
|
+
"""
|
|
104
|
+
获取最新的对话检查点
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Optional[ConversationCheckpoint]: 最新的对话检查点对象,如果不存在则返回None
|
|
108
|
+
"""
|
|
109
|
+
checkpoints = self._get_all_checkpoints()
|
|
110
|
+
|
|
111
|
+
if not checkpoints:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
# 按时间戳降序排序
|
|
115
|
+
checkpoints.sort(key=lambda x: x.timestamp, reverse=True)
|
|
116
|
+
|
|
117
|
+
return checkpoints[0] if checkpoints else None
|
|
118
|
+
|
|
119
|
+
def delete_checkpoint(self, checkpoint_id: str) -> bool:
|
|
120
|
+
"""
|
|
121
|
+
删除指定的对话检查点
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
checkpoint_id: 检查点ID
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
bool: 是否成功删除
|
|
128
|
+
"""
|
|
129
|
+
file_path = os.path.join(self.store_dir, f"{checkpoint_id}.json")
|
|
130
|
+
|
|
131
|
+
if not os.path.exists(file_path):
|
|
132
|
+
logger.warning(f"要删除的对话检查点不存在: {checkpoint_id}")
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
os.remove(file_path)
|
|
137
|
+
logger.info(f"已删除对话检查点: {checkpoint_id}")
|
|
138
|
+
return True
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error(f"删除对话检查点失败: {str(e)}")
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def _get_all_checkpoints(self) -> List[ConversationCheckpoint]:
|
|
144
|
+
"""
|
|
145
|
+
获取所有对话检查点
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List[ConversationCheckpoint]: 对话检查点列表
|
|
149
|
+
"""
|
|
150
|
+
checkpoints = []
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
for filename in os.listdir(self.store_dir):
|
|
154
|
+
if filename.endswith('.json'):
|
|
155
|
+
file_path = os.path.join(self.store_dir, filename)
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
159
|
+
data = json.load(f)
|
|
160
|
+
|
|
161
|
+
checkpoint = ConversationCheckpoint(**data)
|
|
162
|
+
checkpoints.append(checkpoint)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.error(f"读取对话检查点文件失败 {filename}: {str(e)}")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error(f"获取所有对话检查点失败: {str(e)}")
|
|
167
|
+
|
|
168
|
+
return checkpoints
|
|
169
|
+
|
|
170
|
+
def _cleanup_old_checkpoints(self):
|
|
171
|
+
"""清理过期的检查点,保持历史记录数量在限制范围内"""
|
|
172
|
+
checkpoints = self._get_all_checkpoints()
|
|
173
|
+
|
|
174
|
+
if len(checkpoints) <= self.max_history:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
# 按时间戳降序排序
|
|
178
|
+
checkpoints.sort(key=lambda x: x.timestamp, reverse=True)
|
|
179
|
+
|
|
180
|
+
# 删除超出限制的旧检查点
|
|
181
|
+
for checkpoint in checkpoints[self.max_history:]:
|
|
182
|
+
self.delete_checkpoint(checkpoint.checkpoint_id)
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
文件变更管理模块的使用示例
|
|
3
|
+
|
|
4
|
+
展示如何使用文件变更管理模块进行文件变更的应用和撤销。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import json
|
|
10
|
+
from typing import Dict, Any
|
|
11
|
+
|
|
12
|
+
from autocoder.common.file_checkpoint.models import FileChange
|
|
13
|
+
from autocoder.common.file_checkpoint.manager import FileChangeManager
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def example_apply_changes(project_dir: str):
|
|
17
|
+
"""
|
|
18
|
+
示例:应用文件变更
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
project_dir: 项目目录
|
|
22
|
+
"""
|
|
23
|
+
print(f"示例:应用文件变更到项目 {project_dir}")
|
|
24
|
+
|
|
25
|
+
# 创建文件变更管理器
|
|
26
|
+
manager = FileChangeManager(project_dir)
|
|
27
|
+
|
|
28
|
+
# 准备文件变更
|
|
29
|
+
changes = {
|
|
30
|
+
"example.txt": FileChange(
|
|
31
|
+
file_path="example.txt",
|
|
32
|
+
content="这是一个示例文件\n用于演示文件变更管理模块的功能\n",
|
|
33
|
+
is_new=True
|
|
34
|
+
),
|
|
35
|
+
"README.md": FileChange(
|
|
36
|
+
file_path="README.md",
|
|
37
|
+
content="# 示例项目\n\n这是一个用于演示文件变更管理模块的示例项目。\n",
|
|
38
|
+
is_new=True
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# 应用变更
|
|
43
|
+
result = manager.apply_changes(changes)
|
|
44
|
+
|
|
45
|
+
# 输出结果
|
|
46
|
+
if result.success:
|
|
47
|
+
print(f"成功应用了 {len(result.change_ids)} 个文件变更")
|
|
48
|
+
for change_id in result.change_ids:
|
|
49
|
+
print(f" - 变更ID: {change_id}")
|
|
50
|
+
else:
|
|
51
|
+
print("应用变更失败")
|
|
52
|
+
for file_path, error in result.errors.items():
|
|
53
|
+
print(f" - {file_path}: {error}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def example_preview_changes(project_dir: str):
|
|
57
|
+
"""
|
|
58
|
+
示例:预览文件变更
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
project_dir: 项目目录
|
|
62
|
+
"""
|
|
63
|
+
print(f"示例:预览文件变更")
|
|
64
|
+
|
|
65
|
+
# 创建文件变更管理器
|
|
66
|
+
manager = FileChangeManager(project_dir)
|
|
67
|
+
|
|
68
|
+
# 准备文件变更
|
|
69
|
+
changes = {
|
|
70
|
+
"example.txt": FileChange(
|
|
71
|
+
file_path="example.txt",
|
|
72
|
+
content="这是一个修改后的示例文件\n用于演示文件变更管理模块的功能\n新增的一行\n",
|
|
73
|
+
is_new=False
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# 预览变更
|
|
78
|
+
diff_results = manager.preview_changes(changes)
|
|
79
|
+
|
|
80
|
+
# 输出差异
|
|
81
|
+
for file_path, diff_result in diff_results.items():
|
|
82
|
+
print(f"\n文件: {file_path}")
|
|
83
|
+
print(diff_result.get_diff_summary())
|
|
84
|
+
if diff_result.old_content is not None and not diff_result.is_new and not diff_result.is_deletion:
|
|
85
|
+
diff_text = manager.get_diff_text(diff_result.old_content, diff_result.new_content)
|
|
86
|
+
print("\n差异:")
|
|
87
|
+
print(diff_text)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def example_undo_changes(project_dir: str):
|
|
91
|
+
"""
|
|
92
|
+
示例:撤销文件变更
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
project_dir: 项目目录
|
|
96
|
+
"""
|
|
97
|
+
print(f"示例:撤销文件变更")
|
|
98
|
+
|
|
99
|
+
# 创建文件变更管理器
|
|
100
|
+
manager = FileChangeManager(project_dir)
|
|
101
|
+
|
|
102
|
+
# 撤销最近的变更
|
|
103
|
+
result = manager.undo_last_change()
|
|
104
|
+
|
|
105
|
+
# 输出结果
|
|
106
|
+
if result.success:
|
|
107
|
+
print(f"成功撤销了变更,恢复了 {len(result.restored_files)} 个文件")
|
|
108
|
+
for file_path in result.restored_files:
|
|
109
|
+
print(f" - {file_path}")
|
|
110
|
+
else:
|
|
111
|
+
print("撤销变更失败")
|
|
112
|
+
for file_path, error in result.errors.items():
|
|
113
|
+
print(f" - {file_path}: {error}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def example_get_history(project_dir: str):
|
|
117
|
+
"""
|
|
118
|
+
示例:获取变更历史
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
project_dir: 项目目录
|
|
122
|
+
"""
|
|
123
|
+
print(f"示例:获取变更历史")
|
|
124
|
+
|
|
125
|
+
# 创建文件变更管理器
|
|
126
|
+
manager = FileChangeManager(project_dir)
|
|
127
|
+
|
|
128
|
+
# 获取变更历史
|
|
129
|
+
changes = manager.get_change_history(limit=5)
|
|
130
|
+
|
|
131
|
+
# 输出历史记录
|
|
132
|
+
print(f"最近 {len(changes)} 条变更记录:")
|
|
133
|
+
for change in changes:
|
|
134
|
+
timestamp = change.timestamp
|
|
135
|
+
file_path = change.file_path
|
|
136
|
+
change_type = "新建" if change.is_new else "删除" if change.is_deletion else "修改"
|
|
137
|
+
print(f" - [{timestamp}] {change_type} {file_path} (ID: {change.change_id})")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def example_integration_with_agentic_edit():
|
|
141
|
+
"""
|
|
142
|
+
示例:与 AgenticEdit 集成
|
|
143
|
+
|
|
144
|
+
展示如何将文件变更管理模块集成到 AgenticEdit 中
|
|
145
|
+
"""
|
|
146
|
+
print("示例:与 AgenticEdit 集成")
|
|
147
|
+
|
|
148
|
+
# 这是一个伪代码示例,展示如何修改 AgenticEdit.apply_changes 方法
|
|
149
|
+
code = """
|
|
150
|
+
def apply_changes(self):
|
|
151
|
+
\"\"\"
|
|
152
|
+
Apply all tracked file changes to the original project directory.
|
|
153
|
+
\"\"\"
|
|
154
|
+
from autocoder.common.file_checkpoint.models import FileChange
|
|
155
|
+
from autocoder.common.file_checkpoint.manager import FileChangeManager
|
|
156
|
+
|
|
157
|
+
# 创建文件变更管理器
|
|
158
|
+
manager = FileChangeManager(self.args.source_dir)
|
|
159
|
+
|
|
160
|
+
# 将影子系统的变更转换为 FileChange 对象
|
|
161
|
+
changes = {}
|
|
162
|
+
for file_path, change in self.get_all_file_changes().items():
|
|
163
|
+
changes[file_path] = FileChange(
|
|
164
|
+
file_path=file_path,
|
|
165
|
+
content=change.content,
|
|
166
|
+
is_new=not os.path.exists(file_path)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# 应用变更
|
|
170
|
+
result = manager.apply_changes(changes)
|
|
171
|
+
|
|
172
|
+
# 处理结果
|
|
173
|
+
if result.success:
|
|
174
|
+
# 继续执行原有的 Git 提交等逻辑
|
|
175
|
+
if not self.args.skip_commit:
|
|
176
|
+
try:
|
|
177
|
+
# ... 原有的 Git 提交代码 ...
|
|
178
|
+
except Exception as e:
|
|
179
|
+
# ... 原有的错误处理 ...
|
|
180
|
+
else:
|
|
181
|
+
# 处理应用变更失败的情况
|
|
182
|
+
error_messages = "\\n".join([f"{path}: {error}" for path, error in result.errors.items()])
|
|
183
|
+
self.printer.print_str_in_terminal(
|
|
184
|
+
f"Failed to apply changes:\\n{error_messages}",
|
|
185
|
+
style="red"
|
|
186
|
+
)
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
print(code)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def main():
|
|
193
|
+
"""主函数"""
|
|
194
|
+
if len(sys.argv) < 2:
|
|
195
|
+
print("用法: python examples.py <项目目录>")
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
project_dir = sys.argv[1]
|
|
199
|
+
|
|
200
|
+
# 运行示例
|
|
201
|
+
example_apply_changes(project_dir)
|
|
202
|
+
print("\n" + "-" * 50 + "\n")
|
|
203
|
+
|
|
204
|
+
example_preview_changes(project_dir)
|
|
205
|
+
print("\n" + "-" * 50 + "\n")
|
|
206
|
+
|
|
207
|
+
example_get_history(project_dir)
|
|
208
|
+
print("\n" + "-" * 50 + "\n")
|
|
209
|
+
|
|
210
|
+
example_undo_changes(project_dir)
|
|
211
|
+
print("\n" + "-" * 50 + "\n")
|
|
212
|
+
|
|
213
|
+
example_integration_with_agentic_edit()
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
if __name__ == "__main__":
|
|
217
|
+
main()
|