mofox-plugin-dev-toolkit 0.3.3__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.
- mofox_plugin_dev_toolkit-0.3.3.dist-info/METADATA +730 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/RECORD +46 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/WHEEL +5 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/entry_points.txt +2 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/licenses/LICENSE +674 -0
- mofox_plugin_dev_toolkit-0.3.3.dist-info/top_level.txt +1 -0
- mpdt/__init__.py +15 -0
- mpdt/__main__.py +8 -0
- mpdt/cli.py +316 -0
- mpdt/commands/__init__.py +9 -0
- mpdt/commands/check.py +498 -0
- mpdt/commands/dev.py +318 -0
- mpdt/commands/generate.py +448 -0
- mpdt/commands/init.py +686 -0
- mpdt/dev/bridge_plugin/__init__.py +17 -0
- mpdt/dev/bridge_plugin/cleanup_handler.py +65 -0
- mpdt/dev/bridge_plugin/dev_config.py +24 -0
- mpdt/dev/bridge_plugin/file_watcher.py +169 -0
- mpdt/dev/bridge_plugin/plugin.py +219 -0
- mpdt/templates/__init__.py +165 -0
- mpdt/templates/action_template.py +102 -0
- mpdt/templates/adapter_template.py +129 -0
- mpdt/templates/chatter_template.py +103 -0
- mpdt/templates/event_template.py +116 -0
- mpdt/templates/plus_command_template.py +150 -0
- mpdt/templates/prompt_template.py +92 -0
- mpdt/templates/router_template.py +175 -0
- mpdt/templates/tool_template.py +98 -0
- mpdt/utils/__init__.py +10 -0
- mpdt/utils/code_parser.py +401 -0
- mpdt/utils/color_printer.py +99 -0
- mpdt/utils/config_loader.py +171 -0
- mpdt/utils/config_manager.py +297 -0
- mpdt/utils/file_ops.py +207 -0
- mpdt/utils/license_generator.py +980 -0
- mpdt/utils/plugin_parser.py +195 -0
- mpdt/utils/template_engine.py +112 -0
- mpdt/validators/__init__.py +26 -0
- mpdt/validators/auto_fix_validator.py +990 -0
- mpdt/validators/base.py +129 -0
- mpdt/validators/component_validator.py +842 -0
- mpdt/validators/config_validator.py +119 -0
- mpdt/validators/metadata_validator.py +107 -0
- mpdt/validators/structure_validator.py +72 -0
- mpdt/validators/style_validator.py +117 -0
- mpdt/validators/type_validator.py +206 -0
mpdt/utils/file_ops.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"""
|
|
2
|
+
文件操作工具
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def ensure_dir(path: Path | str) -> Path:
|
|
10
|
+
"""
|
|
11
|
+
确保目录存在,如果不存在则创建
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
path: 目录路径
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Path 对象
|
|
18
|
+
"""
|
|
19
|
+
path = Path(path)
|
|
20
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
return path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def safe_write_file(path: Path | str, content: str, force: bool = False) -> bool:
|
|
25
|
+
"""
|
|
26
|
+
安全地写入文件
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
path: 文件路径
|
|
30
|
+
content: 文件内容
|
|
31
|
+
force: 是否覆盖已存在的文件
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
是否写入成功
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
FileExistsError: 文件已存在且 force=False
|
|
38
|
+
"""
|
|
39
|
+
path = Path(path)
|
|
40
|
+
|
|
41
|
+
if path.exists() and not force:
|
|
42
|
+
raise FileExistsError(f"文件已存在: {path}")
|
|
43
|
+
|
|
44
|
+
# 确保父目录存在
|
|
45
|
+
ensure_dir(path.parent)
|
|
46
|
+
|
|
47
|
+
# 写入文件
|
|
48
|
+
path.write_text(content, encoding="utf-8")
|
|
49
|
+
return True
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def copy_directory(src: Path | str, dst: Path | str, force: bool = False) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
复制整个目录
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
src: 源目录
|
|
58
|
+
dst: 目标目录
|
|
59
|
+
force: 是否覆盖已存在的目录
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
是否复制成功
|
|
63
|
+
"""
|
|
64
|
+
src = Path(src)
|
|
65
|
+
dst = Path(dst)
|
|
66
|
+
|
|
67
|
+
if dst.exists() and not force:
|
|
68
|
+
raise FileExistsError(f"目标目录已存在: {dst}")
|
|
69
|
+
|
|
70
|
+
if dst.exists():
|
|
71
|
+
shutil.rmtree(dst)
|
|
72
|
+
|
|
73
|
+
shutil.copytree(src, dst)
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def list_python_files(path: Path | str, recursive: bool = True) -> list[Path]:
|
|
78
|
+
"""
|
|
79
|
+
列出目录中的所有 Python 文件
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
path: 目录路径
|
|
83
|
+
recursive: 是否递归搜索
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Python 文件路径列表
|
|
87
|
+
"""
|
|
88
|
+
path = Path(path)
|
|
89
|
+
|
|
90
|
+
if recursive:
|
|
91
|
+
return list(path.rglob("*.py"))
|
|
92
|
+
else:
|
|
93
|
+
return list(path.glob("*.py"))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def validate_plugin_name(name: str) -> bool:
|
|
97
|
+
"""
|
|
98
|
+
验证插件名称是否符合规范
|
|
99
|
+
|
|
100
|
+
规范: 使用小写字母、数字和下划线,以字母开头
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
name: 插件名称
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
是否符合规范
|
|
107
|
+
"""
|
|
108
|
+
import re
|
|
109
|
+
return bool(re.match(r"^[a-z][a-z0-9_]*$", name))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def validate_component_name(name: str) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
验证组件名称是否符合规范
|
|
115
|
+
|
|
116
|
+
规范: 支持 snake_case 或 PascalCase
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
name: 组件名称
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
是否符合规范
|
|
123
|
+
"""
|
|
124
|
+
import re
|
|
125
|
+
# 支持 snake_case 或 PascalCase
|
|
126
|
+
return bool(re.match(r"^[a-zA-Z][a-zA-Z0-9_]*$", name))
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_git_user_info() -> dict[str, str]:
|
|
130
|
+
"""
|
|
131
|
+
从 git config 获取用户信息
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
包含 name 和 email 的字典
|
|
135
|
+
"""
|
|
136
|
+
import subprocess
|
|
137
|
+
|
|
138
|
+
result = {"name": "", "email": ""}
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
name = subprocess.run(
|
|
142
|
+
["git", "config", "--get", "user.name"],
|
|
143
|
+
capture_output=True,
|
|
144
|
+
text=True,
|
|
145
|
+
check=False,
|
|
146
|
+
encoding='utf-8',
|
|
147
|
+
errors='ignore'
|
|
148
|
+
)
|
|
149
|
+
if name.returncode == 0:
|
|
150
|
+
result["name"] = name.stdout.strip()
|
|
151
|
+
|
|
152
|
+
email = subprocess.run(
|
|
153
|
+
["git", "config", "--get", "user.email"],
|
|
154
|
+
capture_output=True,
|
|
155
|
+
text=True,
|
|
156
|
+
check=False,
|
|
157
|
+
encoding='utf-8',
|
|
158
|
+
errors='ignore'
|
|
159
|
+
)
|
|
160
|
+
if email.returncode == 0:
|
|
161
|
+
result["email"] = email.stdout.strip()
|
|
162
|
+
except Exception:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
return result
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def to_pascal_case(snake_str: str) -> str:
|
|
169
|
+
"""
|
|
170
|
+
将 snake_case 转换为 PascalCase
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
snake_str: snake_case 字符串
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
PascalCase 字符串
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
>>> to_pascal_case("my_action")
|
|
180
|
+
'MyAction'
|
|
181
|
+
>>> to_pascal_case("test_command_handler")
|
|
182
|
+
'TestCommandHandler'
|
|
183
|
+
"""
|
|
184
|
+
return "".join(word.capitalize() for word in snake_str.split("_"))
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def to_snake_case(pascal_str: str) -> str:
|
|
188
|
+
"""
|
|
189
|
+
将 PascalCase 转换为 snake_case
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
pascal_str: PascalCase 字符串
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
snake_case 字符串
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
>>> to_snake_case("MyAction")
|
|
199
|
+
'my_action'
|
|
200
|
+
>>> to_snake_case("TestCommandHandler")
|
|
201
|
+
'test_command_handler'
|
|
202
|
+
"""
|
|
203
|
+
import re
|
|
204
|
+
# 在大写字母前插入下划线
|
|
205
|
+
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', pascal_str)
|
|
206
|
+
# 处理连续大写字母
|
|
207
|
+
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|