super-dev 2.0.0__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.
- super_dev/__init__.py +11 -0
- super_dev/analyzer/__init__.py +34 -0
- super_dev/analyzer/analyzer.py +440 -0
- super_dev/analyzer/detectors.py +511 -0
- super_dev/analyzer/models.py +285 -0
- super_dev/cli.py +3257 -0
- super_dev/config/__init__.py +11 -0
- super_dev/config/frontend.py +557 -0
- super_dev/config/manager.py +281 -0
- super_dev/creators/__init__.py +26 -0
- super_dev/creators/creator.py +134 -0
- super_dev/creators/document_generator.py +2473 -0
- super_dev/creators/frontend_builder.py +371 -0
- super_dev/creators/implementation_builder.py +789 -0
- super_dev/creators/prompt_generator.py +289 -0
- super_dev/creators/requirement_parser.py +354 -0
- super_dev/creators/spec_builder.py +195 -0
- super_dev/deployers/__init__.py +20 -0
- super_dev/deployers/cicd.py +1269 -0
- super_dev/deployers/delivery.py +229 -0
- super_dev/deployers/migration.py +1032 -0
- super_dev/design/__init__.py +74 -0
- super_dev/design/aesthetics.py +530 -0
- super_dev/design/charts.py +396 -0
- super_dev/design/codegen.py +379 -0
- super_dev/design/engine.py +528 -0
- super_dev/design/generator.py +395 -0
- super_dev/design/landing.py +422 -0
- super_dev/design/tech_stack.py +524 -0
- super_dev/design/tokens.py +269 -0
- super_dev/design/ux_guide.py +391 -0
- super_dev/exceptions.py +119 -0
- super_dev/experts/__init__.py +19 -0
- super_dev/experts/service.py +161 -0
- super_dev/integrations/__init__.py +7 -0
- super_dev/integrations/manager.py +264 -0
- super_dev/orchestrator/__init__.py +12 -0
- super_dev/orchestrator/engine.py +958 -0
- super_dev/orchestrator/experts.py +423 -0
- super_dev/orchestrator/knowledge.py +352 -0
- super_dev/orchestrator/quality.py +356 -0
- super_dev/reviewers/__init__.py +17 -0
- super_dev/reviewers/code_review.py +471 -0
- super_dev/reviewers/quality_gate.py +964 -0
- super_dev/reviewers/redteam.py +881 -0
- super_dev/skills/__init__.py +7 -0
- super_dev/skills/manager.py +307 -0
- super_dev/specs/__init__.py +44 -0
- super_dev/specs/generator.py +264 -0
- super_dev/specs/manager.py +428 -0
- super_dev/specs/models.py +348 -0
- super_dev/specs/validator.py +415 -0
- super_dev/utils/__init__.py +11 -0
- super_dev/utils/logger.py +133 -0
- super_dev/web/api.py +1402 -0
- super_dev-2.0.0.dist-info/METADATA +252 -0
- super_dev-2.0.0.dist-info/RECORD +61 -0
- super_dev-2.0.0.dist-info/WHEEL +5 -0
- super_dev-2.0.0.dist-info/entry_points.txt +2 -0
- super_dev-2.0.0.dist-info/licenses/LICENSE +21 -0
- super_dev-2.0.0.dist-info/top_level.txt +1 -0
super_dev/cli.py
ADDED
|
@@ -0,0 +1,3257 @@
|
|
|
1
|
+
"""
|
|
2
|
+
开发:Excellent(11964948@qq.com)
|
|
3
|
+
功能:Super Dev CLI 主入口
|
|
4
|
+
作用:提供命令行界面,统一访问所有功能
|
|
5
|
+
创建时间:2025-12-30
|
|
6
|
+
最后修改:2025-01-29
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
import traceback
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Literal, cast
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.panel import Panel
|
|
21
|
+
from rich.text import Text
|
|
22
|
+
RICH_AVAILABLE = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
RICH_AVAILABLE = False
|
|
25
|
+
|
|
26
|
+
from . import __description__, __version__
|
|
27
|
+
from .config import ConfigManager, get_config_manager
|
|
28
|
+
from .exceptions import SuperDevError
|
|
29
|
+
from .orchestrator import Phase, WorkflowContext, WorkflowEngine
|
|
30
|
+
from .utils import get_logger
|
|
31
|
+
|
|
32
|
+
CICDPlatform = Literal["github", "gitlab", "jenkins", "azure", "bitbucket", "all"]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class SuperDevCLI:
|
|
36
|
+
"""Super Dev 命令行接口"""
|
|
37
|
+
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self.console = Console() if RICH_AVAILABLE else None
|
|
40
|
+
self.parser = self._create_parser()
|
|
41
|
+
self.logger = get_logger('cli', level='WARNING') # CLI只记录WARNING及以上级别
|
|
42
|
+
|
|
43
|
+
def _create_parser(self) -> argparse.ArgumentParser:
|
|
44
|
+
"""创建命令行参数解析器"""
|
|
45
|
+
parser = argparse.ArgumentParser(
|
|
46
|
+
prog="super-dev",
|
|
47
|
+
description=__description__,
|
|
48
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
49
|
+
epilog="""
|
|
50
|
+
示例:
|
|
51
|
+
super-dev init my-project 初始化新项目
|
|
52
|
+
super-dev analyze [path] 分析现有项目
|
|
53
|
+
super-dev workflow 运行完整工作流
|
|
54
|
+
super-dev expert PM 调用产品经理专家
|
|
55
|
+
super-dev quality 运行质量检查
|
|
56
|
+
super-dev preview 生成原型预览
|
|
57
|
+
super-dev deploy 生成部署配置
|
|
58
|
+
"""
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
parser.add_argument(
|
|
62
|
+
"-v", "--version",
|
|
63
|
+
action="version",
|
|
64
|
+
version=f"%(prog)s {__version__}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# 子命令
|
|
68
|
+
subparsers = parser.add_subparsers(
|
|
69
|
+
dest="command",
|
|
70
|
+
title="可用命令",
|
|
71
|
+
description="使用 'super-dev <command> -h' 查看帮助"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# init 命令
|
|
75
|
+
init_parser = subparsers.add_parser(
|
|
76
|
+
"init",
|
|
77
|
+
help="初始化新项目",
|
|
78
|
+
description="创建一个新的 Super Dev 项目"
|
|
79
|
+
)
|
|
80
|
+
init_parser.add_argument(
|
|
81
|
+
"name",
|
|
82
|
+
help="项目名称"
|
|
83
|
+
)
|
|
84
|
+
init_parser.add_argument(
|
|
85
|
+
"-d", "--description",
|
|
86
|
+
default="",
|
|
87
|
+
help="项目描述"
|
|
88
|
+
)
|
|
89
|
+
init_parser.add_argument(
|
|
90
|
+
"-p", "--platform",
|
|
91
|
+
choices=["web", "mobile", "wechat", "desktop"],
|
|
92
|
+
default="web",
|
|
93
|
+
help="目标平台"
|
|
94
|
+
)
|
|
95
|
+
init_parser.add_argument(
|
|
96
|
+
"-f", "--frontend",
|
|
97
|
+
choices=[
|
|
98
|
+
"next", "remix", "react-vite", "gatsby",
|
|
99
|
+
"nuxt", "vue-vite",
|
|
100
|
+
"angular",
|
|
101
|
+
"sveltekit",
|
|
102
|
+
"astro", "solid", "qwik",
|
|
103
|
+
"none"
|
|
104
|
+
],
|
|
105
|
+
default="next",
|
|
106
|
+
help="前端框架"
|
|
107
|
+
)
|
|
108
|
+
init_parser.add_argument(
|
|
109
|
+
"--ui-library",
|
|
110
|
+
choices=[
|
|
111
|
+
"mui", "ant-design", "chakra-ui", "mantine", "shadcn-ui", "radix-ui",
|
|
112
|
+
"element-plus", "naive-ui", "vuetify", "primevue", "arco-design",
|
|
113
|
+
"angular-material", "primeng",
|
|
114
|
+
"skeleton-ui", "svelte-material-ui",
|
|
115
|
+
"tailwind", "daisyui"
|
|
116
|
+
],
|
|
117
|
+
help="UI 组件库"
|
|
118
|
+
)
|
|
119
|
+
init_parser.add_argument(
|
|
120
|
+
"--style",
|
|
121
|
+
choices=["tailwind", "css-modules", "styled-components", "emotion", "scss", "less", "unocss"],
|
|
122
|
+
help="样式方案"
|
|
123
|
+
)
|
|
124
|
+
init_parser.add_argument(
|
|
125
|
+
"--state",
|
|
126
|
+
choices=["react-query", "swr", "zustand", "redux-toolkit", "jotai", "pinia", "xstate"],
|
|
127
|
+
action="append",
|
|
128
|
+
help="状态管理方案 (可多选)"
|
|
129
|
+
)
|
|
130
|
+
init_parser.add_argument(
|
|
131
|
+
"--testing",
|
|
132
|
+
choices=["vitest", "jest", "playwright", "cypress", "testing-library"],
|
|
133
|
+
action="append",
|
|
134
|
+
help="测试框架 (可多选)"
|
|
135
|
+
)
|
|
136
|
+
init_parser.add_argument(
|
|
137
|
+
"-b", "--backend",
|
|
138
|
+
choices=["node", "python", "go", "java", "none"],
|
|
139
|
+
default="node",
|
|
140
|
+
help="后端框架"
|
|
141
|
+
)
|
|
142
|
+
init_parser.add_argument(
|
|
143
|
+
"--domain",
|
|
144
|
+
choices=["", "fintech", "ecommerce", "medical", "social", "iot", "education"],
|
|
145
|
+
default="",
|
|
146
|
+
help="业务领域"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# analyze 命令
|
|
150
|
+
analyze_parser = subparsers.add_parser(
|
|
151
|
+
"analyze",
|
|
152
|
+
help="分析现有项目",
|
|
153
|
+
description="自动检测和分析现有项目的结构、技术栈和架构模式"
|
|
154
|
+
)
|
|
155
|
+
analyze_parser.add_argument(
|
|
156
|
+
"path",
|
|
157
|
+
nargs="?",
|
|
158
|
+
default=".",
|
|
159
|
+
help="项目路径 (默认为当前目录)"
|
|
160
|
+
)
|
|
161
|
+
analyze_parser.add_argument(
|
|
162
|
+
"-o", "--output",
|
|
163
|
+
help="输出报告文件路径 (Markdown 格式)"
|
|
164
|
+
)
|
|
165
|
+
analyze_parser.add_argument(
|
|
166
|
+
"-f", "--format",
|
|
167
|
+
choices=["json", "markdown", "text"],
|
|
168
|
+
default="text",
|
|
169
|
+
help="输出格式"
|
|
170
|
+
)
|
|
171
|
+
analyze_parser.add_argument(
|
|
172
|
+
"--json",
|
|
173
|
+
action="store_true",
|
|
174
|
+
help="以 JSON 格式输出"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# workflow 命令
|
|
178
|
+
workflow_parser = subparsers.add_parser(
|
|
179
|
+
"workflow",
|
|
180
|
+
help="运行工作流",
|
|
181
|
+
description="执行 Super Dev 6 阶段工作流"
|
|
182
|
+
)
|
|
183
|
+
workflow_parser.add_argument(
|
|
184
|
+
"--phase",
|
|
185
|
+
choices=["discovery", "intelligence", "drafting", "redteam", "qa", "delivery", "deployment"],
|
|
186
|
+
nargs="*",
|
|
187
|
+
help="指定要执行的阶段"
|
|
188
|
+
)
|
|
189
|
+
workflow_parser.add_argument(
|
|
190
|
+
"-q", "--quality-gate",
|
|
191
|
+
type=int,
|
|
192
|
+
help="质量门禁阈值 (0-100)"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# studio 命令
|
|
196
|
+
studio_parser = subparsers.add_parser(
|
|
197
|
+
"studio",
|
|
198
|
+
help="启动交互工作台",
|
|
199
|
+
description="启动 Super Dev Web 工作台 API 服务"
|
|
200
|
+
)
|
|
201
|
+
studio_parser.add_argument(
|
|
202
|
+
"--host",
|
|
203
|
+
default="127.0.0.1",
|
|
204
|
+
help="监听地址 (默认: 127.0.0.1)"
|
|
205
|
+
)
|
|
206
|
+
studio_parser.add_argument(
|
|
207
|
+
"--port",
|
|
208
|
+
type=int,
|
|
209
|
+
default=8765,
|
|
210
|
+
help="监听端口 (默认: 8765)"
|
|
211
|
+
)
|
|
212
|
+
studio_parser.add_argument(
|
|
213
|
+
"--reload",
|
|
214
|
+
action="store_true",
|
|
215
|
+
help="启用热重载 (开发模式)"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# expert 命令
|
|
219
|
+
expert_parser = subparsers.add_parser(
|
|
220
|
+
"expert",
|
|
221
|
+
help="调用专家",
|
|
222
|
+
description="直接调用特定专家"
|
|
223
|
+
)
|
|
224
|
+
expert_parser.add_argument(
|
|
225
|
+
"--list",
|
|
226
|
+
action="store_true",
|
|
227
|
+
help="列出所有可用专家"
|
|
228
|
+
)
|
|
229
|
+
expert_parser.add_argument(
|
|
230
|
+
"expert_name",
|
|
231
|
+
nargs="?",
|
|
232
|
+
choices=["PM", "ARCHITECT", "UI", "UX", "SECURITY", "CODE", "DBA", "QA", "DEVOPS", "RCA"],
|
|
233
|
+
help="专家名称"
|
|
234
|
+
)
|
|
235
|
+
expert_parser.add_argument(
|
|
236
|
+
"prompt",
|
|
237
|
+
nargs="*",
|
|
238
|
+
help="提示词"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
# quality 命令
|
|
242
|
+
quality_parser = subparsers.add_parser(
|
|
243
|
+
"quality",
|
|
244
|
+
help="质量检查",
|
|
245
|
+
description="运行质量检查脚本"
|
|
246
|
+
)
|
|
247
|
+
quality_parser.add_argument(
|
|
248
|
+
"-t", "--type",
|
|
249
|
+
choices=["prd", "architecture", "ui", "ux", "code", "all"],
|
|
250
|
+
default="all",
|
|
251
|
+
help="检查类型"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
# preview 命令
|
|
255
|
+
preview_parser = subparsers.add_parser(
|
|
256
|
+
"preview",
|
|
257
|
+
help="生成原型",
|
|
258
|
+
description="从 UI 设计生成可交互的原型"
|
|
259
|
+
)
|
|
260
|
+
preview_parser.add_argument(
|
|
261
|
+
"-o", "--output",
|
|
262
|
+
default="preview.html",
|
|
263
|
+
help="输出文件路径"
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# deploy 命令
|
|
267
|
+
deploy_parser = subparsers.add_parser(
|
|
268
|
+
"deploy",
|
|
269
|
+
help="生成部署配置",
|
|
270
|
+
description="生成 Dockerfile 和 CI/CD 配置"
|
|
271
|
+
)
|
|
272
|
+
deploy_parser.add_argument(
|
|
273
|
+
"--docker",
|
|
274
|
+
action="store_true",
|
|
275
|
+
help="生成 Dockerfile"
|
|
276
|
+
)
|
|
277
|
+
deploy_parser.add_argument(
|
|
278
|
+
"--cicd",
|
|
279
|
+
choices=["github", "gitlab", "jenkins", "azure", "bitbucket", "all"],
|
|
280
|
+
help="生成 CI/CD 配置"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# config 命令
|
|
284
|
+
config_parser = subparsers.add_parser(
|
|
285
|
+
"config",
|
|
286
|
+
help="配置管理",
|
|
287
|
+
description="查看和修改项目配置"
|
|
288
|
+
)
|
|
289
|
+
config_parser.add_argument(
|
|
290
|
+
"action",
|
|
291
|
+
choices=["get", "set", "list"],
|
|
292
|
+
help="操作"
|
|
293
|
+
)
|
|
294
|
+
config_parser.add_argument(
|
|
295
|
+
"key",
|
|
296
|
+
nargs="?",
|
|
297
|
+
help="配置键"
|
|
298
|
+
)
|
|
299
|
+
config_parser.add_argument(
|
|
300
|
+
"value",
|
|
301
|
+
nargs="?",
|
|
302
|
+
help="配置值"
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# skill 命令 - 多平台 Skill 安装/管理
|
|
306
|
+
skill_parser = subparsers.add_parser(
|
|
307
|
+
"skill",
|
|
308
|
+
help="Skill 管理",
|
|
309
|
+
description="安装、列出、卸载跨平台 AI Coding Skills"
|
|
310
|
+
)
|
|
311
|
+
skill_parser.add_argument(
|
|
312
|
+
"action",
|
|
313
|
+
choices=["list", "install", "uninstall", "targets"],
|
|
314
|
+
help="操作类型"
|
|
315
|
+
)
|
|
316
|
+
skill_parser.add_argument(
|
|
317
|
+
"source_or_name",
|
|
318
|
+
nargs="?",
|
|
319
|
+
help="install 时为来源(目录/git/super-dev),uninstall 时为 skill 名称"
|
|
320
|
+
)
|
|
321
|
+
skill_parser.add_argument(
|
|
322
|
+
"-t", "--target",
|
|
323
|
+
choices=["claude-code", "codex-cli", "opencode", "cursor", "qoder", "trae", "codebuddy", "antigravity"],
|
|
324
|
+
default="claude-code",
|
|
325
|
+
help="目标平台 (默认: claude-code)"
|
|
326
|
+
)
|
|
327
|
+
skill_parser.add_argument(
|
|
328
|
+
"--name",
|
|
329
|
+
help="安装后的 skill 名称(可选)"
|
|
330
|
+
)
|
|
331
|
+
skill_parser.add_argument(
|
|
332
|
+
"--force",
|
|
333
|
+
action="store_true",
|
|
334
|
+
help="覆盖已存在的 skill"
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# integrate 命令 - 多平台适配配置
|
|
338
|
+
integrate_parser = subparsers.add_parser(
|
|
339
|
+
"integrate",
|
|
340
|
+
help="平台集成配置",
|
|
341
|
+
description="为 CLI/IDE AI Coding 工具生成集成配置文件"
|
|
342
|
+
)
|
|
343
|
+
integrate_parser.add_argument(
|
|
344
|
+
"action",
|
|
345
|
+
choices=["list", "setup"],
|
|
346
|
+
help="操作类型"
|
|
347
|
+
)
|
|
348
|
+
integrate_parser.add_argument(
|
|
349
|
+
"-t", "--target",
|
|
350
|
+
choices=["claude-code", "codex-cli", "opencode", "cursor", "qoder", "trae", "codebuddy", "antigravity"],
|
|
351
|
+
help="目标平台"
|
|
352
|
+
)
|
|
353
|
+
integrate_parser.add_argument(
|
|
354
|
+
"--all",
|
|
355
|
+
action="store_true",
|
|
356
|
+
help="对所有平台执行 setup"
|
|
357
|
+
)
|
|
358
|
+
integrate_parser.add_argument(
|
|
359
|
+
"--force",
|
|
360
|
+
action="store_true",
|
|
361
|
+
help="覆盖已存在的配置文件"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
# create 命令 - 一键创建项目
|
|
365
|
+
create_parser = subparsers.add_parser(
|
|
366
|
+
"create",
|
|
367
|
+
help="一键创建项目 (从想法到规范)",
|
|
368
|
+
description="从一句话描述自动生成 PRD、架构、UI/UX 文档并创建 Spec"
|
|
369
|
+
)
|
|
370
|
+
create_parser.add_argument(
|
|
371
|
+
"description",
|
|
372
|
+
help="功能描述 (如 '用户认证系统')"
|
|
373
|
+
)
|
|
374
|
+
create_parser.add_argument(
|
|
375
|
+
"-p", "--platform",
|
|
376
|
+
choices=["web", "mobile", "wechat", "desktop"],
|
|
377
|
+
default="web",
|
|
378
|
+
help="目标平台"
|
|
379
|
+
)
|
|
380
|
+
create_parser.add_argument(
|
|
381
|
+
"-f", "--frontend",
|
|
382
|
+
choices=["react", "vue", "angular", "svelte", "none"],
|
|
383
|
+
default="react",
|
|
384
|
+
help="前端框架"
|
|
385
|
+
)
|
|
386
|
+
create_parser.add_argument(
|
|
387
|
+
"-b", "--backend",
|
|
388
|
+
choices=["node", "python", "go", "java", "none"],
|
|
389
|
+
default="node",
|
|
390
|
+
help="后端框架"
|
|
391
|
+
)
|
|
392
|
+
create_parser.add_argument(
|
|
393
|
+
"-d", "--domain",
|
|
394
|
+
choices=["", "fintech", "ecommerce", "medical", "social", "iot", "education", "auth", "content"],
|
|
395
|
+
default="",
|
|
396
|
+
help="业务领域"
|
|
397
|
+
)
|
|
398
|
+
create_parser.add_argument(
|
|
399
|
+
"--name",
|
|
400
|
+
help="项目名称 (默认根据描述生成)"
|
|
401
|
+
)
|
|
402
|
+
create_parser.add_argument(
|
|
403
|
+
"--skip-docs",
|
|
404
|
+
action="store_true",
|
|
405
|
+
help="跳过文档生成,只创建 Spec"
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# design 命令 - 设计智能引擎
|
|
409
|
+
design_parser = subparsers.add_parser(
|
|
410
|
+
"design",
|
|
411
|
+
help="设计智能引擎",
|
|
412
|
+
description="搜索设计资产、生成设计系统、创建 design tokens"
|
|
413
|
+
)
|
|
414
|
+
design_subparsers = design_parser.add_subparsers(
|
|
415
|
+
dest="design_command",
|
|
416
|
+
title="设计命令",
|
|
417
|
+
description="使用 'super-dev design <command> -h' 查看帮助"
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# design search
|
|
421
|
+
design_search_parser = design_subparsers.add_parser(
|
|
422
|
+
"search",
|
|
423
|
+
help="搜索设计资产",
|
|
424
|
+
description="搜索 UI 风格、配色、字体、组件等设计资产"
|
|
425
|
+
)
|
|
426
|
+
design_search_parser.add_argument(
|
|
427
|
+
"query",
|
|
428
|
+
help="搜索关键词"
|
|
429
|
+
)
|
|
430
|
+
design_search_parser.add_argument(
|
|
431
|
+
"-d", "--domain",
|
|
432
|
+
choices=["style", "color", "typography", "component", "layout", "animation", "ux", "chart", "product"],
|
|
433
|
+
help="搜索领域 (默认自动检测)"
|
|
434
|
+
)
|
|
435
|
+
design_search_parser.add_argument(
|
|
436
|
+
"-n", "--max-results",
|
|
437
|
+
type=int,
|
|
438
|
+
default=5,
|
|
439
|
+
help="最大结果数 (默认: 5)"
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
# design generate
|
|
443
|
+
design_generate_parser = design_subparsers.add_parser(
|
|
444
|
+
"generate",
|
|
445
|
+
help="生成完整设计系统",
|
|
446
|
+
description="基于产品类型和行业生成完整的设计系统"
|
|
447
|
+
)
|
|
448
|
+
design_generate_parser.add_argument(
|
|
449
|
+
"--product",
|
|
450
|
+
required=True,
|
|
451
|
+
help="产品类型 (SaaS, E-commerce, Portfolio, Dashboard)"
|
|
452
|
+
)
|
|
453
|
+
design_generate_parser.add_argument(
|
|
454
|
+
"--industry",
|
|
455
|
+
required=True,
|
|
456
|
+
help="行业 (Fintech, Healthcare, Education, Gaming)"
|
|
457
|
+
)
|
|
458
|
+
design_generate_parser.add_argument(
|
|
459
|
+
"--keywords",
|
|
460
|
+
nargs="+",
|
|
461
|
+
help="关键词列表"
|
|
462
|
+
)
|
|
463
|
+
design_generate_parser.add_argument(
|
|
464
|
+
"-p", "--platform",
|
|
465
|
+
choices=["web", "mobile", "desktop"],
|
|
466
|
+
default="web",
|
|
467
|
+
help="目标平台 (默认: web)"
|
|
468
|
+
)
|
|
469
|
+
design_generate_parser.add_argument(
|
|
470
|
+
"-a", "--aesthetic",
|
|
471
|
+
help="美学方向 (可选)"
|
|
472
|
+
)
|
|
473
|
+
design_generate_parser.add_argument(
|
|
474
|
+
"-o", "--output",
|
|
475
|
+
default="output/design",
|
|
476
|
+
help="输出目录 (默认: output/design)"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
# design tokens
|
|
480
|
+
design_tokens_parser = design_subparsers.add_parser(
|
|
481
|
+
"tokens",
|
|
482
|
+
help="生成 design tokens",
|
|
483
|
+
description="生成 CSS 变量、Tailwind 配置等 design tokens"
|
|
484
|
+
)
|
|
485
|
+
design_tokens_parser.add_argument(
|
|
486
|
+
"--primary",
|
|
487
|
+
required=True,
|
|
488
|
+
help="主色 (hex 值,如 #3B82F6)"
|
|
489
|
+
)
|
|
490
|
+
design_tokens_parser.add_argument(
|
|
491
|
+
"--type",
|
|
492
|
+
choices=["monochromatic", "analogous", "complementary", "triadic"],
|
|
493
|
+
default="monochromatic",
|
|
494
|
+
help="调色板类型 (默认: monochromatic)"
|
|
495
|
+
)
|
|
496
|
+
design_tokens_parser.add_argument(
|
|
497
|
+
"--format",
|
|
498
|
+
choices=["css", "json", "tailwind"],
|
|
499
|
+
default="css",
|
|
500
|
+
help="输出格式 (默认: css)"
|
|
501
|
+
)
|
|
502
|
+
design_tokens_parser.add_argument(
|
|
503
|
+
"-o", "--output",
|
|
504
|
+
help="输出文件路径"
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
# design landing - Landing 页面模式
|
|
508
|
+
design_landing_parser = design_subparsers.add_parser(
|
|
509
|
+
"landing",
|
|
510
|
+
help="Landing 页面模式生成",
|
|
511
|
+
description="搜索和推荐 Landing 页面布局模式"
|
|
512
|
+
)
|
|
513
|
+
design_landing_parser.add_argument(
|
|
514
|
+
"query",
|
|
515
|
+
nargs="?",
|
|
516
|
+
help="搜索关键词(可选)"
|
|
517
|
+
)
|
|
518
|
+
design_landing_parser.add_argument(
|
|
519
|
+
"--product-type",
|
|
520
|
+
help="产品类型 (SaaS, E-commerce, Mobile, etc.)"
|
|
521
|
+
)
|
|
522
|
+
design_landing_parser.add_argument(
|
|
523
|
+
"--goal",
|
|
524
|
+
help="目标 (signup, purchase, demo, etc.)"
|
|
525
|
+
)
|
|
526
|
+
design_landing_parser.add_argument(
|
|
527
|
+
"--audience",
|
|
528
|
+
help="受众 (B2B, B2C, Enterprise, etc.)"
|
|
529
|
+
)
|
|
530
|
+
design_landing_parser.add_argument(
|
|
531
|
+
"-n", "--max-results",
|
|
532
|
+
type=int,
|
|
533
|
+
default=5,
|
|
534
|
+
help="最大结果数 (默认: 5)"
|
|
535
|
+
)
|
|
536
|
+
design_landing_parser.add_argument(
|
|
537
|
+
"--list",
|
|
538
|
+
action="store_true",
|
|
539
|
+
help="列出所有可用模式"
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
# design chart - 图表类型推荐
|
|
543
|
+
design_chart_parser = design_subparsers.add_parser(
|
|
544
|
+
"chart",
|
|
545
|
+
help="图表类型推荐",
|
|
546
|
+
description="根据数据类型推荐最佳图表类型"
|
|
547
|
+
)
|
|
548
|
+
design_chart_parser.add_argument(
|
|
549
|
+
"data_description",
|
|
550
|
+
help="数据描述(如 'time series sales data')"
|
|
551
|
+
)
|
|
552
|
+
design_chart_parser.add_argument(
|
|
553
|
+
"-f", "--framework",
|
|
554
|
+
choices=["react", "vue", "svelte", "angular", "next", "vanilla"],
|
|
555
|
+
default="react",
|
|
556
|
+
help="前端框架 (默认: react)"
|
|
557
|
+
)
|
|
558
|
+
design_chart_parser.add_argument(
|
|
559
|
+
"-n", "--max-results",
|
|
560
|
+
type=int,
|
|
561
|
+
default=3,
|
|
562
|
+
help="最大结果数 (默认: 3)"
|
|
563
|
+
)
|
|
564
|
+
design_chart_parser.add_argument(
|
|
565
|
+
"--list",
|
|
566
|
+
action="store_true",
|
|
567
|
+
help="列出所有图表类型"
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
# design ux - UX 指南查询
|
|
571
|
+
design_ux_parser = design_subparsers.add_parser(
|
|
572
|
+
"ux",
|
|
573
|
+
help="UX 指南查询",
|
|
574
|
+
description="查询 UX 最佳实践和反模式"
|
|
575
|
+
)
|
|
576
|
+
design_ux_parser.add_argument(
|
|
577
|
+
"query",
|
|
578
|
+
help="搜索查询"
|
|
579
|
+
)
|
|
580
|
+
design_ux_parser.add_argument(
|
|
581
|
+
"-d", "--domain",
|
|
582
|
+
help="领域过滤 (Animation, A11y, Performance, etc.)"
|
|
583
|
+
)
|
|
584
|
+
design_ux_parser.add_argument(
|
|
585
|
+
"-n", "--max-results",
|
|
586
|
+
type=int,
|
|
587
|
+
default=5,
|
|
588
|
+
help="最大结果数 (默认: 5)"
|
|
589
|
+
)
|
|
590
|
+
design_ux_parser.add_argument(
|
|
591
|
+
"--quick-wins",
|
|
592
|
+
action="store_true",
|
|
593
|
+
help="显示快速见效的改进建议"
|
|
594
|
+
)
|
|
595
|
+
design_ux_parser.add_argument(
|
|
596
|
+
"--checklist",
|
|
597
|
+
action="store_true",
|
|
598
|
+
help="显示 UX 检查清单"
|
|
599
|
+
)
|
|
600
|
+
design_ux_parser.add_argument(
|
|
601
|
+
"--list-domains",
|
|
602
|
+
action="store_true",
|
|
603
|
+
help="列出所有领域"
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# design stack - 技术栈最佳实践
|
|
607
|
+
design_stack_parser = design_subparsers.add_parser(
|
|
608
|
+
"stack",
|
|
609
|
+
help="技术栈最佳实践",
|
|
610
|
+
description="查询技术栈最佳实践、性能优化和设计模式"
|
|
611
|
+
)
|
|
612
|
+
design_stack_parser.add_argument(
|
|
613
|
+
"stack",
|
|
614
|
+
help="技术栈名称 (Next.js, React, Vue, SvelteKit, etc.)"
|
|
615
|
+
)
|
|
616
|
+
design_stack_parser.add_argument(
|
|
617
|
+
"query",
|
|
618
|
+
nargs="?",
|
|
619
|
+
help="搜索查询(可选)"
|
|
620
|
+
)
|
|
621
|
+
design_stack_parser.add_argument(
|
|
622
|
+
"-c", "--category",
|
|
623
|
+
help="类别过滤 (architecture, performance, state_management, etc.)"
|
|
624
|
+
)
|
|
625
|
+
design_stack_parser.add_argument(
|
|
626
|
+
"--patterns",
|
|
627
|
+
action="store_true",
|
|
628
|
+
help="显示设计模式"
|
|
629
|
+
)
|
|
630
|
+
design_stack_parser.add_argument(
|
|
631
|
+
"--performance",
|
|
632
|
+
action="store_true",
|
|
633
|
+
help="显示性能优化建议"
|
|
634
|
+
)
|
|
635
|
+
design_stack_parser.add_argument(
|
|
636
|
+
"--quick-wins",
|
|
637
|
+
action="store_true",
|
|
638
|
+
help="显示快速见效的性能优化"
|
|
639
|
+
)
|
|
640
|
+
design_stack_parser.add_argument(
|
|
641
|
+
"-n", "--max-results",
|
|
642
|
+
type=int,
|
|
643
|
+
default=5,
|
|
644
|
+
help="最大结果数 (默认: 5)"
|
|
645
|
+
)
|
|
646
|
+
design_stack_parser.add_argument(
|
|
647
|
+
"--list",
|
|
648
|
+
action="store_true",
|
|
649
|
+
help="列出所有支持的技术栈"
|
|
650
|
+
)
|
|
651
|
+
|
|
652
|
+
# design codegen - 代码片段生成
|
|
653
|
+
design_codegen_parser = design_subparsers.add_parser(
|
|
654
|
+
"codegen",
|
|
655
|
+
help="代码片段生成",
|
|
656
|
+
description="生成多框架的 UI 组件代码片段"
|
|
657
|
+
)
|
|
658
|
+
design_codegen_parser.add_argument(
|
|
659
|
+
"component",
|
|
660
|
+
help="组件名称 (button, card, input, etc.)"
|
|
661
|
+
)
|
|
662
|
+
design_codegen_parser.add_argument(
|
|
663
|
+
"-f", "--framework",
|
|
664
|
+
choices=["react", "nextjs", "vue", "svelte", "html", "tailwind"],
|
|
665
|
+
default="react",
|
|
666
|
+
help="目标框架 (默认: react)"
|
|
667
|
+
)
|
|
668
|
+
design_codegen_parser.add_argument(
|
|
669
|
+
"-o", "--output",
|
|
670
|
+
help="输出文件路径"
|
|
671
|
+
)
|
|
672
|
+
design_codegen_parser.add_argument(
|
|
673
|
+
"--list",
|
|
674
|
+
action="store_true",
|
|
675
|
+
help="列出所有可用组件"
|
|
676
|
+
)
|
|
677
|
+
design_codegen_parser.add_argument(
|
|
678
|
+
"--search",
|
|
679
|
+
help="搜索组件"
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
# spec 命令 - Spec-Driven Development
|
|
683
|
+
spec_parser = subparsers.add_parser(
|
|
684
|
+
"spec",
|
|
685
|
+
help="规范驱动开发 (SDD)",
|
|
686
|
+
description="管理规范和变更提案"
|
|
687
|
+
)
|
|
688
|
+
spec_subparsers = spec_parser.add_subparsers(
|
|
689
|
+
dest="spec_action",
|
|
690
|
+
title="SDD 命令",
|
|
691
|
+
description="使用 'super-dev spec <command> -h' 查看帮助"
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
# spec init
|
|
695
|
+
spec_subparsers.add_parser(
|
|
696
|
+
"init",
|
|
697
|
+
help="初始化 SDD 目录结构"
|
|
698
|
+
)
|
|
699
|
+
|
|
700
|
+
# spec list
|
|
701
|
+
spec_list_parser = spec_subparsers.add_parser(
|
|
702
|
+
"list",
|
|
703
|
+
help="列出所有变更"
|
|
704
|
+
)
|
|
705
|
+
spec_list_parser.add_argument(
|
|
706
|
+
"--status",
|
|
707
|
+
choices=["draft", "proposed", "approved", "in_progress", "completed", "archived"],
|
|
708
|
+
help="按状态过滤"
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
# spec show
|
|
712
|
+
spec_show_parser = spec_subparsers.add_parser(
|
|
713
|
+
"show",
|
|
714
|
+
help="显示变更详情"
|
|
715
|
+
)
|
|
716
|
+
spec_show_parser.add_argument(
|
|
717
|
+
"change_id",
|
|
718
|
+
help="变更 ID"
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# spec propose
|
|
722
|
+
spec_propose_parser = spec_subparsers.add_parser(
|
|
723
|
+
"propose",
|
|
724
|
+
help="创建变更提案"
|
|
725
|
+
)
|
|
726
|
+
spec_propose_parser.add_argument(
|
|
727
|
+
"change_id",
|
|
728
|
+
help="变更 ID (如 add-user-auth)"
|
|
729
|
+
)
|
|
730
|
+
spec_propose_parser.add_argument(
|
|
731
|
+
"--title",
|
|
732
|
+
required=True,
|
|
733
|
+
help="变更标题"
|
|
734
|
+
)
|
|
735
|
+
spec_propose_parser.add_argument(
|
|
736
|
+
"--description",
|
|
737
|
+
required=True,
|
|
738
|
+
help="变更描述"
|
|
739
|
+
)
|
|
740
|
+
spec_propose_parser.add_argument(
|
|
741
|
+
"--motivation",
|
|
742
|
+
help="变更动机/背景"
|
|
743
|
+
)
|
|
744
|
+
spec_propose_parser.add_argument(
|
|
745
|
+
"--impact",
|
|
746
|
+
help="影响范围"
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
# spec add-req
|
|
750
|
+
spec_add_req_parser = spec_subparsers.add_parser(
|
|
751
|
+
"add-req",
|
|
752
|
+
help="向变更添加需求"
|
|
753
|
+
)
|
|
754
|
+
spec_add_req_parser.add_argument(
|
|
755
|
+
"change_id",
|
|
756
|
+
help="变更 ID"
|
|
757
|
+
)
|
|
758
|
+
spec_add_req_parser.add_argument(
|
|
759
|
+
"spec_name",
|
|
760
|
+
help="规范名称 (如 auth, user-profile)"
|
|
761
|
+
)
|
|
762
|
+
spec_add_req_parser.add_argument(
|
|
763
|
+
"req_name",
|
|
764
|
+
help="需求名称"
|
|
765
|
+
)
|
|
766
|
+
spec_add_req_parser.add_argument(
|
|
767
|
+
"description",
|
|
768
|
+
help="需求描述"
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
# spec archive
|
|
772
|
+
spec_archive_parser = spec_subparsers.add_parser(
|
|
773
|
+
"archive",
|
|
774
|
+
help="归档已完成的变更"
|
|
775
|
+
)
|
|
776
|
+
spec_archive_parser.add_argument(
|
|
777
|
+
"change_id",
|
|
778
|
+
help="变更 ID"
|
|
779
|
+
)
|
|
780
|
+
spec_archive_parser.add_argument(
|
|
781
|
+
"-y", "--yes",
|
|
782
|
+
action="store_true",
|
|
783
|
+
help="跳过确认"
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
# spec validate
|
|
787
|
+
spec_validate_parser = spec_subparsers.add_parser(
|
|
788
|
+
"validate",
|
|
789
|
+
help="验证规格格式和结构"
|
|
790
|
+
)
|
|
791
|
+
spec_validate_parser.add_argument(
|
|
792
|
+
"change_id",
|
|
793
|
+
nargs="?",
|
|
794
|
+
help="变更 ID (留空则验证所有变更)"
|
|
795
|
+
)
|
|
796
|
+
spec_validate_parser.add_argument(
|
|
797
|
+
"-v", "--verbose",
|
|
798
|
+
action="store_true",
|
|
799
|
+
help="显示详细信息"
|
|
800
|
+
)
|
|
801
|
+
|
|
802
|
+
# spec view
|
|
803
|
+
spec_view_parser = spec_subparsers.add_parser(
|
|
804
|
+
"view",
|
|
805
|
+
help="交互式仪表板 - 显示所有规范和变更"
|
|
806
|
+
)
|
|
807
|
+
spec_view_parser.add_argument(
|
|
808
|
+
"--refresh",
|
|
809
|
+
action="store_true",
|
|
810
|
+
help="强制刷新数据"
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
# pipeline 命令 - 完整流水线
|
|
814
|
+
pipeline_parser = subparsers.add_parser(
|
|
815
|
+
"pipeline",
|
|
816
|
+
help="运行完整流水线 (从想法到部署)",
|
|
817
|
+
description="执行完整开发流水线:需求增强 → 文档 → 前端骨架 → Spec → 实现骨架 → 审查与门禁 → 交付配置"
|
|
818
|
+
)
|
|
819
|
+
pipeline_parser.add_argument(
|
|
820
|
+
"description",
|
|
821
|
+
help="功能描述 (如 '用户认证系统')"
|
|
822
|
+
)
|
|
823
|
+
pipeline_parser.add_argument(
|
|
824
|
+
"-p", "--platform",
|
|
825
|
+
choices=["web", "mobile", "wechat", "desktop"],
|
|
826
|
+
default="web",
|
|
827
|
+
help="目标平台"
|
|
828
|
+
)
|
|
829
|
+
pipeline_parser.add_argument(
|
|
830
|
+
"-f", "--frontend",
|
|
831
|
+
choices=["react", "vue", "angular", "svelte", "none"],
|
|
832
|
+
default="react",
|
|
833
|
+
help="前端框架"
|
|
834
|
+
)
|
|
835
|
+
pipeline_parser.add_argument(
|
|
836
|
+
"-b", "--backend",
|
|
837
|
+
choices=["node", "python", "go", "java", "none"],
|
|
838
|
+
default="node",
|
|
839
|
+
help="后端框架"
|
|
840
|
+
)
|
|
841
|
+
pipeline_parser.add_argument(
|
|
842
|
+
"-d", "--domain",
|
|
843
|
+
choices=["", "fintech", "ecommerce", "medical", "social", "iot", "education", "auth", "content"],
|
|
844
|
+
default="",
|
|
845
|
+
help="业务领域"
|
|
846
|
+
)
|
|
847
|
+
pipeline_parser.add_argument(
|
|
848
|
+
"--name",
|
|
849
|
+
help="项目名称 (默认根据描述生成)"
|
|
850
|
+
)
|
|
851
|
+
pipeline_parser.add_argument(
|
|
852
|
+
"--cicd",
|
|
853
|
+
choices=["github", "gitlab", "jenkins", "azure", "bitbucket", "all"],
|
|
854
|
+
default="all",
|
|
855
|
+
help="CI/CD 平台"
|
|
856
|
+
)
|
|
857
|
+
pipeline_parser.add_argument(
|
|
858
|
+
"--skip-redteam",
|
|
859
|
+
action="store_true",
|
|
860
|
+
help="跳过红队审查"
|
|
861
|
+
)
|
|
862
|
+
pipeline_parser.add_argument(
|
|
863
|
+
"--skip-scaffold",
|
|
864
|
+
action="store_true",
|
|
865
|
+
help="跳过前后端实现骨架生成"
|
|
866
|
+
)
|
|
867
|
+
pipeline_parser.add_argument(
|
|
868
|
+
"--skip-quality-gate",
|
|
869
|
+
action="store_true",
|
|
870
|
+
help="跳过质量门禁检查"
|
|
871
|
+
)
|
|
872
|
+
pipeline_parser.add_argument(
|
|
873
|
+
"--offline",
|
|
874
|
+
action="store_true",
|
|
875
|
+
help="离线模式(禁用联网检索)"
|
|
876
|
+
)
|
|
877
|
+
pipeline_parser.add_argument(
|
|
878
|
+
"--quality-threshold",
|
|
879
|
+
type=int,
|
|
880
|
+
default=None,
|
|
881
|
+
help="质量门禁阈值(可选;默认按场景自动判定)"
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
return parser
|
|
885
|
+
|
|
886
|
+
def run(self, args: list | None = None) -> int:
|
|
887
|
+
"""
|
|
888
|
+
运行 CLI
|
|
889
|
+
|
|
890
|
+
Args:
|
|
891
|
+
args: 命令行参数
|
|
892
|
+
|
|
893
|
+
Returns:
|
|
894
|
+
退出码
|
|
895
|
+
"""
|
|
896
|
+
argv = list(args) if args is not None else sys.argv[1:]
|
|
897
|
+
|
|
898
|
+
# 直达入口:`super-dev <需求描述>`
|
|
899
|
+
if self._is_direct_requirement_input(argv):
|
|
900
|
+
return self._run_direct_requirement(" ".join(argv).strip())
|
|
901
|
+
|
|
902
|
+
parsed_args = self.parser.parse_args(argv)
|
|
903
|
+
|
|
904
|
+
if parsed_args.command is None:
|
|
905
|
+
self._print_banner()
|
|
906
|
+
self.parser.print_help()
|
|
907
|
+
return 0
|
|
908
|
+
|
|
909
|
+
# 路由到对应命令
|
|
910
|
+
command_handler = getattr(self, f"_cmd_{parsed_args.command}", None)
|
|
911
|
+
if command_handler is None or not callable(command_handler):
|
|
912
|
+
self.console.print(f"[red]未知命令: {parsed_args.command}[/red]")
|
|
913
|
+
return 1
|
|
914
|
+
|
|
915
|
+
try:
|
|
916
|
+
handler = cast(Callable[[Any], int], command_handler)
|
|
917
|
+
return int(handler(parsed_args))
|
|
918
|
+
|
|
919
|
+
except SuperDevError as e:
|
|
920
|
+
# 已知异常 - 显示友好错误信息
|
|
921
|
+
self.console.print(f"[red]错误: {e.message}[/red]")
|
|
922
|
+
if e.details:
|
|
923
|
+
self.logger.warning(f"命令执行失败: {e.code}", extra={'details': e.details})
|
|
924
|
+
return 1
|
|
925
|
+
|
|
926
|
+
except KeyboardInterrupt:
|
|
927
|
+
# 用户中断
|
|
928
|
+
self.console.print("\n[yellow]操作已取消[/yellow]")
|
|
929
|
+
return 130
|
|
930
|
+
|
|
931
|
+
except Exception as e:
|
|
932
|
+
# 未知异常 - 显示详细错误信息
|
|
933
|
+
self.console.print(f"[red]未预期的错误: {str(e)}[/red]")
|
|
934
|
+
|
|
935
|
+
# 记录完整错误栈
|
|
936
|
+
self.logger.error(
|
|
937
|
+
f"CLI命令异常: {parsed_args.command}",
|
|
938
|
+
extra={
|
|
939
|
+
'error_type': type(e).__name__,
|
|
940
|
+
'error_message': str(e),
|
|
941
|
+
'traceback': traceback.format_exc()
|
|
942
|
+
}
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
# 在调试模式下显示traceback
|
|
946
|
+
if '--debug' in sys.argv or '-d' in sys.argv:
|
|
947
|
+
self.console.print(traceback.format_exc())
|
|
948
|
+
|
|
949
|
+
return 1
|
|
950
|
+
|
|
951
|
+
# ==================== 命令处理器 ====================
|
|
952
|
+
|
|
953
|
+
def _cmd_init(self, args) -> int:
|
|
954
|
+
"""初始化项目"""
|
|
955
|
+
config_manager = get_config_manager()
|
|
956
|
+
|
|
957
|
+
# 检查是否已初始化
|
|
958
|
+
if config_manager.exists():
|
|
959
|
+
self.console.print("[yellow]项目已初始化,使用 'super-dev config set' 修改配置[/yellow]")
|
|
960
|
+
return 0
|
|
961
|
+
|
|
962
|
+
# 创建配置
|
|
963
|
+
config = config_manager.create(
|
|
964
|
+
name=args.name,
|
|
965
|
+
description=args.description,
|
|
966
|
+
platform=args.platform,
|
|
967
|
+
frontend=args.frontend,
|
|
968
|
+
backend=args.backend,
|
|
969
|
+
domain=args.domain,
|
|
970
|
+
ui_library=getattr(args, 'ui_library', None),
|
|
971
|
+
style_solution=getattr(args, 'style', None),
|
|
972
|
+
state_management=getattr(args, 'state', []) or [],
|
|
973
|
+
testing_frameworks=getattr(args, 'testing', []) or [],
|
|
974
|
+
)
|
|
975
|
+
|
|
976
|
+
# 创建输出目录
|
|
977
|
+
output_dir = Path.cwd() / config.output_dir
|
|
978
|
+
output_dir.mkdir(exist_ok=True)
|
|
979
|
+
|
|
980
|
+
self.console.print(f"[green]✓[/green] 项目已初始化: {config.name}")
|
|
981
|
+
self.console.print(f" 平台: {config.platform}")
|
|
982
|
+
self.console.print(f" 前端框架: {config.frontend}")
|
|
983
|
+
if config.ui_library:
|
|
984
|
+
self.console.print(f" UI 组件库: {config.ui_library}")
|
|
985
|
+
if config.style_solution:
|
|
986
|
+
self.console.print(f" 样式方案: {config.style_solution}")
|
|
987
|
+
if config.state_management:
|
|
988
|
+
self.console.print(f" 状态管理: {', '.join(config.state_management)}")
|
|
989
|
+
if config.testing_frameworks:
|
|
990
|
+
self.console.print(f" 测试框架: {', '.join(config.testing_frameworks)}")
|
|
991
|
+
self.console.print(f" 后端: {config.backend}")
|
|
992
|
+
if config.domain:
|
|
993
|
+
self.console.print(f" 领域: {config.domain}")
|
|
994
|
+
|
|
995
|
+
self.console.print("\n[dim]下一步:[/dim]")
|
|
996
|
+
self.console.print(" 1. 编辑 super-dev.yaml 配置项目详情")
|
|
997
|
+
self.console.print(" 2. 运行 'super-dev workflow' 开始开发")
|
|
998
|
+
|
|
999
|
+
return 0
|
|
1000
|
+
|
|
1001
|
+
def _cmd_analyze(self, args) -> int:
|
|
1002
|
+
"""分析现有项目"""
|
|
1003
|
+
from .analyzer import ProjectAnalyzer
|
|
1004
|
+
|
|
1005
|
+
project_path = Path(args.path).resolve()
|
|
1006
|
+
|
|
1007
|
+
if not project_path.exists():
|
|
1008
|
+
self.console.print(f"[red]项目不存在: {project_path}[/red]")
|
|
1009
|
+
return 1
|
|
1010
|
+
|
|
1011
|
+
self.console.print(f"[cyan]正在分析项目: {project_path}[/cyan]")
|
|
1012
|
+
|
|
1013
|
+
try:
|
|
1014
|
+
analyzer = ProjectAnalyzer(project_path)
|
|
1015
|
+
report = analyzer.analyze()
|
|
1016
|
+
|
|
1017
|
+
# 根据格式输出
|
|
1018
|
+
output_format = "json" if args.json else args.format
|
|
1019
|
+
|
|
1020
|
+
if output_format == "json":
|
|
1021
|
+
import json
|
|
1022
|
+
output = json.dumps(report.to_dict(), indent=2, ensure_ascii=False)
|
|
1023
|
+
|
|
1024
|
+
if args.output:
|
|
1025
|
+
Path(args.output).write_text(output, encoding="utf-8")
|
|
1026
|
+
self.console.print(f"[green]报告已保存到: {args.output}[/green]")
|
|
1027
|
+
else:
|
|
1028
|
+
self.console.print(output)
|
|
1029
|
+
|
|
1030
|
+
elif output_format == "markdown":
|
|
1031
|
+
output = report.to_markdown()
|
|
1032
|
+
|
|
1033
|
+
if args.output:
|
|
1034
|
+
Path(args.output).write_text(output, encoding="utf-8")
|
|
1035
|
+
self.console.print(f"[green]报告已保存到: {args.output}[/green]")
|
|
1036
|
+
else:
|
|
1037
|
+
self.console.print(output)
|
|
1038
|
+
|
|
1039
|
+
else: # text
|
|
1040
|
+
framework_value = (
|
|
1041
|
+
report.tech_stack.framework.value
|
|
1042
|
+
if hasattr(report.tech_stack.framework, "value")
|
|
1043
|
+
else str(report.tech_stack.framework)
|
|
1044
|
+
)
|
|
1045
|
+
self.console.print("[cyan]项目分析报告[/cyan]")
|
|
1046
|
+
self.console.print(f" 路径: {report.project_path}")
|
|
1047
|
+
self.console.print(f" 类型: {report.category.value}")
|
|
1048
|
+
self.console.print(f" 语言: {report.tech_stack.language}")
|
|
1049
|
+
self.console.print(f" 框架: {framework_value}")
|
|
1050
|
+
if report.tech_stack.ui_library:
|
|
1051
|
+
self.console.print(f" UI 库: {report.tech_stack.ui_library}")
|
|
1052
|
+
if report.tech_stack.state_management:
|
|
1053
|
+
self.console.print(f" 状态管理: {report.tech_stack.state_management}")
|
|
1054
|
+
self.console.print(f" 文件数: {report.file_count}")
|
|
1055
|
+
self.console.print(f" 代码行数: {report.total_lines:,}")
|
|
1056
|
+
self.console.print(f" 依赖数: {len(report.tech_stack.dependencies)}")
|
|
1057
|
+
|
|
1058
|
+
if args.output:
|
|
1059
|
+
Path(args.output).write_text(report.to_markdown(), encoding="utf-8")
|
|
1060
|
+
self.console.print(f"[green]报告已保存到: {args.output}[/green]")
|
|
1061
|
+
|
|
1062
|
+
return 0
|
|
1063
|
+
|
|
1064
|
+
except FileNotFoundError as e:
|
|
1065
|
+
self.console.print(f"[red]路径不存在: {e}[/red]")
|
|
1066
|
+
self.logger.error("分析失败: 文件不存在", extra={'path': str(e)})
|
|
1067
|
+
return 1
|
|
1068
|
+
|
|
1069
|
+
except PermissionError as e:
|
|
1070
|
+
self.console.print(f"[red]权限不足: {e}[/red]")
|
|
1071
|
+
self.logger.error("分析失败: 权限错误", extra={'path': str(e)})
|
|
1072
|
+
return 1
|
|
1073
|
+
|
|
1074
|
+
except Exception as e:
|
|
1075
|
+
self.console.print(f"[red]分析失败: {e}[/red]")
|
|
1076
|
+
self.logger.error(
|
|
1077
|
+
f"分析异常: {type(e).__name__}",
|
|
1078
|
+
extra={
|
|
1079
|
+
'error_message': str(e),
|
|
1080
|
+
'traceback': traceback.format_exc()
|
|
1081
|
+
}
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
if '--debug' in sys.argv or '-d' in sys.argv:
|
|
1085
|
+
self.console.print(traceback.format_exc())
|
|
1086
|
+
|
|
1087
|
+
return 1
|
|
1088
|
+
|
|
1089
|
+
def _cmd_workflow(self, args) -> int:
|
|
1090
|
+
"""运行工作流"""
|
|
1091
|
+
project_dir = Path.cwd()
|
|
1092
|
+
config_manager = ConfigManager(project_dir)
|
|
1093
|
+
|
|
1094
|
+
if not config_manager.exists():
|
|
1095
|
+
self.console.print("[red]未找到项目配置,请先运行 'super-dev init'[/red]")
|
|
1096
|
+
return 1
|
|
1097
|
+
|
|
1098
|
+
# 更新质量门禁
|
|
1099
|
+
if args.quality_gate is not None:
|
|
1100
|
+
config_manager.update(quality_gate=args.quality_gate)
|
|
1101
|
+
|
|
1102
|
+
# 确定要执行的阶段
|
|
1103
|
+
phases = None
|
|
1104
|
+
if args.phase:
|
|
1105
|
+
phase_map = {
|
|
1106
|
+
"discovery": Phase.DISCOVERY,
|
|
1107
|
+
"intelligence": Phase.INTELLIGENCE,
|
|
1108
|
+
"drafting": Phase.DRAFTING,
|
|
1109
|
+
"redteam": Phase.REDTEAM,
|
|
1110
|
+
"qa": Phase.QA,
|
|
1111
|
+
"delivery": Phase.DELIVERY,
|
|
1112
|
+
"deployment": Phase.DEPLOYMENT,
|
|
1113
|
+
}
|
|
1114
|
+
phases = [phase_map[p] for p in args.phase]
|
|
1115
|
+
|
|
1116
|
+
# 运行工作流
|
|
1117
|
+
import asyncio
|
|
1118
|
+
config = config_manager.config
|
|
1119
|
+
context = WorkflowContext(
|
|
1120
|
+
project_dir=project_dir,
|
|
1121
|
+
config=config_manager,
|
|
1122
|
+
user_input={
|
|
1123
|
+
"name": config.name or project_dir.name,
|
|
1124
|
+
"description": config.description,
|
|
1125
|
+
"platform": config.platform,
|
|
1126
|
+
"frontend": config.frontend,
|
|
1127
|
+
"backend": config.backend,
|
|
1128
|
+
"domain": config.domain,
|
|
1129
|
+
"quality_threshold": args.quality_gate,
|
|
1130
|
+
},
|
|
1131
|
+
)
|
|
1132
|
+
engine = WorkflowEngine(project_dir)
|
|
1133
|
+
results = asyncio.run(engine.run(phases=phases, context=context))
|
|
1134
|
+
|
|
1135
|
+
# 检查是否全部成功
|
|
1136
|
+
all_success = bool(results) and all(r.success and not r.errors for r in results.values())
|
|
1137
|
+
|
|
1138
|
+
return 0 if all_success else 1
|
|
1139
|
+
|
|
1140
|
+
def _cmd_studio(self, args) -> int:
|
|
1141
|
+
"""启动交互工作台 API"""
|
|
1142
|
+
try:
|
|
1143
|
+
import uvicorn
|
|
1144
|
+
except ImportError:
|
|
1145
|
+
self.console.print("[red]缺少依赖: uvicorn[/red]")
|
|
1146
|
+
self.console.print("[dim]请安装: pip install fastapi uvicorn[/dim]")
|
|
1147
|
+
return 1
|
|
1148
|
+
|
|
1149
|
+
self.console.print(f"[cyan]启动 Super Dev Studio: http://{args.host}:{args.port}[/cyan]")
|
|
1150
|
+
uvicorn.run(
|
|
1151
|
+
"super_dev.web.api:app",
|
|
1152
|
+
host=args.host,
|
|
1153
|
+
port=args.port,
|
|
1154
|
+
reload=args.reload,
|
|
1155
|
+
log_level="info",
|
|
1156
|
+
)
|
|
1157
|
+
return 0
|
|
1158
|
+
|
|
1159
|
+
def _cmd_expert(self, args) -> int:
|
|
1160
|
+
"""调用专家"""
|
|
1161
|
+
from .experts import has_expert, list_experts, save_expert_advice
|
|
1162
|
+
|
|
1163
|
+
# 处理 --list 选项
|
|
1164
|
+
if args.list:
|
|
1165
|
+
self.console.print("[cyan]可用专家列表:[/cyan]")
|
|
1166
|
+
for expert in list_experts():
|
|
1167
|
+
self.console.print(f" [green]{expert['id']:<10}[/green] {expert['name']} - {expert['description']}")
|
|
1168
|
+
return 0
|
|
1169
|
+
|
|
1170
|
+
# 如果没有提供专家名称,显示帮助
|
|
1171
|
+
if not args.expert_name:
|
|
1172
|
+
self.console.print("[yellow]请提供专家名称或使用 --list 查看可用专家[/yellow]")
|
|
1173
|
+
return 1
|
|
1174
|
+
|
|
1175
|
+
prompt = " ".join(args.prompt) if args.prompt else ""
|
|
1176
|
+
self.console.print(f"[cyan]调用专家: {args.expert_name}[/cyan]")
|
|
1177
|
+
self.console.print(f"[dim]提示词: {prompt or '(无)'}[/dim]")
|
|
1178
|
+
if not has_expert(args.expert_name):
|
|
1179
|
+
self.console.print(f"[red]未知专家: {args.expert_name}[/red]")
|
|
1180
|
+
return 1
|
|
1181
|
+
|
|
1182
|
+
report_file, _ = save_expert_advice(
|
|
1183
|
+
project_dir=Path.cwd(),
|
|
1184
|
+
expert_id=args.expert_name,
|
|
1185
|
+
prompt=prompt,
|
|
1186
|
+
)
|
|
1187
|
+
self.console.print(f"[green]✓[/green] 专家建议已生成: {report_file}")
|
|
1188
|
+
return 0
|
|
1189
|
+
|
|
1190
|
+
def _cmd_quality(self, args) -> int:
|
|
1191
|
+
"""质量检查"""
|
|
1192
|
+
from .reviewers import QualityGateChecker
|
|
1193
|
+
|
|
1194
|
+
project_dir = Path.cwd()
|
|
1195
|
+
output_dir = project_dir / "output"
|
|
1196
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
1197
|
+
|
|
1198
|
+
config_manager = ConfigManager(project_dir)
|
|
1199
|
+
config = config_manager.load()
|
|
1200
|
+
|
|
1201
|
+
project_name = self._sanitize_project_name(config.name or project_dir.name)
|
|
1202
|
+
tech_stack = {
|
|
1203
|
+
"platform": config.platform,
|
|
1204
|
+
"frontend": self._normalize_pipeline_frontend(config.frontend),
|
|
1205
|
+
"backend": config.backend,
|
|
1206
|
+
"domain": config.domain,
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
self.console.print(f"[cyan]运行质量检查: {args.type}[/cyan]")
|
|
1210
|
+
|
|
1211
|
+
# 轻量文档检查
|
|
1212
|
+
if args.type in {"prd", "architecture", "ui", "ux"}:
|
|
1213
|
+
pattern_map = {
|
|
1214
|
+
"prd": "*-prd.md",
|
|
1215
|
+
"architecture": "*-architecture.md",
|
|
1216
|
+
"ui": "*-uiux.md",
|
|
1217
|
+
"ux": "*-uiux.md",
|
|
1218
|
+
}
|
|
1219
|
+
expected_pattern = pattern_map[args.type]
|
|
1220
|
+
matched = sorted(output_dir.glob(expected_pattern))
|
|
1221
|
+
if matched:
|
|
1222
|
+
self.console.print(f"[green]✓[/green] 检测到 {len(matched)} 个文档: {expected_pattern}")
|
|
1223
|
+
for file_path in matched[:5]:
|
|
1224
|
+
self.console.print(f" - {file_path}")
|
|
1225
|
+
return 0
|
|
1226
|
+
|
|
1227
|
+
self.console.print(f"[red]未找到文档: output/{expected_pattern}[/red]")
|
|
1228
|
+
return 1
|
|
1229
|
+
|
|
1230
|
+
# 代码或全量检查走质量门禁评估
|
|
1231
|
+
gate_checker = QualityGateChecker(
|
|
1232
|
+
project_dir=project_dir,
|
|
1233
|
+
name=project_name,
|
|
1234
|
+
tech_stack=tech_stack,
|
|
1235
|
+
)
|
|
1236
|
+
gate_result = gate_checker.check(redteam_report=None)
|
|
1237
|
+
|
|
1238
|
+
gate_file = output_dir / f"{project_name}-quality-gate.md"
|
|
1239
|
+
gate_file.write_text(gate_result.to_markdown(), encoding="utf-8")
|
|
1240
|
+
|
|
1241
|
+
scenario_label = "0-1 新建项目" if gate_result.scenario == "0-1" else "1-N+1 增量开发"
|
|
1242
|
+
status = "[green]通过[/green]" if gate_result.passed else "[red]未通过[/red]"
|
|
1243
|
+
|
|
1244
|
+
self.console.print(f" [dim]场景: {scenario_label}[/dim]")
|
|
1245
|
+
self.console.print(f" {status} 总分: {gate_result.total_score}/100")
|
|
1246
|
+
self.console.print(f" [green]✓[/green] 报告: {gate_file}")
|
|
1247
|
+
|
|
1248
|
+
if not gate_result.passed and gate_result.critical_failures:
|
|
1249
|
+
self.console.print("[yellow]关键失败项:[/yellow]")
|
|
1250
|
+
for failure in gate_result.critical_failures:
|
|
1251
|
+
self.console.print(f" - {failure}")
|
|
1252
|
+
|
|
1253
|
+
return 0 if gate_result.passed else 1
|
|
1254
|
+
|
|
1255
|
+
def _cmd_preview(self, args) -> int:
|
|
1256
|
+
"""生成原型"""
|
|
1257
|
+
import shutil
|
|
1258
|
+
|
|
1259
|
+
output_path = Path(args.output).expanduser()
|
|
1260
|
+
if not output_path.is_absolute():
|
|
1261
|
+
output_path = Path.cwd() / output_path
|
|
1262
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1263
|
+
|
|
1264
|
+
self.console.print(f"[cyan]生成原型: {output_path}[/cyan]")
|
|
1265
|
+
|
|
1266
|
+
frontend_dir = Path.cwd() / "output" / "frontend"
|
|
1267
|
+
index_file = frontend_dir / "index.html"
|
|
1268
|
+
css_file = frontend_dir / "styles.css"
|
|
1269
|
+
js_file = frontend_dir / "app.js"
|
|
1270
|
+
|
|
1271
|
+
if index_file.exists():
|
|
1272
|
+
html = index_file.read_text(encoding="utf-8")
|
|
1273
|
+
output_path.write_text(html, encoding="utf-8")
|
|
1274
|
+
|
|
1275
|
+
if css_file.exists():
|
|
1276
|
+
shutil.copyfile(css_file, output_path.parent / "styles.css")
|
|
1277
|
+
if js_file.exists():
|
|
1278
|
+
shutil.copyfile(js_file, output_path.parent / "app.js")
|
|
1279
|
+
|
|
1280
|
+
self.console.print("[green]✓[/green] 已基于 output/frontend 生成可预览页面")
|
|
1281
|
+
return 0
|
|
1282
|
+
|
|
1283
|
+
# 回退:生成文档概览预览页
|
|
1284
|
+
output_dir = Path.cwd() / "output"
|
|
1285
|
+
docs = sorted(output_dir.glob("*.md")) if output_dir.exists() else []
|
|
1286
|
+
rows = "\n".join(
|
|
1287
|
+
f"<li><a href=\"{doc.name}\" target=\"_blank\">{doc.name}</a></li>"
|
|
1288
|
+
for doc in docs[:20]
|
|
1289
|
+
) or "<li>未找到可预览文档,请先运行 super-dev create 或 super-dev pipeline。</li>"
|
|
1290
|
+
|
|
1291
|
+
fallback_html = f"""<!doctype html>
|
|
1292
|
+
<html lang="zh-CN">
|
|
1293
|
+
<head>
|
|
1294
|
+
<meta charset="utf-8" />
|
|
1295
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1296
|
+
<title>Super Dev Preview</title>
|
|
1297
|
+
<style>
|
|
1298
|
+
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; margin: 32px; line-height: 1.6; }}
|
|
1299
|
+
h1 {{ margin-bottom: 8px; }}
|
|
1300
|
+
.hint {{ color: #555; margin-bottom: 16px; }}
|
|
1301
|
+
ul {{ padding-left: 18px; }}
|
|
1302
|
+
</style>
|
|
1303
|
+
</head>
|
|
1304
|
+
<body>
|
|
1305
|
+
<h1>Super Dev 预览页</h1>
|
|
1306
|
+
<p class="hint">未检测到 output/frontend 前端骨架,以下为当前 output 文档列表:</p>
|
|
1307
|
+
<ul>{rows}</ul>
|
|
1308
|
+
</body>
|
|
1309
|
+
</html>
|
|
1310
|
+
"""
|
|
1311
|
+
output_path.write_text(fallback_html, encoding="utf-8")
|
|
1312
|
+
self.console.print("[yellow]未检测到 output/frontend,已生成文档概览预览页[/yellow]")
|
|
1313
|
+
return 0
|
|
1314
|
+
|
|
1315
|
+
def _cmd_deploy(self, args) -> int:
|
|
1316
|
+
"""生成部署配置"""
|
|
1317
|
+
from .deployers import CICDGenerator
|
|
1318
|
+
|
|
1319
|
+
project_dir = Path.cwd()
|
|
1320
|
+
config_manager = ConfigManager(project_dir)
|
|
1321
|
+
config = config_manager.load()
|
|
1322
|
+
|
|
1323
|
+
tech_stack = {
|
|
1324
|
+
"platform": config.platform,
|
|
1325
|
+
"frontend": self._normalize_pipeline_frontend(config.frontend),
|
|
1326
|
+
"backend": config.backend,
|
|
1327
|
+
"domain": config.domain,
|
|
1328
|
+
}
|
|
1329
|
+
project_name = self._sanitize_project_name(config.name or project_dir.name)
|
|
1330
|
+
|
|
1331
|
+
platform = self._normalize_cicd_platform(args.cicd or "github")
|
|
1332
|
+
generator = CICDGenerator(
|
|
1333
|
+
project_dir=project_dir,
|
|
1334
|
+
name=project_name,
|
|
1335
|
+
tech_stack=tech_stack,
|
|
1336
|
+
platform=platform,
|
|
1337
|
+
)
|
|
1338
|
+
generated_files = generator.generate()
|
|
1339
|
+
|
|
1340
|
+
cicd_map = {
|
|
1341
|
+
"github": [".github/workflows/ci.yml", ".github/workflows/cd.yml"],
|
|
1342
|
+
"gitlab": [".gitlab-ci.yml"],
|
|
1343
|
+
"jenkins": ["Jenkinsfile"],
|
|
1344
|
+
"azure": [".azure-pipelines.yml"],
|
|
1345
|
+
"bitbucket": ["bitbucket-pipelines.yml"],
|
|
1346
|
+
"all": [
|
|
1347
|
+
".github/workflows/ci.yml",
|
|
1348
|
+
".github/workflows/cd.yml",
|
|
1349
|
+
".gitlab-ci.yml",
|
|
1350
|
+
"Jenkinsfile",
|
|
1351
|
+
".azure-pipelines.yml",
|
|
1352
|
+
"bitbucket-pipelines.yml",
|
|
1353
|
+
],
|
|
1354
|
+
}
|
|
1355
|
+
docker_related = [
|
|
1356
|
+
"Dockerfile",
|
|
1357
|
+
"docker-compose.yml",
|
|
1358
|
+
".dockerignore",
|
|
1359
|
+
"k8s/deployment.yaml",
|
|
1360
|
+
"k8s/service.yaml",
|
|
1361
|
+
"k8s/ingress.yaml",
|
|
1362
|
+
"k8s/configmap.yaml",
|
|
1363
|
+
"k8s/secret.yaml",
|
|
1364
|
+
]
|
|
1365
|
+
|
|
1366
|
+
want_cicd = bool(args.cicd) or (not args.cicd and not args.docker)
|
|
1367
|
+
want_docker = bool(args.docker) or (not args.cicd and not args.docker)
|
|
1368
|
+
|
|
1369
|
+
selected_keys: list[str] = []
|
|
1370
|
+
if want_cicd:
|
|
1371
|
+
selected_keys.extend(cicd_map.get(platform, []))
|
|
1372
|
+
if want_docker:
|
|
1373
|
+
selected_keys.extend(docker_related)
|
|
1374
|
+
|
|
1375
|
+
selected_keys = [key for key in selected_keys if key in generated_files]
|
|
1376
|
+
if not selected_keys:
|
|
1377
|
+
self.console.print("[yellow]没有可生成的部署配置[/yellow]")
|
|
1378
|
+
return 0
|
|
1379
|
+
|
|
1380
|
+
self.console.print("[cyan]生成部署配置...[/cyan]")
|
|
1381
|
+
written = 0
|
|
1382
|
+
for relative_path in selected_keys:
|
|
1383
|
+
full_path = project_dir / relative_path
|
|
1384
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1385
|
+
full_path.write_text(generated_files[relative_path], encoding="utf-8")
|
|
1386
|
+
self.console.print(f" [green]✓[/green] {relative_path}")
|
|
1387
|
+
written += 1
|
|
1388
|
+
|
|
1389
|
+
self.console.print(f"[green]✓[/green] 已生成 {written} 个部署文件")
|
|
1390
|
+
return 0
|
|
1391
|
+
|
|
1392
|
+
def _cmd_create(self, args) -> int:
|
|
1393
|
+
"""一键创建项目 - 从想法到规范"""
|
|
1394
|
+
from .creators import ProjectCreator
|
|
1395
|
+
|
|
1396
|
+
self.console.print("[cyan]Super Dev 项目创建器[/cyan]")
|
|
1397
|
+
self.console.print(f"[dim]描述: {args.description}[/dim]")
|
|
1398
|
+
self.console.print(f"[dim]平台: {args.platform} | 前端: {args.frontend} | 后端: {args.backend}[/dim]")
|
|
1399
|
+
self.console.print("")
|
|
1400
|
+
|
|
1401
|
+
# 确定项目名称
|
|
1402
|
+
project_name = args.name
|
|
1403
|
+
if not project_name:
|
|
1404
|
+
# 从描述生成项目名称
|
|
1405
|
+
import re
|
|
1406
|
+
words = re.findall(r'[\w]+', args.description)
|
|
1407
|
+
if words:
|
|
1408
|
+
project_name = '-'.join(words[:3]).lower()
|
|
1409
|
+
else:
|
|
1410
|
+
project_name = "my-project"
|
|
1411
|
+
project_name = self._sanitize_project_name(project_name)
|
|
1412
|
+
|
|
1413
|
+
# 创建项目目录
|
|
1414
|
+
project_dir = Path.cwd()
|
|
1415
|
+
|
|
1416
|
+
try:
|
|
1417
|
+
creator = ProjectCreator(
|
|
1418
|
+
project_dir=project_dir,
|
|
1419
|
+
name=project_name,
|
|
1420
|
+
description=args.description,
|
|
1421
|
+
platform=args.platform,
|
|
1422
|
+
frontend=args.frontend,
|
|
1423
|
+
backend=args.backend,
|
|
1424
|
+
domain=args.domain,
|
|
1425
|
+
ui_library=getattr(args, 'ui_library', None),
|
|
1426
|
+
style_solution=getattr(args, 'style', None),
|
|
1427
|
+
state_management=getattr(args, 'state', []) or [],
|
|
1428
|
+
testing_frameworks=getattr(args, 'testing', []) or [],
|
|
1429
|
+
)
|
|
1430
|
+
|
|
1431
|
+
# 1. 生成文档
|
|
1432
|
+
if not args.skip_docs:
|
|
1433
|
+
self.console.print("[cyan]第 1 步: 生成专业文档...[/cyan]")
|
|
1434
|
+
docs = creator.generate_documents()
|
|
1435
|
+
self.console.print(f" [green]✓[/green] PRD: {docs['prd']}")
|
|
1436
|
+
self.console.print(f" [green]✓[/green] 架构: {docs['architecture']}")
|
|
1437
|
+
self.console.print(f" [green]✓[/green] UI/UX: {docs['uiux']}")
|
|
1438
|
+
if docs.get("plan"):
|
|
1439
|
+
self.console.print(f" [green]✓[/green] 执行路线图: {docs['plan']}")
|
|
1440
|
+
if docs.get("frontend_blueprint"):
|
|
1441
|
+
self.console.print(f" [green]✓[/green] 前端蓝图: {docs['frontend_blueprint']}")
|
|
1442
|
+
self.console.print("")
|
|
1443
|
+
|
|
1444
|
+
# 2. 创建 Spec
|
|
1445
|
+
self.console.print("[cyan]第 2 步: 创建 Spec 规范...[/cyan]")
|
|
1446
|
+
change_id = creator.create_spec()
|
|
1447
|
+
self.console.print(f" [green]✓[/green] 变更 ID: {change_id}")
|
|
1448
|
+
self.console.print("")
|
|
1449
|
+
|
|
1450
|
+
# 3. 生成 AI 提示词
|
|
1451
|
+
self.console.print("[cyan]第 3 步: 生成 AI 提示词...[/cyan]")
|
|
1452
|
+
prompt_file = creator.generate_ai_prompt()
|
|
1453
|
+
self.console.print(f" [green]✓[/green] 提示词: {prompt_file}")
|
|
1454
|
+
self.console.print("")
|
|
1455
|
+
|
|
1456
|
+
# 完成
|
|
1457
|
+
self.console.print("[green]✓ 项目创建完成![/green]")
|
|
1458
|
+
self.console.print("")
|
|
1459
|
+
self.console.print("[cyan]下一步:[/cyan]")
|
|
1460
|
+
self.console.print(" 1. 查看生成的文档:")
|
|
1461
|
+
self.console.print(f" - PRD: output/{project_name}-prd.md")
|
|
1462
|
+
self.console.print(f" - 架构: output/{project_name}-architecture.md")
|
|
1463
|
+
self.console.print(f" - UI/UX: output/{project_name}-uiux.md")
|
|
1464
|
+
self.console.print(f" - 执行路线图: output/{project_name}-execution-plan.md")
|
|
1465
|
+
self.console.print(f" - 前端蓝图: output/{project_name}-frontend-blueprint.md")
|
|
1466
|
+
self.console.print(f" 2. 查看规范: super-dev spec show {change_id}")
|
|
1467
|
+
self.console.print(f" 3. 复制 AI 提示词: cat {prompt_file}")
|
|
1468
|
+
self.console.print(" 4. 开始开发: 按 tasks 顺序实现并持续更新规范")
|
|
1469
|
+
|
|
1470
|
+
except Exception as e:
|
|
1471
|
+
self.console.print(f"[red]创建失败: {e}[/red]")
|
|
1472
|
+
import traceback
|
|
1473
|
+
self.console.print(traceback.format_exc())
|
|
1474
|
+
return 1
|
|
1475
|
+
|
|
1476
|
+
return 0
|
|
1477
|
+
|
|
1478
|
+
def _cmd_design(self, args) -> int:
|
|
1479
|
+
"""设计智能引擎命令"""
|
|
1480
|
+
from .design import DesignIntelligenceEngine, DesignSystemGenerator, TokenGenerator
|
|
1481
|
+
|
|
1482
|
+
if args.design_command == "search":
|
|
1483
|
+
# 搜索设计资产
|
|
1484
|
+
self.console.print(f"[cyan]搜索设计资产: {args.query}[/cyan]")
|
|
1485
|
+
|
|
1486
|
+
engine = DesignIntelligenceEngine()
|
|
1487
|
+
|
|
1488
|
+
result = engine.search(
|
|
1489
|
+
query=args.query,
|
|
1490
|
+
domain=args.domain,
|
|
1491
|
+
max_results=args.max_results,
|
|
1492
|
+
)
|
|
1493
|
+
|
|
1494
|
+
# 显示结果
|
|
1495
|
+
if "error" in result:
|
|
1496
|
+
self.console.print(f"[red]搜索失败: {result['error']}[/red]")
|
|
1497
|
+
return 1
|
|
1498
|
+
|
|
1499
|
+
domain_name = {
|
|
1500
|
+
"style": "风格",
|
|
1501
|
+
"color": "配色",
|
|
1502
|
+
"typography": "字体",
|
|
1503
|
+
"component": "组件",
|
|
1504
|
+
"layout": "布局",
|
|
1505
|
+
"animation": "动画",
|
|
1506
|
+
"ux": "UX 指南",
|
|
1507
|
+
"chart": "图表",
|
|
1508
|
+
"product": "产品",
|
|
1509
|
+
}.get(result["domain"], result["domain"])
|
|
1510
|
+
|
|
1511
|
+
self.console.print(f"\n[green]找到 {result['count']} 个{domain_name}结果:[/green]\n")
|
|
1512
|
+
|
|
1513
|
+
for idx, item in enumerate(result["results"], 1):
|
|
1514
|
+
relevance_color = {
|
|
1515
|
+
"high": "green",
|
|
1516
|
+
"medium": "yellow",
|
|
1517
|
+
"low": "dim",
|
|
1518
|
+
}.get(item.get("relevance", "low"), "dim")
|
|
1519
|
+
|
|
1520
|
+
self.console.print(f"[{relevance_color}]{idx}.[/] [bold]{item.get('name', item.get('Style Category', item.get('Font Pairing Name', 'N/A')))}[/] (相关度: {item.get('relevance', 'N/A')})")
|
|
1521
|
+
|
|
1522
|
+
# 显示关键信息
|
|
1523
|
+
if "description" in item:
|
|
1524
|
+
self.console.print(f" {item['description']}")
|
|
1525
|
+
if "keywords" in item:
|
|
1526
|
+
self.console.print(f" 关键词: {item['keywords']}")
|
|
1527
|
+
if "primary_colors" in item:
|
|
1528
|
+
self.console.print(f" 色彩: {item['primary_colors']}")
|
|
1529
|
+
if "mood" in item:
|
|
1530
|
+
self.console.print(f" 风格: {item['mood']}")
|
|
1531
|
+
|
|
1532
|
+
self.console.print()
|
|
1533
|
+
|
|
1534
|
+
return 0
|
|
1535
|
+
|
|
1536
|
+
elif args.design_command == "generate":
|
|
1537
|
+
# 生成完整设计系统
|
|
1538
|
+
self.console.print("[cyan]生成设计系统[/cyan]")
|
|
1539
|
+
self.console.print(f" 产品: {args.product}")
|
|
1540
|
+
self.console.print(f" 行业: {args.industry}")
|
|
1541
|
+
self.console.print(f" 关键词: {' '.join(args.keywords) if args.keywords else 'N/A'}")
|
|
1542
|
+
self.console.print(f" 平台: {args.platform}")
|
|
1543
|
+
self.console.print()
|
|
1544
|
+
|
|
1545
|
+
generator = DesignSystemGenerator()
|
|
1546
|
+
|
|
1547
|
+
design_system = generator.generate(
|
|
1548
|
+
product_type=args.product,
|
|
1549
|
+
industry=args.industry,
|
|
1550
|
+
keywords=args.keywords or [],
|
|
1551
|
+
platform=args.platform,
|
|
1552
|
+
aesthetic=args.aesthetic,
|
|
1553
|
+
)
|
|
1554
|
+
|
|
1555
|
+
self.console.print("[green]设计系统已生成:[/green]\n")
|
|
1556
|
+
self.console.print(f" 名称: {design_system.name}")
|
|
1557
|
+
self.console.print(f" 描述: {design_system.description}")
|
|
1558
|
+
|
|
1559
|
+
if design_system.aesthetic:
|
|
1560
|
+
self.console.print(f" 美学方向: {design_system.aesthetic.name}")
|
|
1561
|
+
self.console.print(f" 差异化特征: {design_system.aesthetic.differentiation}")
|
|
1562
|
+
|
|
1563
|
+
self.console.print("\n[cyan]生成文件...[/cyan]")
|
|
1564
|
+
|
|
1565
|
+
output_dir = Path(args.output)
|
|
1566
|
+
generated_files = generator.generate_documentation(design_system, output_dir)
|
|
1567
|
+
|
|
1568
|
+
for file_path in generated_files:
|
|
1569
|
+
self.console.print(f" [green]✓[/green] {file_path}")
|
|
1570
|
+
|
|
1571
|
+
self.console.print("\n[dim]下一步:[/dim]")
|
|
1572
|
+
self.console.print(f" 1. 查看 {output_dir / 'DESIGN_SYSTEM.md'} 了解设计系统")
|
|
1573
|
+
self.console.print(f" 2. 使用 {output_dir / 'design-tokens.css'} 导入 CSS 变量")
|
|
1574
|
+
self.console.print(f" 3. 使用 {output_dir / 'tailwind.config.json'} 配置 Tailwind")
|
|
1575
|
+
|
|
1576
|
+
return 0
|
|
1577
|
+
|
|
1578
|
+
elif args.design_command == "tokens":
|
|
1579
|
+
# 生成 design tokens
|
|
1580
|
+
self.console.print("[cyan]生成 design tokens[/cyan]")
|
|
1581
|
+
self.console.print(f" 主色: {args.primary}")
|
|
1582
|
+
self.console.print(f" 类型: {args.type}")
|
|
1583
|
+
self.console.print()
|
|
1584
|
+
|
|
1585
|
+
token_gen = TokenGenerator()
|
|
1586
|
+
|
|
1587
|
+
if args.format == "css":
|
|
1588
|
+
tokens = token_gen.generate_all_tokens(args.primary, args.type)
|
|
1589
|
+
|
|
1590
|
+
css_content = [":root {"]
|
|
1591
|
+
css_content.append(" /* Colors */")
|
|
1592
|
+
|
|
1593
|
+
for name, value in tokens["colors"].items():
|
|
1594
|
+
css_content.append(f" --color-{name}: {value};")
|
|
1595
|
+
|
|
1596
|
+
css_content.append("")
|
|
1597
|
+
css_content.append(" /* Spacing */")
|
|
1598
|
+
|
|
1599
|
+
for name, value in tokens["spacing"].items():
|
|
1600
|
+
css_content.append(f" --space-{name}: {value};")
|
|
1601
|
+
|
|
1602
|
+
css_content.append("")
|
|
1603
|
+
css_content.append(" /* Shadows */")
|
|
1604
|
+
|
|
1605
|
+
for name, value in tokens["shadows"].items():
|
|
1606
|
+
css_content.append(f" --shadow-{name}: {value};")
|
|
1607
|
+
|
|
1608
|
+
css_content.append("}")
|
|
1609
|
+
|
|
1610
|
+
output = "\n".join(css_content)
|
|
1611
|
+
|
|
1612
|
+
if args.output:
|
|
1613
|
+
Path(args.output).write_text(output, encoding="utf-8")
|
|
1614
|
+
self.console.print(f"[green]✓[/green] 已保存到 {args.output}")
|
|
1615
|
+
else:
|
|
1616
|
+
self.console.print(output)
|
|
1617
|
+
|
|
1618
|
+
elif args.format == "json":
|
|
1619
|
+
tokens = token_gen.generate_all_tokens(args.primary, args.type)
|
|
1620
|
+
output = json.dumps(tokens, indent=2)
|
|
1621
|
+
|
|
1622
|
+
if args.output:
|
|
1623
|
+
Path(args.output).write_text(output, encoding="utf-8")
|
|
1624
|
+
self.console.print(f"[green]✓[/green] 已保存到 {args.output}")
|
|
1625
|
+
else:
|
|
1626
|
+
self.console.print(output)
|
|
1627
|
+
|
|
1628
|
+
elif args.format == "tailwind":
|
|
1629
|
+
tokens = token_gen.generate_all_tokens(args.primary, args.type)
|
|
1630
|
+
|
|
1631
|
+
tailwind_config = {
|
|
1632
|
+
"theme": {
|
|
1633
|
+
"extend": {
|
|
1634
|
+
"colors": {f"{k}": v for k, v in tokens["colors"].items()},
|
|
1635
|
+
"spacing": tokens["spacing"],
|
|
1636
|
+
"boxShadow": tokens["shadows"],
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
output = json.dumps(tailwind_config, indent=2)
|
|
1642
|
+
|
|
1643
|
+
if args.output:
|
|
1644
|
+
Path(args.output).write_text(output, encoding="utf-8")
|
|
1645
|
+
self.console.print(f"[green]✓[/green] 已保存到 {args.output}")
|
|
1646
|
+
else:
|
|
1647
|
+
self.console.print(output)
|
|
1648
|
+
|
|
1649
|
+
return 0
|
|
1650
|
+
|
|
1651
|
+
elif args.design_command == "landing":
|
|
1652
|
+
# Landing 页面模式生成
|
|
1653
|
+
from .design import get_landing_generator
|
|
1654
|
+
|
|
1655
|
+
landing_gen = get_landing_generator()
|
|
1656
|
+
|
|
1657
|
+
# 列出所有模式
|
|
1658
|
+
if hasattr(args, 'list') and args.list:
|
|
1659
|
+
pattern_names = landing_gen.list_patterns()
|
|
1660
|
+
self.console.print(f"\n[green]可用的 Landing 页面模式 ({len(pattern_names)} 个):[/green]\n")
|
|
1661
|
+
for i, pattern_name in enumerate(pattern_names, 1):
|
|
1662
|
+
self.console.print(f" {i}. {pattern_name}")
|
|
1663
|
+
self.console.print()
|
|
1664
|
+
return 0
|
|
1665
|
+
|
|
1666
|
+
# 智能推荐
|
|
1667
|
+
if hasattr(args, 'product_type') and args.product_type:
|
|
1668
|
+
self.console.print("[cyan]智能推荐 Landing 页面模式[/cyan]")
|
|
1669
|
+
self.console.print(f" 产品类型: {args.product_type}")
|
|
1670
|
+
self.console.print(f" 目标: {args.goal if hasattr(args, 'goal') and args.goal else 'N/A'}")
|
|
1671
|
+
self.console.print(f" 受众: {args.audience if hasattr(args, 'audience') and args.audience else 'N/A'}")
|
|
1672
|
+
self.console.print()
|
|
1673
|
+
|
|
1674
|
+
recommended = landing_gen.recommend(
|
|
1675
|
+
product_type=args.product_type,
|
|
1676
|
+
goal=args.goal if hasattr(args, 'goal') and args.goal else "",
|
|
1677
|
+
audience=args.audience if hasattr(args, 'audience') and args.audience else ""
|
|
1678
|
+
)
|
|
1679
|
+
|
|
1680
|
+
if recommended:
|
|
1681
|
+
self.console.print(f"[green]推荐模式: {recommended.name}[/green]")
|
|
1682
|
+
self.console.print(f" {recommended.description}")
|
|
1683
|
+
self.console.print(f" 适合: {', '.join(recommended.best_for)}")
|
|
1684
|
+
self.console.print(f" 复杂度: {recommended.complexity}")
|
|
1685
|
+
self.console.print()
|
|
1686
|
+
return 0
|
|
1687
|
+
|
|
1688
|
+
# 搜索模式
|
|
1689
|
+
query = args.query if hasattr(args, 'query') and args.query else ""
|
|
1690
|
+
if query:
|
|
1691
|
+
self.console.print(f"[cyan]搜索 Landing 页面模式: {query}[/cyan]\n")
|
|
1692
|
+
|
|
1693
|
+
landing_results = landing_gen.search(query, max_results=args.max_results)
|
|
1694
|
+
|
|
1695
|
+
if not landing_results:
|
|
1696
|
+
self.console.print("[yellow]未找到匹配的模式[/yellow]")
|
|
1697
|
+
return 1
|
|
1698
|
+
|
|
1699
|
+
self.console.print(f"[green]找到 {len(landing_results)} 个结果:[/green]\n")
|
|
1700
|
+
|
|
1701
|
+
for idx, landing_pattern in enumerate(landing_results, 1):
|
|
1702
|
+
self.console.print(f"[cyan]{idx}. {landing_pattern.name}[/cyan]")
|
|
1703
|
+
self.console.print(f" {landing_pattern.description}")
|
|
1704
|
+
self.console.print(f" 适合: {', '.join(landing_pattern.best_for)}")
|
|
1705
|
+
self.console.print(f" 复杂度: {landing_pattern.complexity}")
|
|
1706
|
+
self.console.print()
|
|
1707
|
+
|
|
1708
|
+
return 0
|
|
1709
|
+
|
|
1710
|
+
# 默认显示所有模式
|
|
1711
|
+
pattern_names = landing_gen.list_patterns()
|
|
1712
|
+
self.console.print(f"\n[green]可用的 Landing 页面模式 ({len(pattern_names)} 个):[/green]\n")
|
|
1713
|
+
for i, pattern_name in enumerate(pattern_names, 1):
|
|
1714
|
+
self.console.print(f" {i}. {pattern_name}")
|
|
1715
|
+
self.console.print()
|
|
1716
|
+
return 0
|
|
1717
|
+
|
|
1718
|
+
elif args.design_command == "chart":
|
|
1719
|
+
# 图表类型推荐
|
|
1720
|
+
from .design import get_chart_recommender
|
|
1721
|
+
|
|
1722
|
+
chart_recommender = get_chart_recommender()
|
|
1723
|
+
|
|
1724
|
+
# 列出所有图表类型
|
|
1725
|
+
if hasattr(args, 'list') and args.list:
|
|
1726
|
+
chart_types = chart_recommender.list_chart_types()
|
|
1727
|
+
categories = chart_recommender.list_categories()
|
|
1728
|
+
self.console.print(f"\n[green]可用的图表类型 ({len(chart_types)} 个, {len(categories)} 个类别):[/green]\n")
|
|
1729
|
+
for category in sorted(categories):
|
|
1730
|
+
types = [ct for ct in chart_types if ct in [c.name for c in chart_recommender.chart_types if c.category.value == category]]
|
|
1731
|
+
self.console.print(f" [cyan]{category}:[/cyan]")
|
|
1732
|
+
for ct in sorted(types):
|
|
1733
|
+
self.console.print(f" - {ct}")
|
|
1734
|
+
self.console.print()
|
|
1735
|
+
return 0
|
|
1736
|
+
|
|
1737
|
+
# 推荐图表类型
|
|
1738
|
+
data_description = args.data_description if hasattr(args, 'data_description') else ""
|
|
1739
|
+
if data_description:
|
|
1740
|
+
self.console.print("[cyan]推荐图表类型[/cyan]")
|
|
1741
|
+
self.console.print(f" 数据描述: {data_description}")
|
|
1742
|
+
self.console.print(f" 框架: {args.framework}")
|
|
1743
|
+
self.console.print()
|
|
1744
|
+
|
|
1745
|
+
chart_recommendations = chart_recommender.recommend(
|
|
1746
|
+
data_description=data_description,
|
|
1747
|
+
framework=args.framework,
|
|
1748
|
+
max_results=args.max_results
|
|
1749
|
+
)
|
|
1750
|
+
|
|
1751
|
+
if not chart_recommendations:
|
|
1752
|
+
self.console.print("[yellow]未找到合适的图表类型[/yellow]")
|
|
1753
|
+
return 1
|
|
1754
|
+
|
|
1755
|
+
self.console.print("[green]推荐结果:[/green]\n")
|
|
1756
|
+
|
|
1757
|
+
for idx, chart_rec in enumerate(chart_recommendations, 1):
|
|
1758
|
+
confidence_pct = int(chart_rec.confidence * 100)
|
|
1759
|
+
self.console.print(f"[cyan]{idx}. {chart_rec.chart_type.name}[/cyan] (置信度: {confidence_pct}%)")
|
|
1760
|
+
self.console.print(f" {chart_rec.chart_type.description}")
|
|
1761
|
+
self.console.print(f" 理由: {chart_rec.reasoning}")
|
|
1762
|
+
self.console.print(f" 推荐库: {chart_rec.library_recommendation}")
|
|
1763
|
+
self.console.print(f" 无障碍: {chart_rec.chart_type.accessibility_notes}")
|
|
1764
|
+
if chart_rec.alternatives:
|
|
1765
|
+
self.console.print(f" 替代方案: {', '.join([a.name for a in chart_rec.alternatives])}")
|
|
1766
|
+
self.console.print()
|
|
1767
|
+
|
|
1768
|
+
return 0
|
|
1769
|
+
|
|
1770
|
+
# 默认显示所有图表类型
|
|
1771
|
+
chart_types = chart_recommender.list_chart_types()
|
|
1772
|
+
self.console.print(f"\n[green]可用的图表类型 ({len(chart_types)} 个):[/green]\n")
|
|
1773
|
+
for i, ct in enumerate(chart_types, 1):
|
|
1774
|
+
self.console.print(f" {i}. {ct}")
|
|
1775
|
+
self.console.print()
|
|
1776
|
+
return 0
|
|
1777
|
+
|
|
1778
|
+
elif args.design_command == "ux":
|
|
1779
|
+
# UX 指南查询
|
|
1780
|
+
from .design import get_ux_guide
|
|
1781
|
+
|
|
1782
|
+
ux_guide = get_ux_guide()
|
|
1783
|
+
|
|
1784
|
+
# 列出所有领域
|
|
1785
|
+
if hasattr(args, 'list_domains') and args.list_domains:
|
|
1786
|
+
domains = ux_guide.list_domains()
|
|
1787
|
+
self.console.print(f"\n[green]UX 指南领域 ({len(domains)} 个):[/green]\n")
|
|
1788
|
+
for i, domain in enumerate(domains, 1):
|
|
1789
|
+
self.console.print(f" {i}. {domain}")
|
|
1790
|
+
self.console.print()
|
|
1791
|
+
return 0
|
|
1792
|
+
|
|
1793
|
+
# 快速见效的改进
|
|
1794
|
+
if hasattr(args, 'quick_wins') and args.quick_wins:
|
|
1795
|
+
self.console.print("[cyan]快速见效的 UX 改进建议[/cyan]\n")
|
|
1796
|
+
|
|
1797
|
+
ux_quick_wins = ux_guide.get_quick_wins(max_results=args.max_results)
|
|
1798
|
+
|
|
1799
|
+
if not ux_quick_wins:
|
|
1800
|
+
self.console.print("[yellow]未找到快速见效的建议[/yellow]")
|
|
1801
|
+
return 1
|
|
1802
|
+
|
|
1803
|
+
for idx, ux_rec in enumerate(ux_quick_wins, 1):
|
|
1804
|
+
self.console.print(f"[cyan]{idx}. {ux_rec.guideline.topic}[/cyan] ({ux_rec.guideline.domain.value})")
|
|
1805
|
+
self.console.print(f" [green]最佳实践:[/green] {ux_rec.guideline.best_practice}")
|
|
1806
|
+
self.console.print(f" [red]反模式:[/red] {ux_rec.guideline.anti_pattern}")
|
|
1807
|
+
self.console.print(f" 影响: {ux_rec.guideline.impact}")
|
|
1808
|
+
self.console.print(f" 优先级: {ux_rec.priority} | 实现难度: {ux_rec.implementation_effort} | 用户影响: {ux_rec.user_impact}")
|
|
1809
|
+
if ux_rec.resources:
|
|
1810
|
+
self.console.print(f" 资源: {', '.join(ux_rec.resources)}")
|
|
1811
|
+
self.console.print()
|
|
1812
|
+
|
|
1813
|
+
return 0
|
|
1814
|
+
|
|
1815
|
+
# 检查清单
|
|
1816
|
+
if hasattr(args, 'checklist') and args.checklist:
|
|
1817
|
+
self.console.print("[cyan]UX 检查清单[/cyan]\n")
|
|
1818
|
+
|
|
1819
|
+
checklist = ux_guide.get_checklist(domains=[args.domain] if hasattr(args, 'domain') and args.domain else None)
|
|
1820
|
+
|
|
1821
|
+
for domain, items in sorted(checklist.items()):
|
|
1822
|
+
self.console.print(f"[cyan]{domain}:[/cyan]")
|
|
1823
|
+
for item in items:
|
|
1824
|
+
self.console.print(f" {item}")
|
|
1825
|
+
self.console.print()
|
|
1826
|
+
|
|
1827
|
+
return 0
|
|
1828
|
+
|
|
1829
|
+
# 搜索 UX 指南
|
|
1830
|
+
query = args.query if hasattr(args, 'query') and args.query else ""
|
|
1831
|
+
if query:
|
|
1832
|
+
self.console.print(f"[cyan]搜索 UX 指南: {query}[/cyan]\n")
|
|
1833
|
+
|
|
1834
|
+
ux_recommendations = ux_guide.search(
|
|
1835
|
+
query=query,
|
|
1836
|
+
domain=args.domain if hasattr(args, 'domain') else None,
|
|
1837
|
+
max_results=args.max_results
|
|
1838
|
+
)
|
|
1839
|
+
|
|
1840
|
+
if not ux_recommendations:
|
|
1841
|
+
self.console.print("[yellow]未找到匹配的 UX 指南[/yellow]")
|
|
1842
|
+
return 1
|
|
1843
|
+
|
|
1844
|
+
self.console.print(f"[green]找到 {len(ux_recommendations)} 个结果:[/green]\n")
|
|
1845
|
+
|
|
1846
|
+
for idx, ux_rec in enumerate(ux_recommendations, 1):
|
|
1847
|
+
self.console.print(f"[cyan]{idx}. {ux_rec.guideline.topic}[/cyan] ({ux_rec.guideline.domain.value})")
|
|
1848
|
+
self.console.print(f" [green]最佳实践:[/green] {ux_rec.guideline.best_practice}")
|
|
1849
|
+
self.console.print(f" [red]反模式:[/red] {ux_rec.guideline.anti_pattern}")
|
|
1850
|
+
self.console.print(f" 影响: {ux_rec.guideline.impact}")
|
|
1851
|
+
self.console.print(f" 优先级: {ux_rec.priority} | 实现难度: {ux_rec.implementation_effort} | 用户影响: {ux_rec.user_impact}")
|
|
1852
|
+
if ux_rec.resources:
|
|
1853
|
+
self.console.print(f" 资源: {', '.join(ux_rec.resources)}")
|
|
1854
|
+
self.console.print()
|
|
1855
|
+
|
|
1856
|
+
return 0
|
|
1857
|
+
|
|
1858
|
+
# 默认显示所有领域
|
|
1859
|
+
domains = ux_guide.list_domains()
|
|
1860
|
+
self.console.print(f"\n[green]UX 指南领域 ({len(domains)} 个):[/green]\n")
|
|
1861
|
+
for i, domain in enumerate(domains, 1):
|
|
1862
|
+
self.console.print(f" {i}. {domain}")
|
|
1863
|
+
self.console.print()
|
|
1864
|
+
return 0
|
|
1865
|
+
|
|
1866
|
+
elif args.design_command == "stack":
|
|
1867
|
+
# 技术栈最佳实践
|
|
1868
|
+
from .design import get_tech_stack_engine
|
|
1869
|
+
|
|
1870
|
+
tech_engine = get_tech_stack_engine()
|
|
1871
|
+
|
|
1872
|
+
# 列出所有技术栈
|
|
1873
|
+
if hasattr(args, 'list') and args.list:
|
|
1874
|
+
stacks = tech_engine.list_stacks()
|
|
1875
|
+
self.console.print(f"\n[green]支持的技术栈 ({len(stacks)} 个):[/green]\n")
|
|
1876
|
+
for i, stack in enumerate(stacks, 1):
|
|
1877
|
+
self.console.print(f" {i}. {stack}")
|
|
1878
|
+
self.console.print()
|
|
1879
|
+
return 0
|
|
1880
|
+
|
|
1881
|
+
# 查询参数
|
|
1882
|
+
stack_name = args.stack
|
|
1883
|
+
query = args.query if hasattr(args, 'query') and args.query else None
|
|
1884
|
+
category = args.category if hasattr(args, 'category') else None
|
|
1885
|
+
|
|
1886
|
+
# 显示设计模式
|
|
1887
|
+
if hasattr(args, 'patterns') and args.patterns:
|
|
1888
|
+
tech_patterns = tech_engine.get_patterns(stack_name)
|
|
1889
|
+
|
|
1890
|
+
if not tech_patterns:
|
|
1891
|
+
self.console.print(f"[yellow]未找到 {stack_name} 的设计模式[/yellow]")
|
|
1892
|
+
return 1
|
|
1893
|
+
|
|
1894
|
+
self.console.print(f"\n[cyan]{stack_name} 设计模式 ({len(tech_patterns)} 个):[/cyan]\n")
|
|
1895
|
+
|
|
1896
|
+
for idx, tech_pattern in enumerate(tech_patterns, 1):
|
|
1897
|
+
self.console.print(f"[cyan]{idx}. {tech_pattern.name}[/cyan]")
|
|
1898
|
+
self.console.print(f" 描述: {tech_pattern.description}")
|
|
1899
|
+
self.console.print(f" 使用场景: {tech_pattern.use_case}")
|
|
1900
|
+
if tech_pattern.pros:
|
|
1901
|
+
self.console.print(f" 优点: {', '.join(tech_pattern.pros)}")
|
|
1902
|
+
if tech_pattern.cons:
|
|
1903
|
+
self.console.print(f" 缺点: {', '.join(tech_pattern.cons)}")
|
|
1904
|
+
self.console.print()
|
|
1905
|
+
|
|
1906
|
+
return 0
|
|
1907
|
+
|
|
1908
|
+
# 显示性能优化建议
|
|
1909
|
+
if hasattr(args, 'performance') and args.performance:
|
|
1910
|
+
tips = tech_engine.get_performance_tips(stack_name)
|
|
1911
|
+
|
|
1912
|
+
if not tips:
|
|
1913
|
+
self.console.print(f"[yellow]未找到 {stack_name} 的性能建议[/yellow]")
|
|
1914
|
+
return 1
|
|
1915
|
+
|
|
1916
|
+
self.console.print(f"\n[cyan]{stack_name} 性能优化建议 ({len(tips)} 个):[/cyan]\n")
|
|
1917
|
+
|
|
1918
|
+
for idx, tip in enumerate(tips, 1):
|
|
1919
|
+
self.console.print(f"[cyan]{idx}. {tip.topic} - {tip.technique}[/cyan]")
|
|
1920
|
+
self.console.print(f" 描述: {tip.description}")
|
|
1921
|
+
self.console.print(f" 影响: {tip.impact} | 实施难度: {tip.effort}")
|
|
1922
|
+
if tip.code_snippet:
|
|
1923
|
+
self.console.print(f" 代码示例:\n [dim]{tip.code_snippet}[/dim]")
|
|
1924
|
+
self.console.print()
|
|
1925
|
+
|
|
1926
|
+
return 0
|
|
1927
|
+
|
|
1928
|
+
# 快速见效的性能优化
|
|
1929
|
+
if hasattr(args, 'quick_wins') and args.quick_wins:
|
|
1930
|
+
tips = tech_engine.get_quick_wins(stack_name)
|
|
1931
|
+
|
|
1932
|
+
if not tips:
|
|
1933
|
+
self.console.print(f"[yellow]未找到 {stack_name} 的快速性能优化[/yellow]")
|
|
1934
|
+
return 1
|
|
1935
|
+
|
|
1936
|
+
self.console.print(f"\n[cyan]{stack_name} 快速见效的性能优化 ({len(tips)} 个):[/cyan]\n")
|
|
1937
|
+
|
|
1938
|
+
for idx, tip in enumerate(tips, 1):
|
|
1939
|
+
self.console.print(f"[cyan]{idx}. {tip.topic} - {tip.technique}[/cyan]")
|
|
1940
|
+
self.console.print(f" 描述: {tip.description}")
|
|
1941
|
+
if tip.code_snippet:
|
|
1942
|
+
self.console.print(f" 代码示例:\n [dim]{tip.code_snippet}[/dim]")
|
|
1943
|
+
self.console.print()
|
|
1944
|
+
|
|
1945
|
+
return 0
|
|
1946
|
+
|
|
1947
|
+
# 搜索最佳实践
|
|
1948
|
+
self.console.print(f"[cyan]搜索 {stack_name} 最佳实践[/cyan]\n")
|
|
1949
|
+
|
|
1950
|
+
if query:
|
|
1951
|
+
self.console.print(f"查询: {query}\n")
|
|
1952
|
+
|
|
1953
|
+
stack_recommendations = tech_engine.search_practices(
|
|
1954
|
+
stack=stack_name,
|
|
1955
|
+
query=query,
|
|
1956
|
+
category=category,
|
|
1957
|
+
max_results=args.max_results
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1960
|
+
if not stack_recommendations:
|
|
1961
|
+
self.console.print("[yellow]未找到匹配的最佳实践[/yellow]")
|
|
1962
|
+
return 1
|
|
1963
|
+
|
|
1964
|
+
for idx, stack_rec in enumerate(stack_recommendations, 1):
|
|
1965
|
+
self.console.print(f"[cyan]{idx}. {stack_rec.practice.topic}[/cyan] ({stack_rec.practice.category.value})")
|
|
1966
|
+
self.console.print(f" [green]最佳实践:[/green] {stack_rec.practice.practice}")
|
|
1967
|
+
self.console.print(f" [red]反模式:[/red] {stack_rec.practice.anti_pattern}")
|
|
1968
|
+
self.console.print(f" 好处: {stack_rec.practice.benefits}")
|
|
1969
|
+
self.console.print(f" 优先级: {stack_rec.priority} | 复杂度: {stack_rec.practice.complexity}")
|
|
1970
|
+
if stack_rec.context:
|
|
1971
|
+
self.console.print(f" 上下文: {stack_rec.context}")
|
|
1972
|
+
if stack_rec.alternatives:
|
|
1973
|
+
self.console.print(
|
|
1974
|
+
f" 替代方案: {', '.join([a.value if hasattr(a, 'value') else str(a) for a in stack_rec.alternatives])}"
|
|
1975
|
+
)
|
|
1976
|
+
if stack_rec.resources:
|
|
1977
|
+
self.console.print(f" 资源: {', '.join(stack_rec.resources)}")
|
|
1978
|
+
if stack_rec.practice.code_example:
|
|
1979
|
+
self.console.print(f" 代码示例:\n [dim]{stack_rec.practice.code_example[:200]}...[/dim]")
|
|
1980
|
+
self.console.print()
|
|
1981
|
+
|
|
1982
|
+
return 0
|
|
1983
|
+
|
|
1984
|
+
elif args.design_command == "codegen":
|
|
1985
|
+
# 代码片段生成
|
|
1986
|
+
from .design import get_code_generator
|
|
1987
|
+
from .design.codegen import Framework
|
|
1988
|
+
|
|
1989
|
+
codegen = get_code_generator()
|
|
1990
|
+
|
|
1991
|
+
# 列出所有可用组件
|
|
1992
|
+
if hasattr(args, 'list') and args.list:
|
|
1993
|
+
components = codegen.get_available_components(
|
|
1994
|
+
framework=Framework(args.framework) if hasattr(args, 'framework') else None
|
|
1995
|
+
)
|
|
1996
|
+
|
|
1997
|
+
self.console.print(f"\n[green]可用组件 ({args.framework or 'all'}):[/green]\n")
|
|
1998
|
+
|
|
1999
|
+
for category, comp_list in sorted(components.items()):
|
|
2000
|
+
self.console.print(f"[cyan]{category}:[/cyan]")
|
|
2001
|
+
for comp in comp_list:
|
|
2002
|
+
self.console.print(f" - {comp}")
|
|
2003
|
+
self.console.print()
|
|
2004
|
+
|
|
2005
|
+
return 0
|
|
2006
|
+
|
|
2007
|
+
# 搜索组件
|
|
2008
|
+
if hasattr(args, 'search') and args.search:
|
|
2009
|
+
component_snippets = codegen.search_components(
|
|
2010
|
+
query=args.search,
|
|
2011
|
+
framework=args.framework if hasattr(args, 'framework') else None
|
|
2012
|
+
)
|
|
2013
|
+
|
|
2014
|
+
if not component_snippets:
|
|
2015
|
+
self.console.print(f"[yellow]未找到匹配的组件: {args.search}[/yellow]")
|
|
2016
|
+
return 1
|
|
2017
|
+
|
|
2018
|
+
self.console.print(f"\n[green]找到 {len(component_snippets)} 个组件:[/green]\n")
|
|
2019
|
+
|
|
2020
|
+
for idx, snippet in enumerate(component_snippets, 1):
|
|
2021
|
+
self.console.print(f"[cyan]{idx}. {snippet.name}[/cyan] ({snippet.framework.value})")
|
|
2022
|
+
self.console.print(f" 类别: {snippet.category.value}")
|
|
2023
|
+
self.console.print(f" 描述: {snippet.description}")
|
|
2024
|
+
self.console.print(f" 依赖: {', '.join(snippet.dependencies)}")
|
|
2025
|
+
if snippet.preview:
|
|
2026
|
+
self.console.print(f" 预览: [dim]{snippet.preview}[/dim]")
|
|
2027
|
+
self.console.print()
|
|
2028
|
+
|
|
2029
|
+
return 0
|
|
2030
|
+
|
|
2031
|
+
# 生成组件代码
|
|
2032
|
+
component_name = args.component
|
|
2033
|
+
framework = args.framework if hasattr(args, 'framework') else "react"
|
|
2034
|
+
|
|
2035
|
+
self.console.print(f"[cyan]生成 {component_name} 组件 ({framework})[/cyan]\n")
|
|
2036
|
+
|
|
2037
|
+
component = codegen.generate_component(
|
|
2038
|
+
component_name=component_name,
|
|
2039
|
+
framework=Framework(framework)
|
|
2040
|
+
)
|
|
2041
|
+
|
|
2042
|
+
if not component:
|
|
2043
|
+
self.console.print(f"[yellow]未找到组件: {component_name}[/yellow]")
|
|
2044
|
+
self.console.print("使用 --list 查看可用组件")
|
|
2045
|
+
return 1
|
|
2046
|
+
|
|
2047
|
+
self.console.print(f"[green]组件名称:[/green] {component_name}")
|
|
2048
|
+
self.console.print(f"[green]描述:[/green] {component.description}\n")
|
|
2049
|
+
|
|
2050
|
+
self.console.print("[cyan]代码:[/cyan]")
|
|
2051
|
+
self.console.print(f"```{framework}")
|
|
2052
|
+
self.console.print(component.code)
|
|
2053
|
+
self.console.print("```\n")
|
|
2054
|
+
|
|
2055
|
+
if component.imports:
|
|
2056
|
+
self.console.print("[cyan]导入语句:[/cyan]")
|
|
2057
|
+
for imp in component.imports:
|
|
2058
|
+
self.console.print(f" {imp}")
|
|
2059
|
+
self.console.print()
|
|
2060
|
+
|
|
2061
|
+
if component.dependencies:
|
|
2062
|
+
self.console.print("[cyan]依赖:[/cyan]")
|
|
2063
|
+
self.console.print(f" {', '.join(component.dependencies)}")
|
|
2064
|
+
self.console.print()
|
|
2065
|
+
|
|
2066
|
+
if component.usage_example:
|
|
2067
|
+
self.console.print("[cyan]使用示例:[/cyan]")
|
|
2068
|
+
self.console.print(f" [dim]{component.usage_example}[/dim]")
|
|
2069
|
+
|
|
2070
|
+
# 输出到文件
|
|
2071
|
+
if hasattr(args, 'output') and args.output:
|
|
2072
|
+
output_path = Path(args.output)
|
|
2073
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2074
|
+
|
|
2075
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
2076
|
+
f.write(component.code)
|
|
2077
|
+
|
|
2078
|
+
self.console.print(f"\n[green]已保存到: {output_path}[/green]")
|
|
2079
|
+
|
|
2080
|
+
return 0
|
|
2081
|
+
|
|
2082
|
+
else:
|
|
2083
|
+
self.console.print("[yellow]请指定设计子命令[/yellow]")
|
|
2084
|
+
self.console.print(" 可用命令: search, generate, tokens, landing, chart, ux, stack, codegen")
|
|
2085
|
+
self.console.print(" 使用 'super-dev design <command> -h' 查看帮助")
|
|
2086
|
+
return 1
|
|
2087
|
+
|
|
2088
|
+
def _cmd_pipeline(self, args) -> int:
|
|
2089
|
+
"""运行完整流水线 - 从想法到部署"""
|
|
2090
|
+
# 确定项目名称
|
|
2091
|
+
project_name = args.name
|
|
2092
|
+
if not project_name:
|
|
2093
|
+
import re
|
|
2094
|
+
words = re.findall(r'[\w]+', args.description)
|
|
2095
|
+
if words:
|
|
2096
|
+
project_name = '-'.join(words[:3]).lower()
|
|
2097
|
+
else:
|
|
2098
|
+
project_name = "my-project"
|
|
2099
|
+
project_name = self._sanitize_project_name(project_name)
|
|
2100
|
+
|
|
2101
|
+
tech_stack = {
|
|
2102
|
+
"platform": args.platform,
|
|
2103
|
+
"frontend": args.frontend,
|
|
2104
|
+
"backend": args.backend,
|
|
2105
|
+
"domain": args.domain,
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
project_dir = Path.cwd()
|
|
2109
|
+
|
|
2110
|
+
self.console.print(f"[cyan]{'=' * 60}[/cyan]")
|
|
2111
|
+
self.console.print("[cyan]Super Dev 完整流水线[/cyan]")
|
|
2112
|
+
self.console.print(f"[cyan]{'=' * 60}[/cyan]")
|
|
2113
|
+
self.console.print(f"[dim]项目: {project_name}[/dim]")
|
|
2114
|
+
self.console.print(f"[dim]技术栈: {args.platform} | {args.frontend} | {args.backend}[/dim]")
|
|
2115
|
+
self.console.print("")
|
|
2116
|
+
|
|
2117
|
+
try:
|
|
2118
|
+
# ========== 第 0 阶段: 需求增强 ==========
|
|
2119
|
+
self.console.print("[cyan]第 0 阶段: 需求增强 (联网 + 知识库)...[/cyan]")
|
|
2120
|
+
import os
|
|
2121
|
+
|
|
2122
|
+
from .orchestrator.knowledge import KnowledgeAugmenter
|
|
2123
|
+
|
|
2124
|
+
output_dir = project_dir / "output"
|
|
2125
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
2126
|
+
|
|
2127
|
+
disable_web = args.offline or (
|
|
2128
|
+
os.getenv("SUPER_DEV_DISABLE_WEB", "").strip().lower() in {"1", "true", "yes"}
|
|
2129
|
+
)
|
|
2130
|
+
augmenter = KnowledgeAugmenter(project_dir=project_dir, web_enabled=not disable_web)
|
|
2131
|
+
knowledge_bundle = augmenter.augment(
|
|
2132
|
+
requirement=args.description,
|
|
2133
|
+
domain=args.domain or "",
|
|
2134
|
+
)
|
|
2135
|
+
research_file = output_dir / f"{project_name}-research.md"
|
|
2136
|
+
research_file.write_text(augmenter.to_markdown(knowledge_bundle), encoding="utf-8")
|
|
2137
|
+
|
|
2138
|
+
enriched_description = knowledge_bundle.get("enriched_requirement", args.description)
|
|
2139
|
+
self.console.print(f" [green]✓[/green] 需求增强报告: {research_file}")
|
|
2140
|
+
self.console.print(
|
|
2141
|
+
f" [dim]本地知识 {len(knowledge_bundle.get('local_knowledge', []))} 条 | "
|
|
2142
|
+
f"联网结果 {len(knowledge_bundle.get('web_knowledge', []))} 条[/dim]"
|
|
2143
|
+
)
|
|
2144
|
+
self.console.print("")
|
|
2145
|
+
|
|
2146
|
+
# ========== 第 1 阶段: 生成文档 ==========
|
|
2147
|
+
self.console.print("[cyan]第 1 阶段: 生成专业文档...[/cyan]")
|
|
2148
|
+
from .creators import (
|
|
2149
|
+
DocumentGenerator,
|
|
2150
|
+
FrontendScaffoldBuilder,
|
|
2151
|
+
ImplementationScaffoldBuilder,
|
|
2152
|
+
RequirementParser,
|
|
2153
|
+
)
|
|
2154
|
+
|
|
2155
|
+
parser = RequirementParser()
|
|
2156
|
+
scenario = parser.detect_scenario(project_dir)
|
|
2157
|
+
|
|
2158
|
+
doc_generator = DocumentGenerator(
|
|
2159
|
+
name=project_name,
|
|
2160
|
+
description=enriched_description,
|
|
2161
|
+
platform=args.platform,
|
|
2162
|
+
frontend=args.frontend,
|
|
2163
|
+
backend=args.backend,
|
|
2164
|
+
domain=args.domain
|
|
2165
|
+
)
|
|
2166
|
+
|
|
2167
|
+
# 生成文档内容
|
|
2168
|
+
prd_content = doc_generator.generate_prd()
|
|
2169
|
+
arch_content = doc_generator.generate_architecture()
|
|
2170
|
+
uiux_content = doc_generator.generate_uiux()
|
|
2171
|
+
|
|
2172
|
+
# 写入文件
|
|
2173
|
+
|
|
2174
|
+
prd_file = output_dir / f"{project_name}-prd.md"
|
|
2175
|
+
arch_file = output_dir / f"{project_name}-architecture.md"
|
|
2176
|
+
uiux_file = output_dir / f"{project_name}-uiux.md"
|
|
2177
|
+
|
|
2178
|
+
prd_file.write_text(prd_content, encoding="utf-8")
|
|
2179
|
+
arch_file.write_text(arch_content, encoding="utf-8")
|
|
2180
|
+
uiux_file.write_text(uiux_content, encoding="utf-8")
|
|
2181
|
+
|
|
2182
|
+
plan_file = output_dir / f"{project_name}-execution-plan.md"
|
|
2183
|
+
frontend_blueprint_file = output_dir / f"{project_name}-frontend-blueprint.md"
|
|
2184
|
+
plan_file.write_text(doc_generator.generate_execution_plan(scenario=scenario), encoding="utf-8")
|
|
2185
|
+
frontend_blueprint_file.write_text(doc_generator.generate_frontend_blueprint(), encoding="utf-8")
|
|
2186
|
+
|
|
2187
|
+
self.console.print(f" [green]✓[/green] PRD: {prd_file}")
|
|
2188
|
+
self.console.print(f" [green]✓[/green] 架构: {arch_file}")
|
|
2189
|
+
self.console.print(f" [green]✓[/green] UI/UX: {uiux_file}")
|
|
2190
|
+
self.console.print(f" [green]✓[/green] 执行路线图: {plan_file}")
|
|
2191
|
+
self.console.print(f" [green]✓[/green] 前端蓝图: {frontend_blueprint_file}")
|
|
2192
|
+
self.console.print(f" [dim]场景识别: {scenario}[/dim]")
|
|
2193
|
+
self.console.print("")
|
|
2194
|
+
|
|
2195
|
+
# 保存技术栈到配置文件(供后续阶段使用)
|
|
2196
|
+
self._save_tech_stack_to_config(project_dir, tech_stack, args.description)
|
|
2197
|
+
|
|
2198
|
+
requirements = doc_generator.extract_requirements()
|
|
2199
|
+
phases = parser.build_execution_phases(scenario, requirements)
|
|
2200
|
+
|
|
2201
|
+
# ========== 第 2 阶段: 生成前端可演示骨架 ==========
|
|
2202
|
+
self.console.print("[cyan]第 2 阶段: 生成前端可演示骨架...[/cyan]")
|
|
2203
|
+
frontend_builder = FrontendScaffoldBuilder(
|
|
2204
|
+
project_dir=project_dir,
|
|
2205
|
+
name=project_name,
|
|
2206
|
+
description=args.description,
|
|
2207
|
+
frontend=args.frontend,
|
|
2208
|
+
)
|
|
2209
|
+
frontend_files = frontend_builder.generate(
|
|
2210
|
+
requirements=requirements,
|
|
2211
|
+
phases=phases,
|
|
2212
|
+
docs={
|
|
2213
|
+
"prd": str(prd_file),
|
|
2214
|
+
"architecture": str(arch_file),
|
|
2215
|
+
"uiux": str(uiux_file),
|
|
2216
|
+
"plan": str(plan_file),
|
|
2217
|
+
"frontend_blueprint": str(frontend_blueprint_file),
|
|
2218
|
+
},
|
|
2219
|
+
)
|
|
2220
|
+
self.console.print(f" [green]✓[/green] 页面: {frontend_files['html']}")
|
|
2221
|
+
self.console.print(f" [green]✓[/green] 样式: {frontend_files['css']}")
|
|
2222
|
+
self.console.print(f" [green]✓[/green] 脚本: {frontend_files['js']}")
|
|
2223
|
+
self.console.print("")
|
|
2224
|
+
|
|
2225
|
+
# ========== 第 3 阶段: 创建 Spec ==========
|
|
2226
|
+
self.console.print("[cyan]第 3 阶段: 创建 Spec 规范...[/cyan]")
|
|
2227
|
+
from .creators import SpecBuilder
|
|
2228
|
+
|
|
2229
|
+
spec_builder = SpecBuilder(
|
|
2230
|
+
project_dir=project_dir,
|
|
2231
|
+
name=project_name,
|
|
2232
|
+
description=args.description
|
|
2233
|
+
)
|
|
2234
|
+
|
|
2235
|
+
change_id = spec_builder.create_change(requirements, tech_stack, scenario=scenario)
|
|
2236
|
+
|
|
2237
|
+
self.console.print(f" [green]✓[/green] 变更 ID: {change_id}")
|
|
2238
|
+
self.console.print(f" [green]✓[/green] Spec: .super-dev/changes/{change_id}/")
|
|
2239
|
+
self.console.print("")
|
|
2240
|
+
|
|
2241
|
+
# ========== 第 4 阶段: 生成实现骨架 ==========
|
|
2242
|
+
scaffold_result: dict[str, list[str]] = {"frontend_files": [], "backend_files": []}
|
|
2243
|
+
if not args.skip_scaffold:
|
|
2244
|
+
self.console.print("[cyan]第 4 阶段: 生成前后端实现骨架...[/cyan]")
|
|
2245
|
+
implementation_builder = ImplementationScaffoldBuilder(
|
|
2246
|
+
project_dir=project_dir,
|
|
2247
|
+
name=project_name,
|
|
2248
|
+
frontend=args.frontend,
|
|
2249
|
+
backend=args.backend,
|
|
2250
|
+
)
|
|
2251
|
+
scaffold_result = implementation_builder.generate(requirements=requirements)
|
|
2252
|
+
self.console.print(
|
|
2253
|
+
f" [green]✓[/green] 前端骨架文件: {len(scaffold_result['frontend_files'])} 个"
|
|
2254
|
+
)
|
|
2255
|
+
self.console.print(
|
|
2256
|
+
f" [green]✓[/green] 后端骨架文件: {len(scaffold_result['backend_files'])} 个"
|
|
2257
|
+
)
|
|
2258
|
+
self.console.print("")
|
|
2259
|
+
else:
|
|
2260
|
+
self.console.print("[yellow]第 4 阶段: 生成前后端实现骨架 (跳过)[/yellow]")
|
|
2261
|
+
self.console.print("")
|
|
2262
|
+
|
|
2263
|
+
# ========== 第 5 阶段: 红队审查 ==========
|
|
2264
|
+
redteam_report = None
|
|
2265
|
+
if not args.skip_redteam:
|
|
2266
|
+
self.console.print("[cyan]第 5 阶段: 红队审查...[/cyan]")
|
|
2267
|
+
from .reviewers import RedTeamReviewer
|
|
2268
|
+
|
|
2269
|
+
reviewer = RedTeamReviewer(
|
|
2270
|
+
project_dir=project_dir,
|
|
2271
|
+
name=project_name,
|
|
2272
|
+
tech_stack=tech_stack
|
|
2273
|
+
)
|
|
2274
|
+
redteam_report = reviewer.review()
|
|
2275
|
+
|
|
2276
|
+
# 保存红队审查报告
|
|
2277
|
+
redteam_file = project_dir / "output" / f"{project_name}-redteam.md"
|
|
2278
|
+
redteam_file.parent.mkdir(parents=True, exist_ok=True)
|
|
2279
|
+
redteam_file.write_text(redteam_report.to_markdown(), encoding="utf-8")
|
|
2280
|
+
|
|
2281
|
+
self.console.print(f" [green]✓[/green] 安全问题: {sum(1 for i in redteam_report.security_issues if i.severity in ('critical', 'high'))} high/critical")
|
|
2282
|
+
self.console.print(f" [green]✓[/green] 性能问题: {sum(1 for i in redteam_report.performance_issues if i.severity in ('critical', 'high'))} high/critical")
|
|
2283
|
+
self.console.print(f" [green]✓[/green] 架构问题: {sum(1 for i in redteam_report.architecture_issues if i.severity in ('critical', 'high'))} high/critical")
|
|
2284
|
+
self.console.print(f" [green]✓[/green] 总分: {redteam_report.total_score}/100")
|
|
2285
|
+
self.console.print(f" [green]✓[/green] 报告: {redteam_file}")
|
|
2286
|
+
self.console.print("")
|
|
2287
|
+
|
|
2288
|
+
# 红队未通过时直接阻断,确保“先通过红队,再进入质量门禁”
|
|
2289
|
+
if not redteam_report.passed:
|
|
2290
|
+
self.console.print("[red]红队审查未通过,流水线终止[/red]")
|
|
2291
|
+
for reason in redteam_report.blocking_reasons:
|
|
2292
|
+
self.console.print(f" - {reason}")
|
|
2293
|
+
self.console.print("[dim]可使用 --skip-redteam 跳过该阶段(不推荐生产使用)[/dim]")
|
|
2294
|
+
return 1
|
|
2295
|
+
else:
|
|
2296
|
+
self.console.print("[yellow]第 5 阶段: 红队审查 (跳过)[/yellow]")
|
|
2297
|
+
self.console.print("")
|
|
2298
|
+
|
|
2299
|
+
# ========== 第 6 阶段: 质量门禁 ==========
|
|
2300
|
+
if not args.skip_quality_gate:
|
|
2301
|
+
self.console.print("[cyan]第 6 阶段: 质量门禁检查...[/cyan]")
|
|
2302
|
+
from .reviewers import QualityGateChecker
|
|
2303
|
+
|
|
2304
|
+
gate_checker = QualityGateChecker(
|
|
2305
|
+
project_dir=project_dir,
|
|
2306
|
+
name=project_name,
|
|
2307
|
+
tech_stack=tech_stack,
|
|
2308
|
+
scenario_override=scenario,
|
|
2309
|
+
threshold_override=args.quality_threshold,
|
|
2310
|
+
)
|
|
2311
|
+
|
|
2312
|
+
gate_result = gate_checker.check(redteam_report)
|
|
2313
|
+
|
|
2314
|
+
# 显示场景信息
|
|
2315
|
+
scenario_label = "0-1 新建项目" if gate_result.scenario == "0-1" else "1-N+1 增量开发"
|
|
2316
|
+
self.console.print(f" [dim]场景: {scenario_label}[/dim]")
|
|
2317
|
+
|
|
2318
|
+
# 保存质量门禁报告
|
|
2319
|
+
gate_file = project_dir / "output" / f"{project_name}-quality-gate.md"
|
|
2320
|
+
gate_file.parent.mkdir(parents=True, exist_ok=True)
|
|
2321
|
+
gate_file.write_text(gate_result.to_markdown(), encoding="utf-8")
|
|
2322
|
+
|
|
2323
|
+
status = "[green]通过[/green]" if gate_result.passed else "[red]未通过[/red]"
|
|
2324
|
+
self.console.print(f" {status} 总分: {gate_result.total_score}/100")
|
|
2325
|
+
self.console.print(f" [green]✓[/green] 报告: {gate_file}")
|
|
2326
|
+
self.console.print("")
|
|
2327
|
+
|
|
2328
|
+
# 质量门禁未通过,停止流水线
|
|
2329
|
+
if not gate_result.passed:
|
|
2330
|
+
self.console.print("[red]质量门禁未通过,流水线终止[/red]")
|
|
2331
|
+
self.console.print("[cyan]请修复以下问题后重新运行:[/cyan]")
|
|
2332
|
+
for failure in gate_result.critical_failures:
|
|
2333
|
+
self.console.print(f" - {failure}")
|
|
2334
|
+
return 1
|
|
2335
|
+
else:
|
|
2336
|
+
self.console.print("[yellow]第 6 阶段: 质量门禁检查 (跳过)[/yellow]")
|
|
2337
|
+
self.console.print("[dim]提示: 使用 --skip-quality-gate 跳过了质量门禁检查,建议后续补充测试和质量检查[/dim]")
|
|
2338
|
+
self.console.print("")
|
|
2339
|
+
|
|
2340
|
+
# ========== 第 7 阶段: 代码审查指南 ==========
|
|
2341
|
+
self.console.print("[cyan]第 7 阶段: 生成代码审查指南...[/cyan]")
|
|
2342
|
+
from .reviewers import CodeReviewGenerator
|
|
2343
|
+
|
|
2344
|
+
review_gen = CodeReviewGenerator(
|
|
2345
|
+
project_dir=project_dir,
|
|
2346
|
+
name=project_name,
|
|
2347
|
+
tech_stack=tech_stack
|
|
2348
|
+
)
|
|
2349
|
+
|
|
2350
|
+
review_guide = review_gen.generate()
|
|
2351
|
+
review_file = project_dir / "output" / f"{project_name}-code-review.md"
|
|
2352
|
+
review_file.write_text(review_guide, encoding="utf-8")
|
|
2353
|
+
|
|
2354
|
+
self.console.print(f" [green]✓[/green] 代码审查指南: {review_file}")
|
|
2355
|
+
self.console.print("")
|
|
2356
|
+
|
|
2357
|
+
# ========== 第 8 阶段: AI 提示词 ==========
|
|
2358
|
+
self.console.print("[cyan]第 8 阶段: 生成 AI 提示词...[/cyan]")
|
|
2359
|
+
from .creators import AIPromptGenerator
|
|
2360
|
+
|
|
2361
|
+
prompt_gen = AIPromptGenerator(
|
|
2362
|
+
project_dir=project_dir,
|
|
2363
|
+
name=project_name
|
|
2364
|
+
)
|
|
2365
|
+
|
|
2366
|
+
prompt_content = prompt_gen.generate()
|
|
2367
|
+
prompt_file = project_dir / "output" / f"{project_name}-ai-prompt.md"
|
|
2368
|
+
prompt_file.write_text(prompt_content, encoding="utf-8")
|
|
2369
|
+
|
|
2370
|
+
self.console.print(f" [green]✓[/green] AI 提示词: {prompt_file}")
|
|
2371
|
+
self.console.print("")
|
|
2372
|
+
|
|
2373
|
+
# ========== 第 9 阶段: CI/CD 配置 ==========
|
|
2374
|
+
self.console.print(f"[cyan]第 9 阶段: 生成 CI/CD 配置 ({args.cicd.upper()})...[/cyan]")
|
|
2375
|
+
from .deployers import CICDGenerator
|
|
2376
|
+
|
|
2377
|
+
cicd_gen = CICDGenerator(
|
|
2378
|
+
project_dir=project_dir,
|
|
2379
|
+
name=project_name,
|
|
2380
|
+
tech_stack=tech_stack,
|
|
2381
|
+
platform=self._normalize_cicd_platform(args.cicd)
|
|
2382
|
+
)
|
|
2383
|
+
|
|
2384
|
+
cicd_files = cicd_gen.generate()
|
|
2385
|
+
|
|
2386
|
+
for file_path, content in cicd_files.items():
|
|
2387
|
+
full_path = project_dir / file_path
|
|
2388
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2389
|
+
full_path.write_text(content, encoding="utf-8")
|
|
2390
|
+
self.console.print(f" [green]✓[/green] {file_path}")
|
|
2391
|
+
|
|
2392
|
+
self.console.print("")
|
|
2393
|
+
|
|
2394
|
+
# ========== 第 10 阶段: 部署修复模板 ==========
|
|
2395
|
+
self.console.print("[cyan]第 10 阶段: 生成部署修复模板...[/cyan]")
|
|
2396
|
+
remediation_outputs = self._export_deploy_remediation_templates(
|
|
2397
|
+
project_dir=project_dir,
|
|
2398
|
+
cicd_platform=args.cicd,
|
|
2399
|
+
only_missing=True,
|
|
2400
|
+
)
|
|
2401
|
+
self.console.print(f" [green]✓[/green] 环境模板: {remediation_outputs['env_file']}")
|
|
2402
|
+
self.console.print(f" [green]✓[/green] 检查清单: {remediation_outputs['checklist_file']}")
|
|
2403
|
+
self.console.print(f" [dim]缺失变量条目: {remediation_outputs['items_count']}[/dim]")
|
|
2404
|
+
if remediation_outputs.get("per_platform_files"):
|
|
2405
|
+
self.console.print(f" [green]✓[/green] 平台拆分模板: {len(remediation_outputs['per_platform_files'])} 组")
|
|
2406
|
+
self.console.print("")
|
|
2407
|
+
|
|
2408
|
+
# ========== 第 11 阶段: 数据库迁移 + 项目交付包 ==========
|
|
2409
|
+
self.console.print("[cyan]第 11 阶段: 生成数据库迁移脚本 + 项目交付包...[/cyan]")
|
|
2410
|
+
from .deployers import DeliveryPackager, MigrationGenerator
|
|
2411
|
+
|
|
2412
|
+
migration_gen = MigrationGenerator(
|
|
2413
|
+
project_dir=project_dir,
|
|
2414
|
+
name=project_name,
|
|
2415
|
+
tech_stack=tech_stack
|
|
2416
|
+
)
|
|
2417
|
+
|
|
2418
|
+
migration_files = migration_gen.generate()
|
|
2419
|
+
|
|
2420
|
+
for file_path, content in migration_files.items():
|
|
2421
|
+
full_path = project_dir / file_path
|
|
2422
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2423
|
+
full_path.write_text(content, encoding="utf-8")
|
|
2424
|
+
self.console.print(f" [green]✓[/green] {file_path}")
|
|
2425
|
+
|
|
2426
|
+
packager = DeliveryPackager(
|
|
2427
|
+
project_dir=project_dir,
|
|
2428
|
+
name=project_name,
|
|
2429
|
+
version=__version__,
|
|
2430
|
+
)
|
|
2431
|
+
delivery_outputs = packager.package(cicd_platform=args.cicd)
|
|
2432
|
+
missing_required_raw = delivery_outputs.get("missing_required_count", 0)
|
|
2433
|
+
missing_required_count = missing_required_raw if isinstance(missing_required_raw, int) else 0
|
|
2434
|
+
self.console.print(f" [green]✓[/green] 清单: {delivery_outputs['manifest_file']}")
|
|
2435
|
+
self.console.print(f" [green]✓[/green] 报告: {delivery_outputs['report_file']}")
|
|
2436
|
+
self.console.print(f" [green]✓[/green] 交付包: {delivery_outputs['archive_file']}")
|
|
2437
|
+
self.console.print(
|
|
2438
|
+
f" [dim]状态: {delivery_outputs['status']} | 缺失必需项: {missing_required_count}[/dim]"
|
|
2439
|
+
)
|
|
2440
|
+
if missing_required_count > 0:
|
|
2441
|
+
self.console.print("[yellow] 交付包标记为 incomplete,请补齐缺失项后重新打包[/yellow]")
|
|
2442
|
+
self.console.print("")
|
|
2443
|
+
|
|
2444
|
+
# ========== 完成 ==========
|
|
2445
|
+
self.console.print(f"[cyan]{'=' * 60}[/cyan]")
|
|
2446
|
+
self.console.print("[green]✓ 流水线完成![/green]")
|
|
2447
|
+
self.console.print(f"[cyan]{'=' * 60}[/cyan]")
|
|
2448
|
+
self.console.print("")
|
|
2449
|
+
self.console.print("[cyan]生成的文件:[/cyan]")
|
|
2450
|
+
self.console.print(" 文档:")
|
|
2451
|
+
self.console.print(f" - 需求增强报告: output/{project_name}-research.md")
|
|
2452
|
+
self.console.print(f" - PRD: output/{project_name}-prd.md")
|
|
2453
|
+
self.console.print(f" - 架构: output/{project_name}-architecture.md")
|
|
2454
|
+
self.console.print(f" - UI/UX: output/{project_name}-uiux.md")
|
|
2455
|
+
self.console.print(f" - 执行路线图: output/{project_name}-execution-plan.md")
|
|
2456
|
+
self.console.print(f" - 前端蓝图: output/{project_name}-frontend-blueprint.md")
|
|
2457
|
+
if not args.skip_redteam:
|
|
2458
|
+
self.console.print(f" - 红队审查: output/{project_name}-redteam.md")
|
|
2459
|
+
if not args.skip_quality_gate:
|
|
2460
|
+
self.console.print(f" - 质量门禁: output/{project_name}-quality-gate.md")
|
|
2461
|
+
self.console.print(f" - 代码审查: output/{project_name}-code-review.md")
|
|
2462
|
+
self.console.print(f" - AI 提示词: output/{project_name}-ai-prompt.md")
|
|
2463
|
+
self.console.print("")
|
|
2464
|
+
self.console.print(" 前端演示:")
|
|
2465
|
+
self.console.print(" - output/frontend/index.html")
|
|
2466
|
+
self.console.print(" - output/frontend/styles.css")
|
|
2467
|
+
self.console.print(" - output/frontend/app.js")
|
|
2468
|
+
self.console.print("")
|
|
2469
|
+
if not args.skip_scaffold:
|
|
2470
|
+
self.console.print(" 实现骨架:")
|
|
2471
|
+
self.console.print(" - frontend/src/*")
|
|
2472
|
+
self.console.print(" - backend/src/*")
|
|
2473
|
+
self.console.print(" - backend/API_CONTRACT.md")
|
|
2474
|
+
self.console.print("")
|
|
2475
|
+
self.console.print(" CI/CD:")
|
|
2476
|
+
for file_path in cicd_files.keys():
|
|
2477
|
+
self.console.print(f" - {file_path}")
|
|
2478
|
+
self.console.print("")
|
|
2479
|
+
self.console.print(" 部署修复模板:")
|
|
2480
|
+
self.console.print(f" - {Path(remediation_outputs['env_file']).name}")
|
|
2481
|
+
self.console.print(f" - {Path(remediation_outputs['checklist_file']).relative_to(project_dir)}")
|
|
2482
|
+
if remediation_outputs.get("per_platform_files"):
|
|
2483
|
+
for item in remediation_outputs["per_platform_files"]:
|
|
2484
|
+
self.console.print(
|
|
2485
|
+
f" - {Path(item['checklist_file']).relative_to(project_dir)}"
|
|
2486
|
+
)
|
|
2487
|
+
self.console.print(
|
|
2488
|
+
f" - {Path(item['env_file']).relative_to(project_dir)}"
|
|
2489
|
+
)
|
|
2490
|
+
self.console.print("")
|
|
2491
|
+
self.console.print(" 数据库迁移:")
|
|
2492
|
+
for file_path in migration_files.keys():
|
|
2493
|
+
self.console.print(f" - {file_path}")
|
|
2494
|
+
self.console.print("")
|
|
2495
|
+
self.console.print(" 项目交付包:")
|
|
2496
|
+
self.console.print(
|
|
2497
|
+
f" - {Path(str(delivery_outputs['manifest_file'])).relative_to(project_dir)}"
|
|
2498
|
+
)
|
|
2499
|
+
self.console.print(
|
|
2500
|
+
f" - {Path(str(delivery_outputs['report_file'])).relative_to(project_dir)}"
|
|
2501
|
+
)
|
|
2502
|
+
self.console.print(
|
|
2503
|
+
f" - {Path(str(delivery_outputs['archive_file'])).relative_to(project_dir)}"
|
|
2504
|
+
)
|
|
2505
|
+
self.console.print("")
|
|
2506
|
+
self.console.print("[cyan]下一步:[/cyan]")
|
|
2507
|
+
self.console.print(" 1. 打开 output/frontend/index.html 评审前端骨架")
|
|
2508
|
+
self.console.print(" 2. 对照执行路线图按阶段推进开发")
|
|
2509
|
+
self.console.print(" 3. 使用代码审查指南进行评审和修复")
|
|
2510
|
+
self.console.print(" 4. 配置 CI/CD 平台 (设置 secrets/credentials)")
|
|
2511
|
+
self.console.print(" 5. 运行数据库迁移脚本并推送代码触发流水线")
|
|
2512
|
+
self.console.print(" 6. 使用 output/delivery/* 作为对外交付包")
|
|
2513
|
+
self.console.print("")
|
|
2514
|
+
|
|
2515
|
+
except Exception as e:
|
|
2516
|
+
self.console.print(f"[red]流水线失败: {e}[/red]")
|
|
2517
|
+
import traceback
|
|
2518
|
+
self.console.print(traceback.format_exc())
|
|
2519
|
+
return 1
|
|
2520
|
+
|
|
2521
|
+
return 0
|
|
2522
|
+
|
|
2523
|
+
def _cmd_config(self, args) -> int:
|
|
2524
|
+
"""配置管理"""
|
|
2525
|
+
config_manager = get_config_manager()
|
|
2526
|
+
|
|
2527
|
+
if not config_manager.exists():
|
|
2528
|
+
self.console.print("[red]未找到项目配置[/red]")
|
|
2529
|
+
return 1
|
|
2530
|
+
|
|
2531
|
+
if args.action == "list":
|
|
2532
|
+
# 列出所有配置
|
|
2533
|
+
config = config_manager.config
|
|
2534
|
+
self.console.print("[cyan]项目配置:[/cyan]")
|
|
2535
|
+
for key, value in config.__dict__.items():
|
|
2536
|
+
if not key.startswith("_"):
|
|
2537
|
+
self.console.print(f" {key}: {value}")
|
|
2538
|
+
|
|
2539
|
+
elif args.action == "get":
|
|
2540
|
+
if not args.key:
|
|
2541
|
+
self.console.print("[red]请指定配置键[/red]")
|
|
2542
|
+
return 1
|
|
2543
|
+
value = config_manager.get(args.key)
|
|
2544
|
+
self.console.print(f"{args.key}: {value}")
|
|
2545
|
+
|
|
2546
|
+
elif args.action == "set":
|
|
2547
|
+
if not args.key or not args.value:
|
|
2548
|
+
self.console.print("[red]请指定配置键和值[/red]")
|
|
2549
|
+
return 1
|
|
2550
|
+
config_manager.update(**{args.key: args.value})
|
|
2551
|
+
self.console.print(f"[green]✓[/green] {args.key} = {args.value}")
|
|
2552
|
+
|
|
2553
|
+
return 0
|
|
2554
|
+
|
|
2555
|
+
def _cmd_skill(self, args) -> int:
|
|
2556
|
+
"""Skill 管理"""
|
|
2557
|
+
from .skills import SkillManager
|
|
2558
|
+
|
|
2559
|
+
manager = SkillManager(Path.cwd())
|
|
2560
|
+
|
|
2561
|
+
if args.action == "targets":
|
|
2562
|
+
self.console.print("[cyan]支持的 Skill 目标平台:[/cyan]")
|
|
2563
|
+
for target in manager.list_targets():
|
|
2564
|
+
self.console.print(f" - {target}")
|
|
2565
|
+
return 0
|
|
2566
|
+
|
|
2567
|
+
if args.action == "list":
|
|
2568
|
+
installed = manager.list_installed(args.target)
|
|
2569
|
+
if not installed:
|
|
2570
|
+
self.console.print(f"[dim]{args.target} 未安装任何 skill[/dim]")
|
|
2571
|
+
return 0
|
|
2572
|
+
|
|
2573
|
+
self.console.print(f"[cyan]{args.target} 已安装 skill:[/cyan]")
|
|
2574
|
+
for skill_name in installed:
|
|
2575
|
+
self.console.print(f" - {skill_name}")
|
|
2576
|
+
return 0
|
|
2577
|
+
|
|
2578
|
+
if args.action == "install":
|
|
2579
|
+
if not args.source_or_name:
|
|
2580
|
+
self.console.print("[red]请提供 skill 来源(目录/git/super-dev)[/red]")
|
|
2581
|
+
return 1
|
|
2582
|
+
|
|
2583
|
+
try:
|
|
2584
|
+
result = manager.install(
|
|
2585
|
+
source=args.source_or_name,
|
|
2586
|
+
target=args.target,
|
|
2587
|
+
name=args.name,
|
|
2588
|
+
force=args.force,
|
|
2589
|
+
)
|
|
2590
|
+
except Exception as e:
|
|
2591
|
+
self.console.print(f"[red]Skill 安装失败: {e}[/red]")
|
|
2592
|
+
return 1
|
|
2593
|
+
|
|
2594
|
+
self.console.print("[green]✓ Skill 安装成功[/green]")
|
|
2595
|
+
self.console.print(f" 名称: {result.name}")
|
|
2596
|
+
self.console.print(f" 目标: {result.target}")
|
|
2597
|
+
self.console.print(f" 路径: {result.path}")
|
|
2598
|
+
self.console.print(f" 来源: {result.source}")
|
|
2599
|
+
return 0
|
|
2600
|
+
|
|
2601
|
+
if args.action == "uninstall":
|
|
2602
|
+
if not args.source_or_name:
|
|
2603
|
+
self.console.print("[red]请提供要卸载的 skill 名称[/red]")
|
|
2604
|
+
return 1
|
|
2605
|
+
|
|
2606
|
+
try:
|
|
2607
|
+
removed_path = manager.uninstall(args.source_or_name, args.target)
|
|
2608
|
+
except Exception as e:
|
|
2609
|
+
self.console.print(f"[red]Skill 卸载失败: {e}[/red]")
|
|
2610
|
+
return 1
|
|
2611
|
+
|
|
2612
|
+
self.console.print("[green]✓ Skill 已卸载[/green]")
|
|
2613
|
+
self.console.print(f" 路径: {removed_path}")
|
|
2614
|
+
return 0
|
|
2615
|
+
|
|
2616
|
+
self.console.print("[yellow]未知 skill 操作[/yellow]")
|
|
2617
|
+
return 1
|
|
2618
|
+
|
|
2619
|
+
def _cmd_integrate(self, args) -> int:
|
|
2620
|
+
"""多平台集成配置"""
|
|
2621
|
+
from .integrations import IntegrationManager
|
|
2622
|
+
|
|
2623
|
+
manager = IntegrationManager(Path.cwd())
|
|
2624
|
+
|
|
2625
|
+
if args.action == "list":
|
|
2626
|
+
self.console.print("[cyan]支持的集成平台:[/cyan]")
|
|
2627
|
+
for target in manager.list_targets():
|
|
2628
|
+
self.console.print(f" - {target.name}: {target.description}")
|
|
2629
|
+
return 0
|
|
2630
|
+
|
|
2631
|
+
if args.action == "setup":
|
|
2632
|
+
if args.all:
|
|
2633
|
+
results = manager.setup_all(force=args.force)
|
|
2634
|
+
self.console.print("[green]✓ 已完成所有平台集成配置[/green]")
|
|
2635
|
+
for platform, files in results.items():
|
|
2636
|
+
if not files:
|
|
2637
|
+
self.console.print(f" {platform}: [dim]无变更[/dim]")
|
|
2638
|
+
continue
|
|
2639
|
+
self.console.print(f" {platform}:")
|
|
2640
|
+
for file_path in files:
|
|
2641
|
+
self.console.print(f" - {file_path}")
|
|
2642
|
+
return 0
|
|
2643
|
+
|
|
2644
|
+
if not args.target:
|
|
2645
|
+
self.console.print("[red]请通过 --target 指定平台,或使用 --all[/red]")
|
|
2646
|
+
return 1
|
|
2647
|
+
|
|
2648
|
+
files = manager.setup(args.target, force=args.force)
|
|
2649
|
+
if not files:
|
|
2650
|
+
self.console.print("[yellow]配置已存在,无需修改(可加 --force 覆盖)[/yellow]")
|
|
2651
|
+
return 0
|
|
2652
|
+
|
|
2653
|
+
self.console.print("[green]✓ 集成配置已生成[/green]")
|
|
2654
|
+
for file_path in files:
|
|
2655
|
+
self.console.print(f" - {file_path}")
|
|
2656
|
+
return 0
|
|
2657
|
+
|
|
2658
|
+
self.console.print("[yellow]未知 integrate 操作[/yellow]")
|
|
2659
|
+
return 1
|
|
2660
|
+
|
|
2661
|
+
def _cmd_spec(self, args) -> int:
|
|
2662
|
+
"""Spec-Driven Development 命令"""
|
|
2663
|
+
from .specs import ChangeManager, SpecGenerator, SpecManager
|
|
2664
|
+
from .specs.models import ChangeStatus
|
|
2665
|
+
|
|
2666
|
+
project_dir = Path.cwd()
|
|
2667
|
+
|
|
2668
|
+
if args.spec_action == "init":
|
|
2669
|
+
# 初始化 SDD 目录结构
|
|
2670
|
+
generator = SpecGenerator(project_dir)
|
|
2671
|
+
agents_path, project_path = generator.init_sdd()
|
|
2672
|
+
|
|
2673
|
+
self.console.print("[green]✓[/green] SDD 目录结构已初始化")
|
|
2674
|
+
self.console.print(" [dim].super-dev/specs/[/dim] - 当前规范")
|
|
2675
|
+
self.console.print(" [dim].super-dev/changes/[/dim] - 变更提案")
|
|
2676
|
+
self.console.print(" [dim].super-dev/archive/[/dim] - 已归档变更")
|
|
2677
|
+
self.console.print("")
|
|
2678
|
+
self.console.print("[cyan]下一步:[/cyan]")
|
|
2679
|
+
self.console.print(" 1. 编辑 .super-dev/project.md 填写项目上下文")
|
|
2680
|
+
self.console.print(" 2. 运行 'super-dev spec propose <id>' 创建变更提案")
|
|
2681
|
+
|
|
2682
|
+
elif args.spec_action == "list":
|
|
2683
|
+
# 列出所有变更
|
|
2684
|
+
manager = ChangeManager(project_dir)
|
|
2685
|
+
status_filter = None
|
|
2686
|
+
if args.status:
|
|
2687
|
+
status_filter = ChangeStatus(args.status)
|
|
2688
|
+
|
|
2689
|
+
changes = manager.list_changes(status=status_filter)
|
|
2690
|
+
|
|
2691
|
+
if not changes:
|
|
2692
|
+
self.console.print("[dim]没有找到变更[/dim]")
|
|
2693
|
+
return 0
|
|
2694
|
+
|
|
2695
|
+
self.console.print("[cyan]变更列表:[/cyan]")
|
|
2696
|
+
for change in changes:
|
|
2697
|
+
status_color = {
|
|
2698
|
+
ChangeStatus.DRAFT: "dim",
|
|
2699
|
+
ChangeStatus.PROPOSED: "yellow",
|
|
2700
|
+
ChangeStatus.APPROVED: "blue",
|
|
2701
|
+
ChangeStatus.IN_PROGRESS: "cyan",
|
|
2702
|
+
ChangeStatus.COMPLETED: "green",
|
|
2703
|
+
ChangeStatus.ARCHIVED: "dim",
|
|
2704
|
+
}.get(change.status, "white")
|
|
2705
|
+
|
|
2706
|
+
self.console.print(
|
|
2707
|
+
f" [{status_color}]{change.id}[/] - {change.title} "
|
|
2708
|
+
f"({change.status.value})"
|
|
2709
|
+
)
|
|
2710
|
+
if change.tasks:
|
|
2711
|
+
rate = change.completion_rate
|
|
2712
|
+
self.console.print(f" [dim]进度: {rate:.0f}% ({sum(1 for t in change.tasks if t.status.value == 'completed')}/{len(change.tasks)} 任务)[/dim]")
|
|
2713
|
+
|
|
2714
|
+
elif args.spec_action == "show":
|
|
2715
|
+
# 显示变更详情
|
|
2716
|
+
manager = ChangeManager(project_dir)
|
|
2717
|
+
loaded_change = manager.load_change(args.change_id)
|
|
2718
|
+
|
|
2719
|
+
if not loaded_change:
|
|
2720
|
+
self.console.print(f"[red]变更不存在: {args.change_id}[/red]")
|
|
2721
|
+
return 1
|
|
2722
|
+
|
|
2723
|
+
self.console.print(f"[cyan]变更详情: {loaded_change.id}[/cyan]")
|
|
2724
|
+
self.console.print(f" 标题: {loaded_change.title}")
|
|
2725
|
+
self.console.print(f" 状态: {loaded_change.status.value}")
|
|
2726
|
+
|
|
2727
|
+
if loaded_change.proposal:
|
|
2728
|
+
self.console.print("")
|
|
2729
|
+
self.console.print("[cyan]提案:[/cyan]")
|
|
2730
|
+
if loaded_change.proposal.description:
|
|
2731
|
+
self.console.print(f" {loaded_change.proposal.description}")
|
|
2732
|
+
if loaded_change.proposal.motivation:
|
|
2733
|
+
self.console.print(f"[dim]动机: {loaded_change.proposal.motivation}[/dim]")
|
|
2734
|
+
|
|
2735
|
+
if loaded_change.tasks:
|
|
2736
|
+
self.console.print("")
|
|
2737
|
+
self.console.print("[cyan]任务:[/cyan]")
|
|
2738
|
+
for task in loaded_change.tasks:
|
|
2739
|
+
checkbox = "[x]" if task.status.value == "completed" else "[ ]"
|
|
2740
|
+
self.console.print(f" {checkbox} {task.id}: {task.title}")
|
|
2741
|
+
|
|
2742
|
+
if loaded_change.spec_deltas:
|
|
2743
|
+
self.console.print("")
|
|
2744
|
+
self.console.print("[cyan]规范变更:[/cyan]")
|
|
2745
|
+
for delta in loaded_change.spec_deltas:
|
|
2746
|
+
self.console.print(f" - {delta.spec_name} ({delta.delta_type.value})")
|
|
2747
|
+
|
|
2748
|
+
elif args.spec_action == "propose":
|
|
2749
|
+
# 创建变更提案
|
|
2750
|
+
generator = SpecGenerator(project_dir)
|
|
2751
|
+
change = generator.create_change(
|
|
2752
|
+
change_id=args.change_id,
|
|
2753
|
+
title=args.title,
|
|
2754
|
+
description=args.description,
|
|
2755
|
+
motivation=args.motivation or "",
|
|
2756
|
+
impact=args.impact or ""
|
|
2757
|
+
)
|
|
2758
|
+
|
|
2759
|
+
self.console.print(f"[green]✓[/green] 变更提案已创建: {change.id}")
|
|
2760
|
+
self.console.print(f" [dim].super-dev/changes/{change.id}/[/dim]")
|
|
2761
|
+
self.console.print("")
|
|
2762
|
+
self.console.print("[cyan]下一步:[/cyan]")
|
|
2763
|
+
self.console.print(f" 1. 运行 'super-dev spec add-req {change.id} <spec> <req> <desc>' 添加需求")
|
|
2764
|
+
self.console.print(f" 2. 或 'super-dev spec show {change.id}' 查看详情")
|
|
2765
|
+
|
|
2766
|
+
elif args.spec_action == "add-req":
|
|
2767
|
+
# 向变更添加需求
|
|
2768
|
+
generator = SpecGenerator(project_dir)
|
|
2769
|
+
delta = generator.add_requirement_to_change(
|
|
2770
|
+
change_id=args.change_id,
|
|
2771
|
+
spec_name=args.spec_name,
|
|
2772
|
+
requirement_name=args.req_name,
|
|
2773
|
+
description=args.description
|
|
2774
|
+
)
|
|
2775
|
+
|
|
2776
|
+
self.console.print("[green]✓[/green] 需求已添加到变更")
|
|
2777
|
+
self.console.print(f" 规范: {delta.spec_name}")
|
|
2778
|
+
self.console.print(f" 需求: {args.req_name}")
|
|
2779
|
+
|
|
2780
|
+
elif args.spec_action == "archive":
|
|
2781
|
+
# 归档变更
|
|
2782
|
+
if not args.yes:
|
|
2783
|
+
self.console.print(f"[yellow]即将归档变更: {args.change_id}[/yellow]")
|
|
2784
|
+
self.console.print("[dim]这将把规范增量合并到主规范中[/dim]")
|
|
2785
|
+
response = input("确认? (y/N): ")
|
|
2786
|
+
if response.lower() != "y":
|
|
2787
|
+
self.console.print("[dim]已取消[/dim]")
|
|
2788
|
+
return 0
|
|
2789
|
+
|
|
2790
|
+
change_manager = ChangeManager(project_dir)
|
|
2791
|
+
spec_manager = SpecManager(project_dir)
|
|
2792
|
+
|
|
2793
|
+
try:
|
|
2794
|
+
change = change_manager.archive_change(args.change_id, spec_manager)
|
|
2795
|
+
self.console.print(f"[green]✓[/green] 变更已归档: {change.id}")
|
|
2796
|
+
self.console.print(f" [dim].super-dev/archive/{change.id}/[/dim]")
|
|
2797
|
+
except FileNotFoundError as e:
|
|
2798
|
+
self.console.print(f"[red]{e}[/red]")
|
|
2799
|
+
return 1
|
|
2800
|
+
except Exception as e:
|
|
2801
|
+
self.console.print(f"[red]归档失败: {e}[/red]")
|
|
2802
|
+
return 1
|
|
2803
|
+
|
|
2804
|
+
elif args.spec_action == "validate":
|
|
2805
|
+
# 验证规格格式
|
|
2806
|
+
from .specs import SpecValidator
|
|
2807
|
+
|
|
2808
|
+
validator = SpecValidator(project_dir)
|
|
2809
|
+
|
|
2810
|
+
if args.change_id:
|
|
2811
|
+
# 验证单个变更
|
|
2812
|
+
result = validator.validate_change(args.change_id)
|
|
2813
|
+
self.console.print(f"[cyan]验证变更: {args.change_id}[/cyan]")
|
|
2814
|
+
else:
|
|
2815
|
+
# 验证所有变更
|
|
2816
|
+
result = validator.validate_all()
|
|
2817
|
+
self.console.print("[cyan]验证所有变更[/cyan]")
|
|
2818
|
+
|
|
2819
|
+
self.console.print(result.to_summary())
|
|
2820
|
+
|
|
2821
|
+
if args.verbose or (not result.is_valid):
|
|
2822
|
+
# 显示详细信息
|
|
2823
|
+
for error in result.errors:
|
|
2824
|
+
self.console.print(
|
|
2825
|
+
f" [red]错误[/red]: {error.message}"
|
|
2826
|
+
)
|
|
2827
|
+
if error.line > 0:
|
|
2828
|
+
self.console.print(
|
|
2829
|
+
f" [dim]{error.file}:{error.line}[/dim]"
|
|
2830
|
+
)
|
|
2831
|
+
|
|
2832
|
+
for warning in result.warnings:
|
|
2833
|
+
self.console.print(
|
|
2834
|
+
f" [yellow]警告[/yellow]: {warning.message}"
|
|
2835
|
+
)
|
|
2836
|
+
if warning.line > 0:
|
|
2837
|
+
self.console.print(
|
|
2838
|
+
f" [dim]{warning.file}:{warning.line}[/dim]"
|
|
2839
|
+
)
|
|
2840
|
+
|
|
2841
|
+
return 0 if result.is_valid else 1
|
|
2842
|
+
|
|
2843
|
+
elif args.spec_action == "view":
|
|
2844
|
+
# 交互式仪表板
|
|
2845
|
+
from rich.console import Console
|
|
2846
|
+
from rich.panel import Panel
|
|
2847
|
+
from rich.table import Table
|
|
2848
|
+
from rich.text import Text
|
|
2849
|
+
|
|
2850
|
+
console = Console()
|
|
2851
|
+
change_manager = ChangeManager(project_dir)
|
|
2852
|
+
spec_manager = SpecManager(project_dir)
|
|
2853
|
+
|
|
2854
|
+
# 获取所有变更和规范
|
|
2855
|
+
changes = change_manager.list_changes()
|
|
2856
|
+
specs = spec_manager.list_specs()
|
|
2857
|
+
|
|
2858
|
+
# 标题
|
|
2859
|
+
title = Text.assemble(
|
|
2860
|
+
("Super Dev ", "bold cyan"),
|
|
2861
|
+
("Spec Dashboard", "bold white"),
|
|
2862
|
+
)
|
|
2863
|
+
console.print(Panel(title, padding=(0, 1)))
|
|
2864
|
+
|
|
2865
|
+
# 变更统计
|
|
2866
|
+
if changes:
|
|
2867
|
+
table = Table(title="活跃变更", show_header=True, header_style="bold magenta")
|
|
2868
|
+
table.add_column("ID", style="cyan", width=20)
|
|
2869
|
+
table.add_column("标题", style="white", width=30)
|
|
2870
|
+
table.add_column("状态", style="yellow", width=12)
|
|
2871
|
+
table.add_column("进度", style="green", width=10)
|
|
2872
|
+
table.add_column("任务", style="blue", width=8)
|
|
2873
|
+
|
|
2874
|
+
for change in changes:
|
|
2875
|
+
progress = f"{change.completion_rate:.0f}%"
|
|
2876
|
+
tasks = f"{sum(1 for t in change.tasks if t.status.value == 'completed')}/{len(change.tasks)}"
|
|
2877
|
+
table.add_row(
|
|
2878
|
+
change.id,
|
|
2879
|
+
change.title or "(无标题)",
|
|
2880
|
+
change.status.value,
|
|
2881
|
+
progress,
|
|
2882
|
+
tasks
|
|
2883
|
+
)
|
|
2884
|
+
|
|
2885
|
+
console.print(table)
|
|
2886
|
+
else:
|
|
2887
|
+
console.print("[dim]没有活跃变更[/dim]")
|
|
2888
|
+
|
|
2889
|
+
# 规范列表
|
|
2890
|
+
if specs:
|
|
2891
|
+
console.print("")
|
|
2892
|
+
specs_table = Table(title="当前规范", show_header=True, header_style="bold green")
|
|
2893
|
+
specs_table.add_column("规范名称", style="cyan", width=30)
|
|
2894
|
+
specs_table.add_column("文件路径", style="dim", width=50)
|
|
2895
|
+
|
|
2896
|
+
for spec_name in specs:
|
|
2897
|
+
spec_path = spec_manager.get_spec_path(spec_name)
|
|
2898
|
+
specs_table.add_row(spec_name, str(spec_path.relative_to(project_dir)))
|
|
2899
|
+
|
|
2900
|
+
console.print(specs_table)
|
|
2901
|
+
|
|
2902
|
+
# 统计信息
|
|
2903
|
+
console.print("")
|
|
2904
|
+
stats_table = Table(show_header=False, box=None)
|
|
2905
|
+
stats_table.add_column("指标", style="bold white")
|
|
2906
|
+
stats_table.add_column("数量", style="cyan")
|
|
2907
|
+
|
|
2908
|
+
stats_table.add_row("活跃变更", str(len(changes)))
|
|
2909
|
+
stats_table.add_row("规范文件", str(len(specs)))
|
|
2910
|
+
stats_table.add_row("待处理任务", str(sum(1 for c in changes for t in c.tasks if t.status.value == "pending")))
|
|
2911
|
+
|
|
2912
|
+
console.print(stats_table)
|
|
2913
|
+
|
|
2914
|
+
return 0
|
|
2915
|
+
|
|
2916
|
+
else:
|
|
2917
|
+
self.console.print("[yellow]请指定 SDD 命令[/yellow]")
|
|
2918
|
+
return 1
|
|
2919
|
+
|
|
2920
|
+
return 0
|
|
2921
|
+
|
|
2922
|
+
# ==================== 辅助方法 ====================
|
|
2923
|
+
|
|
2924
|
+
def _is_direct_requirement_input(self, argv: list[str]) -> bool:
|
|
2925
|
+
"""判断是否为直达需求输入(非子命令模式)"""
|
|
2926
|
+
if not argv:
|
|
2927
|
+
return False
|
|
2928
|
+
|
|
2929
|
+
first = argv[0]
|
|
2930
|
+
if first.startswith("-"):
|
|
2931
|
+
return False
|
|
2932
|
+
|
|
2933
|
+
known_commands = {
|
|
2934
|
+
"init", "analyze", "workflow", "studio", "expert", "quality", "preview",
|
|
2935
|
+
"deploy", "create", "design", "spec", "pipeline", "config", "skill", "integrate",
|
|
2936
|
+
}
|
|
2937
|
+
return first not in known_commands
|
|
2938
|
+
|
|
2939
|
+
def _normalize_pipeline_frontend(self, frontend: str) -> str:
|
|
2940
|
+
"""将 init 的前端框架映射到 pipeline 可接受值"""
|
|
2941
|
+
mapping = {
|
|
2942
|
+
"next": "react",
|
|
2943
|
+
"remix": "react",
|
|
2944
|
+
"react-vite": "react",
|
|
2945
|
+
"gatsby": "react",
|
|
2946
|
+
"nuxt": "vue",
|
|
2947
|
+
"vue-vite": "vue",
|
|
2948
|
+
"sveltekit": "svelte",
|
|
2949
|
+
"astro": "react",
|
|
2950
|
+
"solid": "react",
|
|
2951
|
+
"qwik": "react",
|
|
2952
|
+
}
|
|
2953
|
+
if frontend in {"react", "vue", "angular", "svelte", "none"}:
|
|
2954
|
+
return frontend
|
|
2955
|
+
return mapping.get(frontend, "react")
|
|
2956
|
+
|
|
2957
|
+
def _normalize_cicd_platform(self, value: str) -> CICDPlatform:
|
|
2958
|
+
valid = {"github", "gitlab", "jenkins", "azure", "bitbucket", "all"}
|
|
2959
|
+
normalized = (value or "github").lower()
|
|
2960
|
+
if normalized not in valid:
|
|
2961
|
+
raise ValueError(f"不支持的 CI/CD 平台: {value}")
|
|
2962
|
+
return cast(CICDPlatform, normalized)
|
|
2963
|
+
|
|
2964
|
+
def _sanitize_project_name(self, name: str) -> str:
|
|
2965
|
+
"""清理项目名,避免路径非法字符"""
|
|
2966
|
+
import re
|
|
2967
|
+
|
|
2968
|
+
cleaned = re.sub(r"[\\/:*?\"<>|]+", "-", name.strip())
|
|
2969
|
+
cleaned = re.sub(r"\s+", "-", cleaned)
|
|
2970
|
+
cleaned = re.sub(r"-{2,}", "-", cleaned).strip("-")
|
|
2971
|
+
return cleaned or "my-project"
|
|
2972
|
+
|
|
2973
|
+
def _run_direct_requirement(self, description: str) -> int:
|
|
2974
|
+
"""将 `super-dev <需求描述>` 直达路由到完整流水线"""
|
|
2975
|
+
if not description:
|
|
2976
|
+
self.console.print("[red]请提供需求描述[/red]")
|
|
2977
|
+
return 1
|
|
2978
|
+
|
|
2979
|
+
config_manager = get_config_manager()
|
|
2980
|
+
config_exists = config_manager.exists()
|
|
2981
|
+
config = config_manager.config
|
|
2982
|
+
|
|
2983
|
+
args = argparse.Namespace(
|
|
2984
|
+
description=description,
|
|
2985
|
+
platform=config.platform if config_exists else "web",
|
|
2986
|
+
frontend=self._normalize_pipeline_frontend(config.frontend) if config_exists else "react",
|
|
2987
|
+
backend=config.backend if config_exists else "node",
|
|
2988
|
+
domain=config.domain if config_exists else "",
|
|
2989
|
+
name=None,
|
|
2990
|
+
cicd="all",
|
|
2991
|
+
skip_redteam=False,
|
|
2992
|
+
skip_scaffold=False,
|
|
2993
|
+
skip_quality_gate=False,
|
|
2994
|
+
offline=False,
|
|
2995
|
+
quality_threshold=None,
|
|
2996
|
+
)
|
|
2997
|
+
|
|
2998
|
+
self.console.print("[cyan]需求直达模式:自动执行完整流水线[/cyan]")
|
|
2999
|
+
return self._cmd_pipeline(args)
|
|
3000
|
+
|
|
3001
|
+
def _save_tech_stack_to_config(self, project_dir: Path, tech_stack: dict, description: str) -> None:
|
|
3002
|
+
"""保存技术栈到项目配置文件"""
|
|
3003
|
+
import yaml # type: ignore[import-untyped]
|
|
3004
|
+
|
|
3005
|
+
config_file = project_dir / "super-dev.yaml"
|
|
3006
|
+
|
|
3007
|
+
# 读取现有配置(如果有)
|
|
3008
|
+
config: dict[str, Any] = {}
|
|
3009
|
+
if config_file.exists():
|
|
3010
|
+
with open(config_file, encoding='utf-8') as f:
|
|
3011
|
+
config = yaml.safe_load(f) or {}
|
|
3012
|
+
|
|
3013
|
+
# 更新配置
|
|
3014
|
+
config['platform'] = tech_stack.get('platform', 'web')
|
|
3015
|
+
config['frontend'] = tech_stack.get('frontend', 'react')
|
|
3016
|
+
config['backend'] = tech_stack.get('backend', 'node')
|
|
3017
|
+
config['domain'] = tech_stack.get('domain', '')
|
|
3018
|
+
config['description'] = description
|
|
3019
|
+
|
|
3020
|
+
# 保存配置
|
|
3021
|
+
with open(config_file, 'w', encoding='utf-8') as f:
|
|
3022
|
+
yaml.dump(config, f, allow_unicode=True, default_flow_style=False)
|
|
3023
|
+
|
|
3024
|
+
def _export_deploy_remediation_templates(
|
|
3025
|
+
self,
|
|
3026
|
+
project_dir: Path,
|
|
3027
|
+
cicd_platform: str,
|
|
3028
|
+
only_missing: bool = True,
|
|
3029
|
+
) -> dict:
|
|
3030
|
+
"""导出部署修复模板:环境变量示例 + secrets 检查清单。"""
|
|
3031
|
+
env_hints_map = {
|
|
3032
|
+
"github": [
|
|
3033
|
+
{"name": "DOCKER_USERNAME", "description": "Docker 镜像仓库用户名"},
|
|
3034
|
+
{"name": "DOCKER_PASSWORD", "description": "Docker 镜像仓库密码/Token"},
|
|
3035
|
+
{"name": "KUBE_CONFIG_DEV", "description": "开发环境 Kubernetes kubeconfig"},
|
|
3036
|
+
{"name": "KUBE_CONFIG_PROD", "description": "生产环境 Kubernetes kubeconfig"},
|
|
3037
|
+
],
|
|
3038
|
+
"gitlab": [
|
|
3039
|
+
{"name": "CI_REGISTRY_USER", "description": "GitLab Registry 用户名"},
|
|
3040
|
+
{"name": "CI_REGISTRY_PASSWORD", "description": "GitLab Registry 密码/Token"},
|
|
3041
|
+
{"name": "KUBE_CONTEXT_DEV", "description": "开发环境 K8s 上下文"},
|
|
3042
|
+
{"name": "KUBE_CONTEXT_PROD", "description": "生产环境 K8s 上下文"},
|
|
3043
|
+
],
|
|
3044
|
+
"azure": [
|
|
3045
|
+
{"name": "AZURE_ACR_SERVICE_CONNECTION", "description": "Azure ACR 服务连接标识"},
|
|
3046
|
+
{"name": "AZURE_DEV_K8S_CONNECTION", "description": "开发环境 AKS 服务连接标识"},
|
|
3047
|
+
{"name": "AZURE_PROD_K8S_CONNECTION", "description": "生产环境 AKS 服务连接标识"},
|
|
3048
|
+
],
|
|
3049
|
+
"bitbucket": [
|
|
3050
|
+
{"name": "REGISTRY_URL", "description": "镜像仓库地址"},
|
|
3051
|
+
{"name": "KUBE_CONFIG_DEV", "description": "开发环境 Kubernetes kubeconfig"},
|
|
3052
|
+
{"name": "KUBE_CONFIG_PROD", "description": "生产环境 Kubernetes kubeconfig"},
|
|
3053
|
+
],
|
|
3054
|
+
}
|
|
3055
|
+
manual_hints_map = {
|
|
3056
|
+
"jenkins": [
|
|
3057
|
+
"Jenkins Credentials: docker-credentials",
|
|
3058
|
+
"Jenkins Credentials: kubeconfig-dev",
|
|
3059
|
+
"Jenkins Credentials: kubeconfig-prod",
|
|
3060
|
+
]
|
|
3061
|
+
}
|
|
3062
|
+
platform_guidance_map = {
|
|
3063
|
+
"github": [
|
|
3064
|
+
"在 GitHub Settings > Secrets and variables > Actions 配置变量。",
|
|
3065
|
+
"按 dev/prod 环境拆分敏感变量。",
|
|
3066
|
+
],
|
|
3067
|
+
"gitlab": [
|
|
3068
|
+
"在 GitLab Settings > CI/CD > Variables 中配置变量并启用 Masked。",
|
|
3069
|
+
],
|
|
3070
|
+
"jenkins": [
|
|
3071
|
+
"在 Jenkins Credentials 中创建与流水线一致的凭据 ID。",
|
|
3072
|
+
],
|
|
3073
|
+
"azure": [
|
|
3074
|
+
"在 Azure DevOps 配置 Service Connection 和 Variable Group。",
|
|
3075
|
+
],
|
|
3076
|
+
"bitbucket": [
|
|
3077
|
+
"在 Bitbucket Repository variables 中配置密钥。",
|
|
3078
|
+
],
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
def _resolve_env_hints(platform: str) -> list[dict]:
|
|
3082
|
+
if platform == "all":
|
|
3083
|
+
merged = []
|
|
3084
|
+
seen = set()
|
|
3085
|
+
for item_platform in ("github", "gitlab", "azure", "bitbucket"):
|
|
3086
|
+
for item in env_hints_map.get(item_platform, []):
|
|
3087
|
+
if item["name"] in seen:
|
|
3088
|
+
continue
|
|
3089
|
+
seen.add(item["name"])
|
|
3090
|
+
merged.append(item)
|
|
3091
|
+
return merged
|
|
3092
|
+
return list(env_hints_map.get(platform, []))
|
|
3093
|
+
|
|
3094
|
+
def _resolve_manual_hints(platform: str) -> list[str]:
|
|
3095
|
+
if platform == "all":
|
|
3096
|
+
return list(manual_hints_map.get("jenkins", []))
|
|
3097
|
+
return list(manual_hints_map.get(platform, []))
|
|
3098
|
+
|
|
3099
|
+
def _resolve_guidance(platform: str) -> list[str]:
|
|
3100
|
+
if platform == "all":
|
|
3101
|
+
merged = []
|
|
3102
|
+
seen = set()
|
|
3103
|
+
for item_platform in ("github", "gitlab", "jenkins", "azure", "bitbucket"):
|
|
3104
|
+
for item in platform_guidance_map.get(item_platform, []):
|
|
3105
|
+
if item in seen:
|
|
3106
|
+
continue
|
|
3107
|
+
seen.add(item)
|
|
3108
|
+
merged.append(item)
|
|
3109
|
+
return merged
|
|
3110
|
+
return list(platform_guidance_map.get(platform, []))
|
|
3111
|
+
|
|
3112
|
+
def _collect_items(platform: str) -> list[dict]:
|
|
3113
|
+
env_hints = _resolve_env_hints(platform)
|
|
3114
|
+
items = []
|
|
3115
|
+
for item in env_hints:
|
|
3116
|
+
name = item["name"]
|
|
3117
|
+
present = bool(os.getenv(name, "").strip())
|
|
3118
|
+
if only_missing and present:
|
|
3119
|
+
continue
|
|
3120
|
+
items.append(
|
|
3121
|
+
{
|
|
3122
|
+
"name": name,
|
|
3123
|
+
"description": item["description"],
|
|
3124
|
+
"present": present,
|
|
3125
|
+
"template": f'{name}="<value>"',
|
|
3126
|
+
}
|
|
3127
|
+
)
|
|
3128
|
+
return items
|
|
3129
|
+
|
|
3130
|
+
def _write_env_example(file_path: Path, platform: str, items: list[dict]) -> None:
|
|
3131
|
+
lines = [
|
|
3132
|
+
"# Super Dev Deployment Environment Template",
|
|
3133
|
+
f"# Platform: {platform}",
|
|
3134
|
+
f"# only_missing: {str(only_missing).lower()}",
|
|
3135
|
+
"",
|
|
3136
|
+
]
|
|
3137
|
+
if not items:
|
|
3138
|
+
lines.append("# No variables to export for current filter.")
|
|
3139
|
+
else:
|
|
3140
|
+
for item in items:
|
|
3141
|
+
lines.append(f"# {item['description']}")
|
|
3142
|
+
lines.append(item["template"])
|
|
3143
|
+
lines.append("")
|
|
3144
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3145
|
+
file_path.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
|
|
3146
|
+
|
|
3147
|
+
def _write_checklist(
|
|
3148
|
+
file_path: Path,
|
|
3149
|
+
platform: str,
|
|
3150
|
+
items: list[dict],
|
|
3151
|
+
manual_hints: list[str],
|
|
3152
|
+
platform_guidance: list[str],
|
|
3153
|
+
) -> None:
|
|
3154
|
+
lines = [
|
|
3155
|
+
"# Deploy Remediation Checklist",
|
|
3156
|
+
"",
|
|
3157
|
+
f"- Platform: `{platform}`",
|
|
3158
|
+
f"- only_missing: `{str(only_missing).lower()}`",
|
|
3159
|
+
"",
|
|
3160
|
+
"## Environment Variables",
|
|
3161
|
+
"",
|
|
3162
|
+
"| Name | Status | Description | Template |",
|
|
3163
|
+
"|:---|:---:|:---|:---|",
|
|
3164
|
+
]
|
|
3165
|
+
if items:
|
|
3166
|
+
for item in items:
|
|
3167
|
+
status = "present" if item["present"] else "missing"
|
|
3168
|
+
lines.append(
|
|
3169
|
+
f"| `{item['name']}` | `{status}` | {item['description']} | `{item['template']}` |"
|
|
3170
|
+
)
|
|
3171
|
+
else:
|
|
3172
|
+
lines.append("| - | - | No variables in current filter | - |")
|
|
3173
|
+
|
|
3174
|
+
lines.extend(["", "## Platform Guidance", ""])
|
|
3175
|
+
if platform_guidance:
|
|
3176
|
+
lines.extend([f"- {line}" for line in platform_guidance])
|
|
3177
|
+
else:
|
|
3178
|
+
lines.append("- No guidance available.")
|
|
3179
|
+
|
|
3180
|
+
lines.extend(["", "## Manual Requirements", ""])
|
|
3181
|
+
if manual_hints:
|
|
3182
|
+
lines.extend([f"- {line}" for line in manual_hints])
|
|
3183
|
+
else:
|
|
3184
|
+
lines.append("- No manual requirements.")
|
|
3185
|
+
|
|
3186
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3187
|
+
file_path.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
|
|
3188
|
+
|
|
3189
|
+
output_dir = project_dir / "output" / "deploy"
|
|
3190
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
3191
|
+
|
|
3192
|
+
aggregate_items = _collect_items(cicd_platform)
|
|
3193
|
+
env_path = project_dir / ".env.deploy.example"
|
|
3194
|
+
checklist_path = output_dir / f"{cicd_platform}-secrets-checklist.md"
|
|
3195
|
+
_write_env_example(env_path, cicd_platform, aggregate_items)
|
|
3196
|
+
_write_checklist(
|
|
3197
|
+
checklist_path,
|
|
3198
|
+
cicd_platform,
|
|
3199
|
+
aggregate_items,
|
|
3200
|
+
_resolve_manual_hints(cicd_platform),
|
|
3201
|
+
_resolve_guidance(cicd_platform),
|
|
3202
|
+
)
|
|
3203
|
+
|
|
3204
|
+
per_platform_files = []
|
|
3205
|
+
if cicd_platform == "all":
|
|
3206
|
+
platform_dir = output_dir / "platforms"
|
|
3207
|
+
for platform in ("github", "gitlab", "jenkins", "azure", "bitbucket"):
|
|
3208
|
+
platform_items = _collect_items(platform)
|
|
3209
|
+
platform_env = platform_dir / f".env.deploy.{platform}.example"
|
|
3210
|
+
platform_checklist = platform_dir / f"{platform}-secrets-checklist.md"
|
|
3211
|
+
_write_env_example(platform_env, platform, platform_items)
|
|
3212
|
+
_write_checklist(
|
|
3213
|
+
platform_checklist,
|
|
3214
|
+
platform,
|
|
3215
|
+
platform_items,
|
|
3216
|
+
_resolve_manual_hints(platform),
|
|
3217
|
+
_resolve_guidance(platform),
|
|
3218
|
+
)
|
|
3219
|
+
per_platform_files.append(
|
|
3220
|
+
{
|
|
3221
|
+
"platform": platform,
|
|
3222
|
+
"env_file": str(platform_env),
|
|
3223
|
+
"checklist_file": str(platform_checklist),
|
|
3224
|
+
"items_count": len(platform_items),
|
|
3225
|
+
}
|
|
3226
|
+
)
|
|
3227
|
+
|
|
3228
|
+
return {
|
|
3229
|
+
"env_file": str(env_path),
|
|
3230
|
+
"checklist_file": str(checklist_path),
|
|
3231
|
+
"items_count": len(aggregate_items),
|
|
3232
|
+
"per_platform_files": per_platform_files,
|
|
3233
|
+
}
|
|
3234
|
+
|
|
3235
|
+
def _print_banner(self) -> None:
|
|
3236
|
+
"""打印欢迎横幅"""
|
|
3237
|
+
if self.console:
|
|
3238
|
+
banner = Text()
|
|
3239
|
+
banner.append("Super Dev ", style="bold cyan")
|
|
3240
|
+
banner.append(f"v{__version__}\n", style="dim")
|
|
3241
|
+
banner.append(__description__, style="white")
|
|
3242
|
+
|
|
3243
|
+
self.console.print(Panel.fit(
|
|
3244
|
+
banner,
|
|
3245
|
+
title="Super Dev",
|
|
3246
|
+
border_style="cyan"
|
|
3247
|
+
))
|
|
3248
|
+
|
|
3249
|
+
|
|
3250
|
+
def main() -> int:
|
|
3251
|
+
"""主入口"""
|
|
3252
|
+
cli = SuperDevCLI()
|
|
3253
|
+
return cli.run()
|
|
3254
|
+
|
|
3255
|
+
|
|
3256
|
+
if __name__ == "__main__":
|
|
3257
|
+
sys.exit(main())
|