mofox-plugin-dev-toolkit 0.2.1__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.2.1.dist-info/METADATA +409 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/RECORD +43 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/WHEEL +5 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/entry_points.txt +2 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/licenses/LICENSE +674 -0
- mofox_plugin_dev_toolkit-0.2.1.dist-info/top_level.txt +1 -0
- mpdt/__init__.py +15 -0
- mpdt/__main__.py +8 -0
- mpdt/cli.py +314 -0
- mpdt/commands/__init__.py +9 -0
- mpdt/commands/check.py +316 -0
- mpdt/commands/dev.py +550 -0
- mpdt/commands/generate.py +366 -0
- mpdt/commands/init.py +487 -0
- mpdt/dev/bridge_plugin/__init__.py +17 -0
- mpdt/dev/bridge_plugin/discovery_server.py +126 -0
- mpdt/dev/bridge_plugin/plugin.py +258 -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/color_printer.py +99 -0
- mpdt/utils/config_loader.py +171 -0
- mpdt/utils/config_manager.py +297 -0
- mpdt/utils/file_ops.py +203 -0
- mpdt/utils/license_generator.py +980 -0
- mpdt/utils/plugin_parser.py +196 -0
- mpdt/utils/template_engine.py +112 -0
- mpdt/validators/__init__.py +26 -0
- mpdt/validators/auto_fix_validator.py +182 -0
- mpdt/validators/base.py +121 -0
- mpdt/validators/component_validator.py +415 -0
- mpdt/validators/config_validator.py +173 -0
- mpdt/validators/metadata_validator.py +125 -0
- mpdt/validators/structure_validator.py +70 -0
- mpdt/validators/style_validator.py +125 -0
- mpdt/validators/type_validator.py +223 -0
mpdt/__init__.py
ADDED
mpdt/__main__.py
ADDED
mpdt/cli.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI 主入口
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from mpdt import __version__
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group()
|
|
14
|
+
@click.version_option(version=__version__, prog_name="MPDT - MoFox-Bot 插件开发工具")
|
|
15
|
+
@click.option("--verbose", "-v", is_flag=True, help="详细输出模式")
|
|
16
|
+
@click.option("--no-color", is_flag=True, help="禁用彩色输出")
|
|
17
|
+
@click.pass_context
|
|
18
|
+
def cli(ctx: click.Context, verbose: bool, no_color: bool) -> None:
|
|
19
|
+
"""
|
|
20
|
+
MoFox Plugin Dev Toolkit - MoFox-Bot 插件开发工具
|
|
21
|
+
|
|
22
|
+
一个类似 Vite 的开发工具,用于快速创建、开发和测试 MoFox-Bot 插件。
|
|
23
|
+
"""
|
|
24
|
+
# 设置上下文对象
|
|
25
|
+
ctx.ensure_object(dict)
|
|
26
|
+
ctx.obj["verbose"] = verbose
|
|
27
|
+
ctx.obj["no_color"] = no_color
|
|
28
|
+
|
|
29
|
+
# 禁用彩色输出
|
|
30
|
+
if no_color:
|
|
31
|
+
console._color_system = None
|
|
32
|
+
|
|
33
|
+
if verbose:
|
|
34
|
+
console.print(f"[bold green]MPDT v{__version__}[/bold green]")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@cli.command()
|
|
38
|
+
@click.argument("plugin_name", required=False)
|
|
39
|
+
@click.option("--template", "-t", type=click.Choice(["basic", "action", "tool", "plus_command", "full", "adapter"]),
|
|
40
|
+
default="basic", help="插件模板类型")
|
|
41
|
+
@click.option("--author", "-a", help="作者名称")
|
|
42
|
+
@click.option("--license", "-l", type=click.Choice(["GPL-v3.0", "MIT", "Apache-2.0", "BSD-3-Clause"]),
|
|
43
|
+
default="GPL-v3.0", help="开源协议")
|
|
44
|
+
@click.option("--with-examples", is_flag=True, help="包含示例代码")
|
|
45
|
+
@click.option("--with-docs", is_flag=True, help="创建文档文件")
|
|
46
|
+
@click.option("--init-git/--no-init-git", default=None, help="是否初始化 Git 仓库")
|
|
47
|
+
@click.option("--output", "-o", type=click.Path(), help="输出目录")
|
|
48
|
+
@click.pass_context
|
|
49
|
+
def init(ctx: click.Context, plugin_name: str | None, template: str, author: str | None,
|
|
50
|
+
license: str, with_examples: bool, with_docs: bool, init_git: bool | None, output: str | None) -> None:
|
|
51
|
+
"""初始化新插件项目"""
|
|
52
|
+
from mpdt.commands.init import init_plugin
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
init_plugin(
|
|
56
|
+
plugin_name=plugin_name,
|
|
57
|
+
template=template,
|
|
58
|
+
author=author,
|
|
59
|
+
license_type=license,
|
|
60
|
+
with_examples=with_examples,
|
|
61
|
+
with_docs=with_docs,
|
|
62
|
+
init_git=init_git,
|
|
63
|
+
output_dir=output,
|
|
64
|
+
verbose=ctx.obj["verbose"],
|
|
65
|
+
)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
console.print(f"[bold red]❌ 初始化失败: {e}[/bold red]")
|
|
68
|
+
raise click.Abort()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@cli.command()
|
|
72
|
+
@click.argument("component_type", type=click.Choice(["action", "tool", "event", "adapter", "prompt", "plus-command","router","chatter"]), required=False)
|
|
73
|
+
@click.argument("component_name", required=False)
|
|
74
|
+
@click.option("--description", "-d", help="组件描述")
|
|
75
|
+
@click.option("--output", "-o", type=click.Path(), help="输出目录")
|
|
76
|
+
@click.option("--force", "-f", is_flag=True, help="覆盖已存在的文件")
|
|
77
|
+
@click.pass_context
|
|
78
|
+
def generate(ctx: click.Context, component_type: str | None, component_name: str | None, description: str | None,
|
|
79
|
+
output: str | None, force: bool) -> None:
|
|
80
|
+
"""生成插件组件(始终生成异步方法)
|
|
81
|
+
|
|
82
|
+
如果不提供参数,将进入交互式问答模式
|
|
83
|
+
"""
|
|
84
|
+
from mpdt.commands.generate import generate_component
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
generate_component(
|
|
88
|
+
component_type=component_type,
|
|
89
|
+
component_name=component_name,
|
|
90
|
+
description=description,
|
|
91
|
+
output_dir=output,
|
|
92
|
+
force=force,
|
|
93
|
+
verbose=ctx.obj["verbose"],
|
|
94
|
+
)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
console.print(f"[bold red]❌ 生成失败: {e}[/bold red]")
|
|
97
|
+
raise click.Abort()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@cli.command()
|
|
101
|
+
@click.argument("path", type=click.Path(exists=True), required=False, default=".")
|
|
102
|
+
@click.option("--level", "-l", type=click.Choice(["error", "warning", "info"]), default="warning",
|
|
103
|
+
help="显示的最低级别")
|
|
104
|
+
@click.option("--fix", is_flag=True, help="自动修复可修复的问题")
|
|
105
|
+
@click.option("--report", type=click.Choice(["console","markdown"]), default="console",
|
|
106
|
+
help="报告格式")
|
|
107
|
+
@click.option("--output", "-o", type=click.Path(), help="报告输出路径")
|
|
108
|
+
@click.option("--no-structure", is_flag=True, help="跳过结构检查")
|
|
109
|
+
@click.option("--no-metadata", is_flag=True, help="跳过元数据检查")
|
|
110
|
+
@click.option("--no-component", is_flag=True, help="跳过组件检查")
|
|
111
|
+
@click.option("--no-type", is_flag=True, help="跳过类型检查")
|
|
112
|
+
@click.option("--no-style", is_flag=True, help="跳过代码风格检查")
|
|
113
|
+
@click.pass_context
|
|
114
|
+
def check(ctx: click.Context, path: str, level: str, fix: bool, report: str, output: str | None,
|
|
115
|
+
no_structure: bool, no_metadata: bool, no_component: bool, no_type: bool,
|
|
116
|
+
no_style: bool) -> None:
|
|
117
|
+
"""对插件进行静态检查"""
|
|
118
|
+
from mpdt.commands.check import check_plugin
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
check_plugin(
|
|
122
|
+
plugin_path=path,
|
|
123
|
+
level=level,
|
|
124
|
+
auto_fix=fix,
|
|
125
|
+
report_format=report,
|
|
126
|
+
output_path=output,
|
|
127
|
+
skip_structure=no_structure,
|
|
128
|
+
skip_metadata=no_metadata,
|
|
129
|
+
skip_component=no_component,
|
|
130
|
+
skip_type=no_type,
|
|
131
|
+
skip_style=no_style,
|
|
132
|
+
verbose=ctx.obj["verbose"],
|
|
133
|
+
)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
console.print(f"[bold red]❌ 检查失败: {e}[/bold red]")
|
|
136
|
+
raise click.Abort()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@cli.command()
|
|
140
|
+
@click.option("--output", "-o", type=click.Path(), default="dist", help="输出目录")
|
|
141
|
+
@click.option("--with-docs", is_flag=True, help="包含文档")
|
|
142
|
+
@click.option("--format", type=click.Choice(["zip", "tar.gz", "wheel"]), default="zip", help="构建格式")
|
|
143
|
+
@click.option("--bump", type=click.Choice(["major", "minor", "patch"]), help="自动升级版本号")
|
|
144
|
+
@click.pass_context
|
|
145
|
+
def build(ctx: click.Context, output: str, with_docs: bool, format: str, bump: str | None) -> None:
|
|
146
|
+
"""构建和打包插件"""
|
|
147
|
+
console.print("[yellow]⚠️ 构建命令尚未实现[/yellow]")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@cli.command()
|
|
151
|
+
@click.option("--mmc-path", type=click.Path(exists=True), help="mmc 主程序路径")
|
|
152
|
+
@click.option("--plugin-path", type=click.Path(exists=True), help="插件路径(默认当前目录)")
|
|
153
|
+
@click.pass_context
|
|
154
|
+
def dev(ctx: click.Context, mmc_path: str | None, plugin_path: str | None) -> None:
|
|
155
|
+
"""启动开发模式,支持热重载"""
|
|
156
|
+
import asyncio
|
|
157
|
+
from pathlib import Path
|
|
158
|
+
from mpdt.commands.dev import dev_command
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
asyncio.run(dev_command(
|
|
162
|
+
plugin_path=Path(plugin_path) if plugin_path else None,
|
|
163
|
+
mmc_path=Path(mmc_path) if mmc_path else None
|
|
164
|
+
))
|
|
165
|
+
except Exception as e:
|
|
166
|
+
console.print(f"[bold red]❌ 启动失败: {e}[/bold red]")
|
|
167
|
+
if ctx.obj.get("verbose"):
|
|
168
|
+
import traceback
|
|
169
|
+
traceback.print_exc()
|
|
170
|
+
raise click.Abort()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@cli.group()
|
|
174
|
+
def config() -> None:
|
|
175
|
+
"""配置管理"""
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@config.command("init")
|
|
180
|
+
def config_init() -> None:
|
|
181
|
+
"""交互式配置向导"""
|
|
182
|
+
from mpdt.utils.config_manager import interactive_config
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
interactive_config()
|
|
186
|
+
except Exception as e:
|
|
187
|
+
console.print(f"[bold red]❌ 配置失败: {e}[/bold red]")
|
|
188
|
+
raise click.Abort()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@config.command("show")
|
|
192
|
+
def config_show() -> None:
|
|
193
|
+
"""显示当前配置"""
|
|
194
|
+
from mpdt.utils.config_manager import MPDTConfig
|
|
195
|
+
from rich.table import Table
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
config = MPDTConfig()
|
|
199
|
+
|
|
200
|
+
if not config.is_configured():
|
|
201
|
+
console.print("[yellow]⚠️ 未找到配置文件[/yellow]")
|
|
202
|
+
console.print("请运行 [cyan]mpdt config init[/cyan] 进行配置")
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
table = Table(title="MPDT 配置")
|
|
206
|
+
table.add_column("配置项", style="cyan")
|
|
207
|
+
table.add_column("值", style="green")
|
|
208
|
+
|
|
209
|
+
table.add_row("配置文件", str(config.config_path))
|
|
210
|
+
table.add_row("mmc 路径", str(config.mmc_path) if config.mmc_path else "[red]未配置[/red]")
|
|
211
|
+
table.add_row("虚拟环境类型", config.venv_type)
|
|
212
|
+
table.add_row("虚拟环境路径", str(config.venv_path) if config.venv_path else "[dim]无[/dim]")
|
|
213
|
+
table.add_row("自动重载", "是" if config.auto_reload else "否")
|
|
214
|
+
table.add_row("重载延迟", f"{config.reload_delay}秒")
|
|
215
|
+
|
|
216
|
+
console.print(table)
|
|
217
|
+
|
|
218
|
+
# 显示 Python 命令
|
|
219
|
+
console.print("\n[bold]Python 命令:[/bold]")
|
|
220
|
+
console.print(f" {' '.join(config.get_python_command())}")
|
|
221
|
+
|
|
222
|
+
except Exception as e:
|
|
223
|
+
console.print(f"[bold red]❌ 读取配置失败: {e}[/bold red]")
|
|
224
|
+
raise click.Abort()
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@config.command("test")
|
|
228
|
+
def config_test() -> None:
|
|
229
|
+
"""测试配置是否有效"""
|
|
230
|
+
from mpdt.utils.config_manager import MPDTConfig
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
config = MPDTConfig()
|
|
234
|
+
|
|
235
|
+
if not config.is_configured():
|
|
236
|
+
console.print("[yellow]⚠️ 未找到配置文件[/yellow]")
|
|
237
|
+
console.print("请运行 [cyan]mpdt config init[/cyan] 进行配置")
|
|
238
|
+
return
|
|
239
|
+
|
|
240
|
+
console.print("[cyan]正在验证配置...[/cyan]\n")
|
|
241
|
+
|
|
242
|
+
valid, errors = config.validate()
|
|
243
|
+
|
|
244
|
+
if valid:
|
|
245
|
+
console.print("[bold green]✓ 配置有效![/bold green]")
|
|
246
|
+
console.print(f"\nmmc 路径: {config.mmc_path}")
|
|
247
|
+
console.print(f"Python 命令: {' '.join(config.get_python_command())}")
|
|
248
|
+
else:
|
|
249
|
+
console.print("[bold red]✗ 配置验证失败:[/bold red]")
|
|
250
|
+
for error in errors:
|
|
251
|
+
console.print(f" - {error}")
|
|
252
|
+
console.print("\n请运行 [cyan]mpdt config init[/cyan] 重新配置")
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
console.print(f"[bold red]❌ 测试失败: {e}[/bold red]")
|
|
256
|
+
raise click.Abort()
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@config.command("set-mmc")
|
|
260
|
+
@click.argument("path", type=click.Path(exists=True))
|
|
261
|
+
def config_set_mmc(path: str) -> None:
|
|
262
|
+
"""设置 mmc 主程序路径"""
|
|
263
|
+
from pathlib import Path
|
|
264
|
+
from mpdt.utils.config_manager import MPDTConfig
|
|
265
|
+
|
|
266
|
+
try:
|
|
267
|
+
config = MPDTConfig()
|
|
268
|
+
config.mmc_path = Path(path)
|
|
269
|
+
config.save()
|
|
270
|
+
|
|
271
|
+
console.print(f"[green]✓ mmc 路径已设置: {path}[/green]")
|
|
272
|
+
|
|
273
|
+
except Exception as e:
|
|
274
|
+
console.print(f"[bold red]❌ 设置失败: {e}[/bold red]")
|
|
275
|
+
raise click.Abort()
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@config.command("set-venv")
|
|
279
|
+
@click.argument("path", type=click.Path(exists=True), required=False)
|
|
280
|
+
@click.option("--type", "venv_type", type=click.Choice(["venv", "uv", "conda", "poetry", "none"]),
|
|
281
|
+
default="venv", help="虚拟环境类型")
|
|
282
|
+
def config_set_venv(path: str | None, venv_type: str) -> None:
|
|
283
|
+
"""设置虚拟环境"""
|
|
284
|
+
from pathlib import Path
|
|
285
|
+
from mpdt.utils.config_manager import MPDTConfig
|
|
286
|
+
|
|
287
|
+
try:
|
|
288
|
+
config = MPDTConfig()
|
|
289
|
+
config.venv_type = venv_type
|
|
290
|
+
|
|
291
|
+
if venv_type == "none":
|
|
292
|
+
config.venv_path = None
|
|
293
|
+
console.print("[green]✓ 已设置为使用系统 Python[/green]")
|
|
294
|
+
elif path:
|
|
295
|
+
config.venv_path = Path(path)
|
|
296
|
+
console.print(f"[green]✓ 虚拟环境已设置: {path} (类型: {venv_type})[/green]")
|
|
297
|
+
else:
|
|
298
|
+
console.print("[red]❌ 请提供虚拟环境路径[/red]")
|
|
299
|
+
raise click.Abort()
|
|
300
|
+
|
|
301
|
+
config.save()
|
|
302
|
+
|
|
303
|
+
except Exception as e:
|
|
304
|
+
console.print(f"[bold red]❌ 设置失败: {e}[/bold red]")
|
|
305
|
+
raise click.Abort()
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def main() -> None:
|
|
309
|
+
"""主入口函数"""
|
|
310
|
+
cli(obj={})
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if __name__ == "__main__":
|
|
314
|
+
main()
|
mpdt/commands/check.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
静态检查命令实现
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from mpdt.utils.color_printer import console, print_error, print_info, print_success, print_warning
|
|
11
|
+
from mpdt.validators import (
|
|
12
|
+
AutoFixValidator,
|
|
13
|
+
ComponentValidator,
|
|
14
|
+
ConfigValidator,
|
|
15
|
+
MetadataValidator,
|
|
16
|
+
StructureValidator,
|
|
17
|
+
StyleValidator,
|
|
18
|
+
TypeValidator,
|
|
19
|
+
ValidationLevel,
|
|
20
|
+
ValidationResult,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_plugin(
|
|
25
|
+
plugin_path: str,
|
|
26
|
+
level: str = "warning",
|
|
27
|
+
auto_fix: bool = False,
|
|
28
|
+
report_format: str = "console",
|
|
29
|
+
output_path: str | None = None,
|
|
30
|
+
skip_structure: bool = False,
|
|
31
|
+
skip_metadata: bool = False,
|
|
32
|
+
skip_component: bool = False,
|
|
33
|
+
skip_type: bool = False,
|
|
34
|
+
skip_style: bool = False,
|
|
35
|
+
verbose: bool = False,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""
|
|
38
|
+
检查插件
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
plugin_path: 插件路径
|
|
42
|
+
level: 显示级别 (error, warning, info)
|
|
43
|
+
auto_fix: 自动修复
|
|
44
|
+
report_format: 报告格式 (console, markdown)
|
|
45
|
+
output_path: 输出路径
|
|
46
|
+
skip_structure: 跳过结构检查
|
|
47
|
+
skip_metadata: 跳过元数据检查
|
|
48
|
+
skip_component: 跳过组件检查
|
|
49
|
+
skip_type: 跳过类型检查
|
|
50
|
+
skip_style: 跳过代码风格检查
|
|
51
|
+
skip_security: 跳过安全检查
|
|
52
|
+
verbose: 详细输出
|
|
53
|
+
"""
|
|
54
|
+
path = Path(plugin_path).resolve()
|
|
55
|
+
|
|
56
|
+
if not path.exists():
|
|
57
|
+
print_error(f"插件路径不存在: {plugin_path}")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if not path.is_dir():
|
|
61
|
+
print_error(f"插件路径不是目录: {plugin_path}")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
console.print(Panel.fit(f"🔍 检查插件: [cyan]{path.name}[/cyan]", border_style="blue"))
|
|
65
|
+
|
|
66
|
+
# 收集所有验证结果
|
|
67
|
+
all_results: list[ValidationResult] = []
|
|
68
|
+
|
|
69
|
+
# 结构验证
|
|
70
|
+
if not skip_structure:
|
|
71
|
+
print_info("正在检查插件结构...")
|
|
72
|
+
validator = StructureValidator(path)
|
|
73
|
+
result = validator.validate()
|
|
74
|
+
all_results.append(result)
|
|
75
|
+
_print_validation_summary(result, verbose)
|
|
76
|
+
|
|
77
|
+
# 元数据验证
|
|
78
|
+
if not skip_metadata:
|
|
79
|
+
print_info("正在检查插件元数据...")
|
|
80
|
+
validator = MetadataValidator(path)
|
|
81
|
+
result = validator.validate()
|
|
82
|
+
all_results.append(result)
|
|
83
|
+
_print_validation_summary(result, verbose)
|
|
84
|
+
|
|
85
|
+
# 组件验证
|
|
86
|
+
if not skip_component:
|
|
87
|
+
print_info("正在检查组件元数据...")
|
|
88
|
+
validator = ComponentValidator(path)
|
|
89
|
+
result = validator.validate()
|
|
90
|
+
all_results.append(result)
|
|
91
|
+
_print_validation_summary(result, verbose)
|
|
92
|
+
|
|
93
|
+
# 配置验证
|
|
94
|
+
print_info("正在检查配置文件...")
|
|
95
|
+
validator = ConfigValidator(path)
|
|
96
|
+
result = validator.validate()
|
|
97
|
+
all_results.append(result)
|
|
98
|
+
_print_validation_summary(result, verbose)
|
|
99
|
+
|
|
100
|
+
# 类型检查
|
|
101
|
+
if not skip_type:
|
|
102
|
+
print_info("正在进行类型检查...")
|
|
103
|
+
validator = TypeValidator(path)
|
|
104
|
+
result = validator.validate()
|
|
105
|
+
all_results.append(result)
|
|
106
|
+
_print_validation_summary(result, verbose)
|
|
107
|
+
|
|
108
|
+
# 代码风格检查
|
|
109
|
+
if not skip_style:
|
|
110
|
+
print_info("正在检查代码风格...")
|
|
111
|
+
validator = StyleValidator(path, auto_fix=auto_fix)
|
|
112
|
+
result = validator.validate()
|
|
113
|
+
all_results.append(result)
|
|
114
|
+
_print_validation_summary(result, verbose)
|
|
115
|
+
|
|
116
|
+
# 自动修复(如果启用)
|
|
117
|
+
if auto_fix:
|
|
118
|
+
print_info("正在应用自动修复...")
|
|
119
|
+
auto_fixer = AutoFixValidator(path)
|
|
120
|
+
fix_result = auto_fixer.validate()
|
|
121
|
+
all_results.append(fix_result)
|
|
122
|
+
_print_validation_summary(fix_result, verbose)
|
|
123
|
+
|
|
124
|
+
# 生成总体报告
|
|
125
|
+
_print_overall_report(all_results, level)
|
|
126
|
+
|
|
127
|
+
# 保存报告(如果需要)
|
|
128
|
+
if output_path:
|
|
129
|
+
_save_report(all_results, output_path, report_format)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _print_validation_summary(result: ValidationResult, verbose: bool = False) -> None:
|
|
133
|
+
"""打印验证摘要
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
result: 验证结果
|
|
137
|
+
verbose: 是否详细输出
|
|
138
|
+
"""
|
|
139
|
+
if result.success:
|
|
140
|
+
print_success(f" ✓ {result.validator_name}: 通过")
|
|
141
|
+
else:
|
|
142
|
+
print_error(f" ✗ {result.validator_name}: 发现 {result.error_count} 个错误")
|
|
143
|
+
|
|
144
|
+
if verbose and result.issues:
|
|
145
|
+
for issue in result.issues:
|
|
146
|
+
_print_issue(issue)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _print_issue(issue) -> None:
|
|
150
|
+
"""打印单个问题
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
issue: 验证问题
|
|
154
|
+
"""
|
|
155
|
+
level_colors = {
|
|
156
|
+
ValidationLevel.ERROR: "red",
|
|
157
|
+
ValidationLevel.WARNING: "yellow",
|
|
158
|
+
ValidationLevel.INFO: "blue",
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
level_icons = {
|
|
162
|
+
ValidationLevel.ERROR: "✗",
|
|
163
|
+
ValidationLevel.WARNING: "⚠",
|
|
164
|
+
ValidationLevel.INFO: "ℹ",
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
color = level_colors.get(issue.level, "white")
|
|
168
|
+
icon = level_icons.get(issue.level, "•")
|
|
169
|
+
|
|
170
|
+
message = f" [{color}]{icon}[/{color}] {issue.message}"
|
|
171
|
+
|
|
172
|
+
if issue.file_path:
|
|
173
|
+
message += f" ([dim]{issue.file_path}"
|
|
174
|
+
if issue.line_number:
|
|
175
|
+
message += f":{issue.line_number}"
|
|
176
|
+
message += "[/dim])"
|
|
177
|
+
|
|
178
|
+
console.print(message)
|
|
179
|
+
|
|
180
|
+
if issue.suggestion:
|
|
181
|
+
console.print(f" [dim]💡 {issue.suggestion}[/dim]")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _print_overall_report(results: list[ValidationResult], level: str) -> None:
|
|
185
|
+
"""打印总体报告
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
results: 所有验证结果
|
|
189
|
+
level: 显示级别
|
|
190
|
+
"""
|
|
191
|
+
console.print()
|
|
192
|
+
console.print("=" * 60)
|
|
193
|
+
console.print()
|
|
194
|
+
|
|
195
|
+
# 统计总数
|
|
196
|
+
total_errors = sum(r.error_count for r in results)
|
|
197
|
+
total_warnings = sum(r.warning_count for r in results)
|
|
198
|
+
|
|
199
|
+
# 创建统计表格
|
|
200
|
+
table = Table(title="检查结果汇总", show_header=True, header_style="bold")
|
|
201
|
+
table.add_column("验证器", style="cyan")
|
|
202
|
+
table.add_column("错误", style="red")
|
|
203
|
+
table.add_column("警告", style="yellow")
|
|
204
|
+
table.add_column("信息", style="blue")
|
|
205
|
+
table.add_column("状态", style="green")
|
|
206
|
+
|
|
207
|
+
for result in results:
|
|
208
|
+
status = "✓ 通过" if result.success else "✗ 失败"
|
|
209
|
+
status_style = "green" if result.success else "red"
|
|
210
|
+
table.add_row(
|
|
211
|
+
result.validator_name,
|
|
212
|
+
str(result.error_count),
|
|
213
|
+
str(result.warning_count),
|
|
214
|
+
str(result.info_count),
|
|
215
|
+
f"[{status_style}]{status}[/{status_style}]",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
console.print(table)
|
|
219
|
+
console.print()
|
|
220
|
+
|
|
221
|
+
# 打印详细问题列表
|
|
222
|
+
level_filter = ValidationLevel(level)
|
|
223
|
+
for result in results:
|
|
224
|
+
filtered_issues = [
|
|
225
|
+
issue
|
|
226
|
+
for issue in result.issues
|
|
227
|
+
if (issue.level == ValidationLevel.ERROR)
|
|
228
|
+
or (issue.level == ValidationLevel.WARNING and level_filter in [ValidationLevel.WARNING, ValidationLevel.INFO])
|
|
229
|
+
or (issue.level == ValidationLevel.INFO and level_filter == ValidationLevel.INFO)
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
if filtered_issues:
|
|
233
|
+
console.print(f"\n[bold]{result.validator_name}:[/bold]")
|
|
234
|
+
for issue in filtered_issues:
|
|
235
|
+
_print_issue(issue)
|
|
236
|
+
|
|
237
|
+
# 总结
|
|
238
|
+
console.print()
|
|
239
|
+
if total_errors > 0:
|
|
240
|
+
print_error(f"发现 {total_errors} 个错误,{total_warnings} 个警告")
|
|
241
|
+
elif total_warnings > 0:
|
|
242
|
+
print_warning(f"发现 {total_warnings} 个警告")
|
|
243
|
+
else:
|
|
244
|
+
print_success("所有检查通过!")
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _save_report(results: list[ValidationResult], output_path: str, report_format: str) -> None:
|
|
248
|
+
"""保存检查报告
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
results: 验证结果列表
|
|
252
|
+
output_path: 输出路径
|
|
253
|
+
report_format: 报告格式
|
|
254
|
+
"""
|
|
255
|
+
if report_format == "markdown":
|
|
256
|
+
_save_markdown_report(results, output_path)
|
|
257
|
+
else:
|
|
258
|
+
print_warning(f"不支持的报告格式: {report_format}")
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _save_markdown_report(results: list[ValidationResult], output_path: str) -> None:
|
|
262
|
+
"""保存 Markdown 格式的报告
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
results: 验证结果列表
|
|
266
|
+
output_path: 输出路径
|
|
267
|
+
"""
|
|
268
|
+
lines = ["# 插件检查报告\n"]
|
|
269
|
+
|
|
270
|
+
# 统计
|
|
271
|
+
total_errors = sum(r.error_count for r in results)
|
|
272
|
+
total_warnings = sum(r.warning_count for r in results)
|
|
273
|
+
|
|
274
|
+
lines.append("## 摘要\n")
|
|
275
|
+
lines.append(f"- 错误: {total_errors}\n")
|
|
276
|
+
lines.append(f"- 警告: {total_warnings}\n")
|
|
277
|
+
lines.append("\n")
|
|
278
|
+
|
|
279
|
+
# 详细结果
|
|
280
|
+
for result in results:
|
|
281
|
+
lines.append(f"## {result.validator_name}\n")
|
|
282
|
+
|
|
283
|
+
if result.success:
|
|
284
|
+
lines.append("✓ 通过\n\n")
|
|
285
|
+
else:
|
|
286
|
+
lines.append(f"✗ 发现 {result.error_count} 个错误\n\n")
|
|
287
|
+
|
|
288
|
+
if result.issues:
|
|
289
|
+
lines.append("### 问题列表\n\n")
|
|
290
|
+
for issue in result.issues:
|
|
291
|
+
level_icons = {
|
|
292
|
+
ValidationLevel.ERROR: "❌",
|
|
293
|
+
ValidationLevel.WARNING: "⚠️",
|
|
294
|
+
ValidationLevel.INFO: "ℹ️",
|
|
295
|
+
}
|
|
296
|
+
icon = level_icons.get(issue.level, "•")
|
|
297
|
+
lines.append(f"- {icon} **{issue.level.value.upper()}**: {issue.message}\n")
|
|
298
|
+
|
|
299
|
+
if issue.file_path:
|
|
300
|
+
lines.append(f" - 文件: `{issue.file_path}`")
|
|
301
|
+
if issue.line_number:
|
|
302
|
+
lines.append(f":{issue.line_number}")
|
|
303
|
+
lines.append("\n")
|
|
304
|
+
|
|
305
|
+
if issue.suggestion:
|
|
306
|
+
lines.append(f" - 建议: {issue.suggestion}\n")
|
|
307
|
+
|
|
308
|
+
lines.append("\n")
|
|
309
|
+
|
|
310
|
+
# 写入文件
|
|
311
|
+
try:
|
|
312
|
+
with open(output_path, "w", encoding="utf-8") as f:
|
|
313
|
+
f.writelines(lines)
|
|
314
|
+
print_success(f"报告已保存到: {output_path}")
|
|
315
|
+
except Exception as e:
|
|
316
|
+
print_error(f"保存报告失败: {e}")
|