auto-coder 0.1.366__py3-none-any.whl → 0.1.369__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.366
3
+ Version: 0.1.369
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
14
14
  autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
15
15
  autocoder/models.py,sha256=Gu50IATQtZtgEir1PpCfwgH6o4ygw6XqqbQRj3lx5dU,13798
16
16
  autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
17
- autocoder/version.py,sha256=A7C7tSowjNmr_-5lcW2Fq9hyKMLQ4SiVmIfg6RE9CAM,23
17
+ autocoder/version.py,sha256=JeehxQcnVWSrItvPu8R4Gmgu-fvSBfT0YK_GQNxNeqY,25
18
18
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
20
20
  autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
@@ -142,6 +142,11 @@ autocoder/common/text.py,sha256=KGRQq314GHBmY4MWG8ossRoQi1_DTotvhxchpn78c-k,1003
142
142
  autocoder/common/token_cost_caculate.py,sha256=MSWJtl7YpQSUt-gFQoqUcJMblyPqHXe2ZioiZOFkV80,10085
143
143
  autocoder/common/types.py,sha256=Cw_4RH-rGmAgQE-Ck69maMAMqlPCDA4Yj37QmuUY0mQ,713
144
144
  autocoder/common/utils_code_auto_generate.py,sha256=sqtLmVxRveRwqLwJ8UlDgwngwmh2AAo3vgl-I-ALcDQ,4597
145
+ autocoder/common/command_file_manager/__init__.py,sha256=ardiG_iuWhurfbN6RO4wij2U7Dmafnxn2vgWYYxKqDM,970
146
+ autocoder/common/command_file_manager/examples.py,sha256=kWNVK_iDvQn5NtA8D791Oxkj8xP5DWSD72PcMYIgwBQ,4067
147
+ autocoder/common/command_file_manager/manager.py,sha256=XmCOaWgGo3RemU1ubYOoNr40eaRqGHCwX7GS2vt3voo,5935
148
+ autocoder/common/command_file_manager/models.py,sha256=cDYRSQGK5O4nFI0GGHQvHcPeNKUxKePZQCgy7Mfw3kU,3471
149
+ autocoder/common/command_file_manager/utils.py,sha256=OA9OhB1VnkvnFGof9p6jGfIVx31usUcqucXZCkqNm4I,3488
145
150
  autocoder/common/conversations/__init__.py,sha256=xGZeOFrDsgg2fkPK1zmvYBDhAyX66FtgOcZaxhYKJXU,1338
146
151
  autocoder/common/conversations/compatibility.py,sha256=WuBXB4-dw5X9LUMsB16VWbihvRZQ1tT99m6zuBwDfqE,9606
147
152
  autocoder/common/conversations/conversation_manager.py,sha256=ZhuhfSdOTncqgy3nHPoEU7Cg0dCsSl-VPcvLbUlL2Tk,18295
@@ -187,7 +192,7 @@ autocoder/common/v2/code_editblock_manager.py,sha256=DMwJw-FAM6VyaBQV3p4xespHpgZ
187
192
  autocoder/common/v2/code_manager.py,sha256=C403bS-f6urixwitlKHcml-J03hci-UyNwHJOqBiY6Q,9182
188
193
  autocoder/common/v2/code_strict_diff_manager.py,sha256=Bys7tFAq4G03R1zUZuxrszBTvP4QB96jIw2y5BDLyRM,9424
189
194
  autocoder/common/v2/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
190
- autocoder/common/v2/agent/agentic_edit.py,sha256=Y4eQwEHHmtt8oQNHzkYcFQW2l8aEDQ4V37qXQ4YCw0o,115955
195
+ autocoder/common/v2/agent/agentic_edit.py,sha256=IqOku4jEWnmc_5Tn984t84TMDqIzIFCp6UhOVF3cY8k,116071
191
196
  autocoder/common/v2/agent/agentic_edit_conversation.py,sha256=pFgWPWHKhZ4J9EcFmIdiGsrSolTZuYcH1qkgKdD8nwk,7726
192
197
  autocoder/common/v2/agent/agentic_edit_types.py,sha256=nEcZc2MOZ_fQLaJX-YDha_x9Iim22ao4tykYM2iIy4k,4908
193
198
  autocoder/common/v2/agent/agentic_tool_display.py,sha256=-a-JTQLc4q03E_rdIILKMI0B6DHN-5gcGlrqq-mBYK4,7239
@@ -234,7 +239,7 @@ autocoder/dispacher/actions/plugins/action_regex_project.py,sha256=mziGcOtGafeAB
234
239
  autocoder/dispacher/actions/plugins/action_translate.py,sha256=GEn7dZA22jy5WyzINomjmzzB795p2Olg-CJla97lRF8,7744
235
240
  autocoder/events/__init__.py,sha256=1x_juwr9Ows2RADDa2LyI4QlmPxOVOXZeLO1cht-slM,1443
236
241
  autocoder/events/event_content.py,sha256=eLHf5M1BifSqhzzEBgAWKn3JD5_z_1mWeNdZ53TpMqk,12240
237
- autocoder/events/event_manager.py,sha256=ObbvPfNrrhC85w5VvsnLS9oy92oHEwqMN08qGPReNNA,11884
242
+ autocoder/events/event_manager.py,sha256=--V3sEdoSmYDCXqJXRMaMa1qWR9umuv9Cjd5Czjpavc,11887
238
243
  autocoder/events/event_manager_singleton.py,sha256=HOyDeiJGhLcC1yirHavtg-PG9faWXhQEIFrb0sZBtec,14621
239
244
  autocoder/events/event_store.py,sha256=y6tT3P-o3yhDptrKi-UmqI_ZBNg7v21FriI3f7lo_ME,12709
240
245
  autocoder/events/event_types.py,sha256=W_S6PTDIBdufcuPosgz64iITzQy79flL8s3hWB-vZ9o,3638
@@ -354,9 +359,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
354
359
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
355
360
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=t902pKxQ5xM7zgIHiAOsTPLwxhE6VuvXAqPy751S7fg,14096
356
361
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
357
- auto_coder-0.1.366.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
358
- auto_coder-0.1.366.dist-info/METADATA,sha256=Lr6o3S0iHyom02KXAn3fOC3uqCQDBDtbZpwGcq-_2qs,2775
359
- auto_coder-0.1.366.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
360
- auto_coder-0.1.366.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
361
- auto_coder-0.1.366.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
362
- auto_coder-0.1.366.dist-info/RECORD,,
362
+ auto_coder-0.1.369.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
363
+ auto_coder-0.1.369.dist-info/METADATA,sha256=4KZiRdKwKx1Zekk7MhVmWVSMpFmv6iwcEjgt7PuovUo,2775
364
+ auto_coder-0.1.369.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
365
+ auto_coder-0.1.369.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
366
+ auto_coder-0.1.369.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
367
+ auto_coder-0.1.369.dist-info/RECORD,,
@@ -0,0 +1,25 @@
1
+ """
2
+ 命令管理模块
3
+
4
+ 该模块提供了一种机制来管理和分析 .autocodercommands 目录中的命令文件。
5
+ 主要功能包括:
6
+ 1. 列出命令目录中的所有命令文件
7
+ 2. 读取指定的命令文件内容
8
+ 3. 分析命令文件,提取其中的Jinja2变量
9
+ 4. 提供简单直观的API,便于集成到现有的项目中
10
+ """
11
+
12
+ from autocoder.common.command_file_manager.models import (
13
+ CommandFile, JinjaVariable, CommandFileAnalysisResult, ListCommandsResult
14
+ )
15
+ from autocoder.common.command_file_manager.manager import CommandManager
16
+ from autocoder.common.command_file_manager.utils import (
17
+ extract_jinja2_variables, extract_jinja2_variables_with_metadata,
18
+ analyze_command_file, is_command_file
19
+ )
20
+
21
+ __all__ = [
22
+ 'CommandFile', 'JinjaVariable', 'CommandFileAnalysisResult', 'ListCommandsResult',
23
+ 'CommandManager', 'extract_jinja2_variables', 'extract_jinja2_variables_with_metadata',
24
+ 'analyze_command_file', 'is_command_file'
25
+ ]
@@ -0,0 +1,142 @@
1
+ """
2
+ 命令管理模块的使用示例
3
+
4
+ 展示如何使用命令管理模块的主要功能。
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ from typing import Dict, Set, List
11
+
12
+ from autocoder.common.command_manager import (
13
+ CommandManager, CommandFile, JinjaVariable, CommandFileAnalysisResult
14
+ )
15
+
16
+
17
+ def setup_test_environment():
18
+ """设置测试环境,创建示例命令文件"""
19
+ # 创建临时命令目录
20
+ test_dir = os.path.join(os.path.dirname(__file__), "test_commands")
21
+ os.makedirs(test_dir, exist_ok=True)
22
+
23
+ # 创建示例命令文件
24
+ example1 = os.path.join(test_dir, "example1.autocodercommand")
25
+ with open(example1, "w") as f:
26
+ f.write("""
27
+ {# @var: project_name, default: MyProject, description: 项目名称 #}
28
+ {# @var: author, default: User, description: 作者姓名 #}
29
+ # {{ project_name }}
30
+
31
+ 这是一个由 {{ author }} 创建的项目。
32
+
33
+ {% if include_license %}
34
+ ## 许可证
35
+ 本项目使用 {{ license_type }} 许可证。
36
+ {% endif %}
37
+ """)
38
+
39
+ # 创建子目录和另一个示例文件
40
+ sub_dir = os.path.join(test_dir, "subdir")
41
+ os.makedirs(sub_dir, exist_ok=True)
42
+
43
+ example2 = os.path.join(sub_dir, "example2.j2")
44
+ with open(example2, "w") as f:
45
+ f.write("""
46
+ # {{ project_name }} 配置
47
+
48
+ version: {{ version }}
49
+ environment: {{ env }}
50
+ """)
51
+
52
+ return test_dir
53
+
54
+
55
+ def list_command_files_example(manager: CommandManager):
56
+ """列出命令文件示例"""
57
+ print("\n=== 列出命令文件 ===")
58
+
59
+ # 不递归列出
60
+ print("不递归列出:")
61
+ result = manager.list_command_files(recursive=False)
62
+ if result.success:
63
+ for file in result.command_files:
64
+ print(f"- {file}")
65
+ else:
66
+ print(f"错误: {result.errors}")
67
+
68
+ # 递归列出
69
+ print("\n递归列出:")
70
+ result = manager.list_command_files(recursive=True)
71
+ if result.success:
72
+ for file in result.command_files:
73
+ print(f"- {file}")
74
+ else:
75
+ print(f"错误: {result.errors}")
76
+
77
+
78
+ def read_command_file_example(manager: CommandManager):
79
+ """读取命令文件示例"""
80
+ print("\n=== 读取命令文件 ===")
81
+
82
+ # 读取第一个示例文件
83
+ command_file = manager.read_command_file("example1.autocodercommand")
84
+ if command_file:
85
+ print(f"文件名: {command_file.file_name}")
86
+ print(f"文件路径: {command_file.file_path}")
87
+ print("文件内容:")
88
+ print(command_file.content)
89
+ else:
90
+ print("文件不存在或读取失败")
91
+
92
+
93
+ def analyze_command_file_example(manager: CommandManager):
94
+ """分析命令文件示例"""
95
+ print("\n=== 分析命令文件 ===")
96
+
97
+ # 分析第一个示例文件
98
+ analysis = manager.analyze_command_file("example1.autocodercommand")
99
+ if analysis:
100
+ print(f"文件名: {analysis.file_name}")
101
+ print(f"原始变量: {', '.join(analysis.raw_variables)}")
102
+ print("变量详情:")
103
+ for var in analysis.variables:
104
+ print(f" - {var.name}")
105
+ if var.default_value:
106
+ print(f" 默认值: {var.default_value}")
107
+ if var.description:
108
+ print(f" 描述: {var.description}")
109
+ else:
110
+ print("文件分析失败")
111
+
112
+
113
+ def get_all_variables_example(manager: CommandManager):
114
+ """获取所有变量示例"""
115
+ print("\n=== 获取所有变量 ===")
116
+
117
+ # 获取所有命令文件中的变量
118
+ variables_map = manager.get_all_variables(recursive=True)
119
+ for file, variables in variables_map.items():
120
+ print(f"文件: {file}")
121
+ print(f"变量: {', '.join(variables)}")
122
+ print()
123
+
124
+
125
+ def main():
126
+ """主函数"""
127
+ # 设置测试环境
128
+ test_dir = setup_test_environment()
129
+ print(f"测试目录: {test_dir}")
130
+
131
+ # 创建命令管理器
132
+ manager = CommandManager(test_dir)
133
+
134
+ # 运行示例
135
+ list_command_files_example(manager)
136
+ read_command_file_example(manager)
137
+ analyze_command_file_example(manager)
138
+ get_all_variables_example(manager)
139
+
140
+
141
+ if __name__ == "__main__":
142
+ main()
@@ -0,0 +1,175 @@
1
+ """
2
+ 命令管理器
3
+
4
+ 整个模块的主入口,提供高层次的API接口,用于列出、读取和分析命令文件。
5
+ """
6
+
7
+ import os
8
+ import logging
9
+ from typing import Dict, List, Optional, Set, Tuple
10
+
11
+ from autocoder.common.command_file_manager.models import (
12
+ CommandFile, JinjaVariable, CommandFileAnalysisResult, ListCommandsResult
13
+ )
14
+ from autocoder.common.command_file_manager.utils import (
15
+ extract_jinja2_variables, extract_jinja2_variables_with_metadata,
16
+ analyze_command_file, is_command_file
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class CommandManager:
23
+ """命令管理器,提供高层次的API接口"""
24
+
25
+ def __init__(self, commands_dir: str):
26
+ """
27
+ 初始化命令管理器
28
+
29
+ Args:
30
+ commands_dir: 命令文件目录路径
31
+ """
32
+ self.commands_dir = os.path.abspath(commands_dir)
33
+
34
+ # 确保目录存在
35
+ if not os.path.exists(self.commands_dir):
36
+ logger.warning(f"命令目录不存在: {self.commands_dir}")
37
+ os.makedirs(self.commands_dir, exist_ok=True)
38
+ logger.info(f"已创建命令目录: {self.commands_dir}")
39
+
40
+ def list_command_files(self, recursive: bool = False) -> ListCommandsResult:
41
+ """
42
+ 列出命令目录中的所有命令文件
43
+
44
+ Args:
45
+ recursive: 是否递归搜索子目录
46
+
47
+ Returns:
48
+ ListCommandsResult: 列出结果
49
+ """
50
+ result = ListCommandsResult(success=True)
51
+
52
+ try:
53
+ if recursive:
54
+ for root, _, files in os.walk(self.commands_dir):
55
+ for file in files:
56
+ if is_command_file(file):
57
+ file_path = os.path.join(root, file)
58
+ rel_path = os.path.relpath(file_path, self.commands_dir)
59
+ result.add_command_file(rel_path)
60
+ else:
61
+ for item in os.listdir(self.commands_dir):
62
+ item_path = os.path.join(self.commands_dir, item)
63
+ if os.path.isfile(item_path) and is_command_file(item):
64
+ result.add_command_file(item)
65
+ except Exception as e:
66
+ logger.error(f"列出命令文件时出错: {str(e)}")
67
+ result.add_error(self.commands_dir, f"列出命令文件时出错: {str(e)}")
68
+
69
+ return result
70
+
71
+ def read_command_file(self, file_name: str) -> Optional[CommandFile]:
72
+ """
73
+ 读取指定的命令文件
74
+
75
+ Args:
76
+ file_name: 命令文件名或相对路径
77
+
78
+ Returns:
79
+ Optional[CommandFile]: 命令文件对象,如果文件不存在则返回None
80
+ """
81
+ file_path = os.path.join(self.commands_dir, file_name)
82
+
83
+ if not os.path.isfile(file_path):
84
+ logger.warning(f"命令文件不存在: {file_path}")
85
+ return None
86
+
87
+ try:
88
+ with open(file_path, 'r', encoding='utf-8') as f:
89
+ content = f.read()
90
+
91
+ return CommandFile(
92
+ file_path=file_path,
93
+ file_name=os.path.basename(file_path),
94
+ content=content
95
+ )
96
+ except Exception as e:
97
+ logger.error(f"读取命令文件时出错: {str(e)}")
98
+ return None
99
+
100
+ def analyze_command_file(self, file_name: str) -> Optional[CommandFileAnalysisResult]:
101
+ """
102
+ 分析指定的命令文件,提取其中的Jinja2变量
103
+
104
+ Args:
105
+ file_name: 命令文件名或相对路径
106
+
107
+ Returns:
108
+ Optional[CommandFileAnalysisResult]: 分析结果,如果文件不存在则返回None
109
+ """
110
+ command_file = self.read_command_file(file_name)
111
+ if command_file is None:
112
+ return None
113
+
114
+ try:
115
+ return analyze_command_file(command_file.file_path, command_file.content)
116
+ except Exception as e:
117
+ logger.error(f"分析命令文件时出错: {str(e)}")
118
+ return None
119
+
120
+ def get_all_variables(self, recursive: bool = False) -> Dict[str, Set[str]]:
121
+ """
122
+ 获取所有命令文件中的变量
123
+
124
+ Args:
125
+ recursive: 是否递归搜索子目录
126
+
127
+ Returns:
128
+ Dict[str, Set[str]]: 文件路径到变量集合的映射
129
+ """
130
+ result: Dict[str, Set[str]] = {}
131
+
132
+ list_result = self.list_command_files(recursive)
133
+ if not list_result.success:
134
+ logger.error("获取命令文件列表失败")
135
+ return result
136
+
137
+ for file_name in list_result.command_files:
138
+ command_file = self.read_command_file(file_name)
139
+ if command_file is None:
140
+ continue
141
+
142
+ try:
143
+ variables = extract_jinja2_variables(command_file.content)
144
+ result[file_name] = variables
145
+ except Exception as e:
146
+ logger.error(f"提取文件 {file_name} 的变量时出错: {str(e)}")
147
+
148
+ return result
149
+
150
+ def get_command_file_path(self, file_name: str) -> str:
151
+ """
152
+ 获取命令文件的完整路径
153
+
154
+ Args:
155
+ file_name: 命令文件名或相对路径
156
+
157
+ Returns:
158
+ str: 命令文件的完整路径
159
+ """
160
+ return os.path.join(self.commands_dir, file_name)
161
+
162
+ def _get_absolute_path(self, file_path: str) -> str:
163
+ """
164
+ 获取文件的绝对路径
165
+
166
+ Args:
167
+ file_path: 文件相对路径或绝对路径
168
+
169
+ Returns:
170
+ str: 文件的绝对路径
171
+ """
172
+ if os.path.isabs(file_path):
173
+ return file_path
174
+ else:
175
+ return os.path.join(self.commands_dir, file_path)
@@ -0,0 +1,119 @@
1
+ """
2
+ 命令管理模块的数据模型
3
+
4
+ 定义了表示命令文件、命令内容和操作结果的数据类。
5
+ """
6
+
7
+ import os
8
+ import re
9
+ from dataclasses import dataclass, field
10
+ from typing import Dict, List, Optional, Set
11
+
12
+
13
+ @dataclass
14
+ class CommandFile:
15
+ """表示单个命令文件的信息"""
16
+ file_path: str
17
+ file_name: str
18
+ content: str
19
+
20
+ @classmethod
21
+ def from_dict(cls, data: Dict) -> 'CommandFile':
22
+ """从字典创建CommandFile对象"""
23
+ return cls(
24
+ file_path=data['file_path'],
25
+ file_name=data['file_name'],
26
+ content=data['content']
27
+ )
28
+
29
+ def to_dict(self) -> Dict:
30
+ """将CommandFile对象转换为字典"""
31
+ return {
32
+ 'file_path': self.file_path,
33
+ 'file_name': self.file_name,
34
+ 'content': self.content
35
+ }
36
+
37
+
38
+ @dataclass
39
+ class JinjaVariable:
40
+ """表示从命令文件中提取的Jinja2变量"""
41
+ name: str
42
+ default_value: Optional[str] = None
43
+ description: Optional[str] = None
44
+
45
+ @classmethod
46
+ def from_dict(cls, data: Dict) -> 'JinjaVariable':
47
+ """从字典创建JinjaVariable对象"""
48
+ return cls(
49
+ name=data['name'],
50
+ default_value=data.get('default_value'),
51
+ description=data.get('description')
52
+ )
53
+
54
+ def to_dict(self) -> Dict:
55
+ """将JinjaVariable对象转换为字典"""
56
+ return {
57
+ 'name': self.name,
58
+ 'default_value': self.default_value,
59
+ 'description': self.description
60
+ }
61
+
62
+
63
+ @dataclass
64
+ class CommandFileAnalysisResult:
65
+ """表示命令文件分析的结果"""
66
+ file_path: str
67
+ file_name: str
68
+ variables: List[JinjaVariable] = field(default_factory=list)
69
+ raw_variables: Set[str] = field(default_factory=set)
70
+
71
+ def add_variable(self, variable: JinjaVariable) -> None:
72
+ """添加一个变量"""
73
+ self.variables.append(variable)
74
+ self.raw_variables.add(variable.name)
75
+
76
+ @classmethod
77
+ def from_dict(cls, data: Dict) -> 'CommandFileAnalysisResult':
78
+ """从字典创建CommandFileAnalysisResult对象"""
79
+ result = cls(
80
+ file_path=data['file_path'],
81
+ file_name=data['file_name'],
82
+ raw_variables=set(data.get('raw_variables', []))
83
+ )
84
+
85
+ for var_data in data.get('variables', []):
86
+ result.variables.append(JinjaVariable.from_dict(var_data))
87
+
88
+ return result
89
+
90
+ def to_dict(self) -> Dict:
91
+ """将CommandFileAnalysisResult对象转换为字典"""
92
+ return {
93
+ 'file_path': self.file_path,
94
+ 'file_name': self.file_name,
95
+ 'variables': [var.to_dict() for var in self.variables],
96
+ 'raw_variables': list(self.raw_variables)
97
+ }
98
+
99
+
100
+ @dataclass
101
+ class ListCommandsResult:
102
+ """表示列出命令文件的结果"""
103
+ success: bool
104
+ command_files: List[str] = field(default_factory=list)
105
+ errors: Dict[str, str] = field(default_factory=dict)
106
+
107
+ @property
108
+ def has_errors(self) -> bool:
109
+ """是否有错误"""
110
+ return len(self.errors) > 0
111
+
112
+ def add_error(self, path: str, error_message: str) -> None:
113
+ """添加错误信息"""
114
+ self.errors[path] = error_message
115
+ self.success = False
116
+
117
+ def add_command_file(self, file_path: str) -> None:
118
+ """添加命令文件路径"""
119
+ self.command_files.append(file_path)
@@ -0,0 +1,119 @@
1
+ """
2
+ 命令管理模块的工具函数
3
+
4
+ 提供用于处理命令文件和提取Jinja2变量的工具函数。
5
+ """
6
+
7
+ import os
8
+ import re
9
+ from typing import List, Set, Dict, Optional, Tuple
10
+
11
+ from autocoder.common.command_file_manager.models import JinjaVariable, CommandFileAnalysisResult
12
+
13
+
14
+ def extract_jinja2_variables(content: str) -> Set[str]:
15
+ """
16
+ 从文本内容中提取Jinja2变量名
17
+
18
+ Args:
19
+ content: 文本内容
20
+
21
+ Returns:
22
+ Set[str]: 变量名集合
23
+ """
24
+ # 匹配 {{ variable }} 格式的变量
25
+ pattern = r'{{\s*([a-zA-Z0-9_]+)\s*}}'
26
+ variables = set(re.findall(pattern, content))
27
+
28
+ # 匹配 {% if variable %} 等控制结构中的变量
29
+ control_pattern = r'{%\s*(?:if|for|elif|unless)\s+(?:not\s+)?([a-zA-Z0-9_]+)'
30
+ control_vars = set(re.findall(control_pattern, content))
31
+
32
+ # 合并结果
33
+ variables.update(control_vars)
34
+
35
+ return variables
36
+
37
+
38
+ def extract_jinja2_variables_with_metadata(content: str) -> List[JinjaVariable]:
39
+ """
40
+ 从文本内容中提取Jinja2变量及其元数据(如默认值和描述)
41
+
42
+ 此函数会查找特殊注释格式来提取变量的元数据:
43
+ {# @var: variable_name, default: default_value, description: variable description #}
44
+
45
+ Args:
46
+ content: 文本内容
47
+
48
+ Returns:
49
+ List[JinjaVariable]: 变量对象列表
50
+ """
51
+ # 首先提取所有原始变量名
52
+ raw_variables = extract_jinja2_variables(content)
53
+
54
+ # 查找变量元数据注释
55
+ metadata_pattern = r'{#\s*@var:\s*([a-zA-Z0-9_]+)(?:\s*,\s*default:\s*([^,]+))?(?:\s*,\s*description:\s*([^#]+))?\s*#}'
56
+ metadata_matches = re.finditer(metadata_pattern, content)
57
+
58
+ # 创建变量字典,键为变量名
59
+ variables_dict: Dict[str, JinjaVariable] = {}
60
+
61
+ # 处理元数据注释
62
+ for match in metadata_matches:
63
+ var_name = match.group(1).strip()
64
+ default_value = match.group(2).strip() if match.group(2) else None
65
+ description = match.group(3).strip() if match.group(3) else None
66
+
67
+ variables_dict[var_name] = JinjaVariable(
68
+ name=var_name,
69
+ default_value=default_value,
70
+ description=description
71
+ )
72
+
73
+ # 为没有元数据的变量创建基本对象
74
+ for var_name in raw_variables:
75
+ if var_name not in variables_dict:
76
+ variables_dict[var_name] = JinjaVariable(name=var_name)
77
+
78
+ return list(variables_dict.values())
79
+
80
+
81
+ def analyze_command_file(file_path: str, file_content: str) -> CommandFileAnalysisResult:
82
+ """
83
+ 分析命令文件,提取其中的Jinja2变量
84
+
85
+ Args:
86
+ file_path: 文件路径
87
+ file_content: 文件内容
88
+
89
+ Returns:
90
+ CommandFileAnalysisResult: 分析结果
91
+ """
92
+ file_name = os.path.basename(file_path)
93
+
94
+ # 提取变量及其元数据
95
+ variables = extract_jinja2_variables_with_metadata(file_content)
96
+ raw_variable_names = extract_jinja2_variables(file_content)
97
+
98
+ # 创建分析结果
99
+ result = CommandFileAnalysisResult(
100
+ file_path=file_path,
101
+ file_name=file_name,
102
+ variables=variables,
103
+ raw_variables=raw_variable_names
104
+ )
105
+
106
+ return result
107
+
108
+
109
+ def is_command_file(file_name: str) -> bool:
110
+ """
111
+ 检查文件是否为命令文件
112
+
113
+ Args:
114
+ file_name: 文件名
115
+
116
+ Returns:
117
+ bool: 是否为命令文件
118
+ """
119
+ return file_name.endswith('.md')
@@ -936,7 +936,8 @@ class AgenticEdit:
936
936
  logger.info(
937
937
  "AgenticEdit analyze loop finished due to AttemptCompletion.")
938
938
  save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
939
- return
939
+ mark_event_should_finish = True
940
+ continue
940
941
 
941
942
  if isinstance(tool_obj, PlanModeRespondTool):
942
943
  logger.info(
@@ -946,7 +947,8 @@ class AgenticEdit:
946
947
  logger.info(
947
948
  "AgenticEdit analyze loop finished due to PlanModeRespond.")
948
949
  save_formatted_log(self.args.source_dir, json.dumps(conversations, ensure_ascii=False), "agentic_conversation")
949
- return
950
+ mark_event_should_finish = True
951
+ continue
950
952
 
951
953
  # Resolve the tool
952
954
  resolver_cls = TOOL_RESOLVER_MAP.get(type(tool_obj))
@@ -147,7 +147,7 @@ class EventManager:
147
147
  logger.debug(f"Waiting for response to event: {event.event_id}")
148
148
 
149
149
  # 无限等待响应,不设超时
150
- wait_result = blocker.wait(timeout=60.0) # 设置一个较长的超时时间(60秒)
150
+ wait_result = blocker.wait(timeout=60.0*60) # 设置一个较长的超时时间(60秒)
151
151
 
152
152
  if not wait_result:
153
153
  logger.warning(f"Timeout waiting for response to event: {event.event_id}")
autocoder/version.py CHANGED
@@ -1 +1,2 @@
1
- __version__ = "0.1.366"
1
+
2
+ __version__ = "0.1.369"