adspecs 0.1.19 → 0.1.21

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.
Files changed (74) hide show
  1. package/.adspecs/feature.json +16 -16
  2. package/.adspecs/feature.yml +28 -28
  3. package/.adspecs/paths.json +17 -17
  4. package/.adspecs/templates/04-/345/211/215/347/253/257/345/212/237/350/203/275/350/256/276/350/256/241/346/250/241/346/235/277.md +1 -1
  5. package/.adspecs/templates/05-/345/220/216/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +613 -724
  6. package/.adspecs/templates/05b-/345/211/215/347/253/257/344/273/273/345/212/241/346/270/205/345/215/225/346/250/241/346/235/277.md +51 -51
  7. package/.claude-plugin/marketplace.json +23 -23
  8. package/.claude-plugin/plugin.json +18 -18
  9. package/.qoder-plugin/plugin.json +31 -31
  10. package/CLAUDE.md +1 -5
  11. package/INSTALL.md +3 -3
  12. package/README.md +395 -395
  13. package/bin/adspecs.js +129 -129
  14. package/hooks/commit-queue.js +245 -245
  15. package/hooks/hooks.json +63 -63
  16. package/hooks/session-start.js +44 -44
  17. package/hooks/wiki-queue.js +127 -127
  18. package/package.json +61 -61
  19. package/references/ant6-front-standard/index.md +99 -99
  20. package/references/antd-front-demo/public/mockServiceWorker.js +361 -361
  21. package/references/ecp-end-standard/index.md +63 -63
  22. package/references/python-end-standard/01-Python/345/220/216/347/253/257/347/274/226/347/240/201/350/247/204/350/214/203.md +372 -372
  23. package/references/python-end-standard/02-/346/225/260/346/215/256/345/272/223/350/256/276/350/256/241/344/270/216/344/275/277/347/224/250/350/247/204/350/214/203.md +226 -226
  24. package/references/python-end-standard/03-Celery/345/274/202/346/255/245/344/273/273/345/212/241/350/247/204/350/214/203.md +237 -237
  25. package/references/python-end-standard/04-Redis/344/275/277/347/224/250/350/247/204/350/214/203.md +231 -231
  26. package/scripts/postinstall.js +107 -107
  27. package/scripts/sync-version.js +105 -105
  28. package/skills/.claude/.wiki-update-queue +26 -26
  29. package/skills/adspecs-constitution/SKILL.md +157 -0
  30. package/skills/adspecs-export-word/SKILL.md +498 -498
  31. package/skills/adspecs-export-word/references/md-to-docx.js +862 -862
  32. package/skills/adspecs-export-word/references/package-lock.json +220 -220
  33. package/skills/adspecs-export-word/references/package.json +10 -10
  34. package/skills/adspecs-front-prototype/SKILL.md +405 -405
  35. package/skills/adspecs-front-spec/SKILL.md +4 -4
  36. package/skills/adspecs-front-tasks/SKILL.md +213 -173
  37. package/skills/adspecs-plan/SKILL.md +59 -69
  38. package/skills/adspecs-prd/SKILL.md +13 -5
  39. package/skills/adspecs-prd-to-demo/SKILL.md +532 -0
  40. package/skills/adspecs-tasks/SKILL.md +175 -204
  41. package/skills/adspecs-update-status/SKILL.md +382 -382
  42. package/skills/adspecs-utest/SKILL.md +107 -116
  43. package/skills/grill-me/SKILL.md +7 -0
  44. package/skills/grill-me/agents/openai.yaml +5 -0
  45. package/skills/playwright-cli/SKILL.md +420 -0
  46. package/skills/playwright-cli/references/element-attributes.md +23 -0
  47. package/skills/playwright-cli/references/playwright-tests.md +39 -0
  48. package/skills/playwright-cli/references/request-mocking.md +87 -0
  49. package/skills/playwright-cli/references/running-code.md +241 -0
  50. package/skills/playwright-cli/references/session-management.md +225 -0
  51. package/skills/playwright-cli/references/storage-state.md +275 -0
  52. package/skills/playwright-cli/references/test-generation.md +433 -0
  53. package/skills/playwright-cli/references/tracing.md +139 -0
  54. package/skills/playwright-cli/references/video-recording.md +143 -0
  55. package/skills/playwright-trace/SKILL.md +171 -0
  56. package/skills/project-init/SKILL.md +93 -22
  57. package/skills/project-init/references/front-demo/.claude/settings.local.json +9 -0
  58. package/skills/wiki-update/SKILL.md +232 -232
  59. package/src/commands/doctor.js +197 -197
  60. package/src/commands/init.js +83 -83
  61. package/src/commands/plugin.js +165 -165
  62. package/src/commands/update.js +87 -87
  63. package/src/lib/area-scanner.js +129 -129
  64. package/src/lib/copier.js +104 -104
  65. package/src/lib/dir-utils.js +161 -133
  66. package/src/lib/json-merge.js +114 -114
  67. package/src/lib/paths-defaults.js +37 -37
  68. package/src/lib/prompts.js +428 -347
  69. package/src/lib/readme-gen.js +143 -143
  70. package/src/lib/report.js +338 -327
  71. package/src/lib/scaffolder.js +551 -518
  72. package/src/lib/short-name.js +36 -36
  73. package/src/utils.js +80 -80
  74. package/references/antd-front-demo/.env +0 -15
package/src/lib/report.js CHANGED
@@ -1,327 +1,338 @@
1
- 'use strict';
2
-
3
- const chalk = require('chalk');
4
- const { areaDisplayName, decisionLabel, decisionIcon } = require('./area-scanner');
5
-
6
- /**
7
- * 报告生成
8
- *
9
- * 提供两种报告:
10
- * 1. 计划摘要(执行前)— 让用户确认即将执行的操作
11
- * 2. 最终详细报告(执行后)— 含表格、目录树、状态说明、后续步骤
12
- */
13
-
14
- // ============================================================
15
- // 模式标签
16
- // ============================================================
17
-
18
- function modeLabel(mode) {
19
- const labels = {
20
- simple: '简易模式',
21
- classic: '经典模式',
22
- full: '完整模式',
23
- demo: '轻量模式',
24
- };
25
- return labels[mode] || mode;
26
- }
27
-
28
- function frontDemoLabel(frontDemo) {
29
- if (frontDemo === 'antd') return 'Ant Design React';
30
- if (frontDemo === 'vue') return 'Vue';
31
- if (frontDemo === 'skip') return '跳过';
32
- return frontDemo;
33
- }
34
-
35
- // ============================================================
36
- // 计划摘要(执行前)
37
- // ============================================================
38
-
39
- /**
40
- * 输出计划摘要
41
- *
42
- * @param {object} info - 项目信息
43
- * @param {object} decisions - 每区域决策
44
- * @param {object} subDecisions - 子目录决策
45
- * @param {object} modeUpgrade - 模式升级检测
46
- * @param {string} targetDir - 目标路径
47
- */
48
- function printPlanSummary(info, decisions, subDecisions, modeUpgrade, targetDir) {
49
- console.log('');
50
- console.log(chalk.bold('📋 初始化计划:'));
51
- console.log('');
52
- console.log(chalk.gray(' 项目信息'));
53
- console.log(` 项目名称: ${chalk.green(info.project_name)}`);
54
- console.log(` 项目短名称: ${chalk.green(info.project_short_name)}`);
55
- console.log(` 公司名称: ${info.company_name}`);
56
- console.log(` 文件密级: ${info.confidentiality}`);
57
- console.log(` 初始化模式: ${chalk.yellow(modeLabel(info.mode))}`);
58
-
59
- if (info.mode === 'classic' || info.mode === 'full') {
60
- console.log(` 前端技术栈: ${frontDemoLabel(info.frontDemo)}`);
61
- }
62
- if (info.mode === 'demo') {
63
- console.log(` 前端技术栈: ${frontDemoLabel('antd')} (固定)`);
64
- }
65
-
66
- console.log(` 目标路径: ${chalk.blue(targetDir)}`);
67
- console.log('');
68
-
69
- // 区域操作预览
70
- console.log(chalk.gray(' 区域操作'));
71
- const areaEntries = Object.entries(decisions);
72
- const maxNameLen = Math.max(...areaEntries.map(([k]) => areaDisplayName(k).length));
73
-
74
- for (const [areaKey, decision] of areaEntries) {
75
- // frontDemo='skip' 覆盖决策显示
76
- let displayDecision = decision;
77
- let displayIcon = decisionIcon(decision);
78
- let displayLabel = decisionLabel(decision);
79
-
80
- if (areaKey === 'frontDemo' && info.frontDemo === 'skip') {
81
- displayIcon = '⏭';
82
- displayLabel = '跳过(用户选择不创建)';
83
- }
84
-
85
- const name = areaDisplayName(areaKey).padEnd(maxNameLen);
86
- console.log(` ${displayIcon} ${name}/ ${chalk.gray('→')} ${displayLabel}`);
87
-
88
- // 子决策预览
89
- if (areaKey === 'docs' && decision === 'skip' && subDecisions.conventions !== 'skip') {
90
- console.log(
91
- ` ${chalk.gray('└')} 40-project_conventions/ ${chalk.gray('→')} ${decisionLabel(subDecisions.conventions)}`
92
- );
93
- }
94
- if (areaKey === 'adspecs' && decision === 'skip' && subDecisions.templates !== 'skip') {
95
- console.log(
96
- ` ${chalk.gray('└')} templates/ ${chalk.gray('→')} ${decisionLabel(subDecisions.templates)}`
97
- );
98
- }
99
- }
100
-
101
- // 模式升级提示
102
- if (modeUpgrade.detected) {
103
- console.log('');
104
- console.log(
105
- chalk.yellow(
106
- ` 📦 模式升级检测:检测到 40-project_conventions/ 为空目录(简易模式产物),将自动补充 frontend-standard/`
107
- )
108
- );
109
- }
110
-
111
- console.log('');
112
- }
113
-
114
- // ============================================================
115
- // 最终详细报告(执行后)
116
- // ============================================================
117
-
118
- /**
119
- * 输出最终详细报告
120
- *
121
- * @param {object} report - scaffolder 返回的执行报告
122
- * @param {object} info - 项目信息
123
- * @param {object} decisions - 每区域决策
124
- * @param {object} subDecisions - 子目录决策
125
- * @param {object} modeUpgrade - 模式升级检测
126
- */
127
- function printFinalReport(report, info, decisions, subDecisions, modeUpgrade) {
128
- console.log('');
129
- console.log(chalk.green.bold('## 项目初始化完成 🎉'));
130
- console.log('');
131
-
132
- // 项目信息段
133
- console.log(chalk.bold('项目名称:'), info.project_name);
134
- console.log(chalk.bold('项目短名称:'), info.project_short_name);
135
- console.log(chalk.bold('公司名称:'), info.company_name);
136
- console.log(chalk.bold('文件密级:'), info.confidentiality);
137
- console.log(chalk.bold('初始化模式:'), modeLabel(info.mode));
138
- if (info.mode === 'classic' || info.mode === 'full' || info.mode === 'demo') {
139
- const fdLabel = info.mode === 'demo' ? frontDemoLabel('antd') : frontDemoLabel(info.frontDemo);
140
- console.log(chalk.bold('前端技术栈:'), fdLabel);
141
- }
142
- console.log(chalk.bold('目标路径:'), report.targetDir);
143
- console.log('');
144
-
145
- // 区域执行结果表格
146
- console.log(chalk.bold('### 区域执行结果'));
147
- console.log('');
148
- console.log(' | 区域 | 状态 | 说明 |');
149
- console.log(' | --- | --- | --- |');
150
-
151
- for (const stage of report.stages) {
152
- const icon = statusIcon(stage.status);
153
- const detail = stageDetail(stage);
154
- console.log(` | ${stage.name} | ${icon} ${stage.status} | ${detail} |`);
155
-
156
- // 子行:templates/
157
- if (stage.name === '.adspecs/' && stage.subDecisions) {
158
- if (stage.subDecisions.templates === 'update') {
159
- console.log(` | ${chalk.gray('└ templates/')} | ${chalk.green('🔄')} 已更新 | 从插件根目录覆盖更新 |`);
160
- }
161
- }
162
-
163
- // 子行:feature.json
164
- if (stage.name === '.adspecs/' && stage.featureResult) {
165
- if (stage.featureResult.addedKeys && stage.featureResult.addedKeys.length > 0) {
166
- console.log(
167
- ` | ${chalk.gray('└ feature.json')} | ${chalk.yellow('🔧')} 已补全 | 补充缺失属性 [${stage.featureResult.addedKeys.join(', ')}] |`
168
- );
169
- }
170
- }
171
-
172
- // 子行:40-project_conventions/
173
- if (stage.name === 'docs/' && stage.subDecisions) {
174
- if (stage.subDecisions.conventions === 'update') {
175
- console.log(
176
- ` | ${chalk.gray('└ 40-project_conventions/')} | ${chalk.green('🔄')} 已更新 | 按前端技术栈覆盖更新 |`
177
- );
178
- }
179
- if (stage.modeUpgrade && stage.modeUpgrade.detected) {
180
- console.log(
181
- ` | ${chalk.gray('└ 40-project_conventions/')} | ${chalk.yellow('📦')} 已补充 | 模式升级自动补充 frontend-standard/ |`
182
- );
183
- }
184
- }
185
- }
186
-
187
- console.log('');
188
-
189
- // 状态说明
190
- console.log(chalk.bold('状态说明:'));
191
- console.log(' ✅ 已创建 — 目录原本不存在,新建完成');
192
- console.log(' 🔄 已重建 目录原本存在,按用户选择删除后重新创建');
193
- console.log(' 🔄 已更新 — 父目录跳过,但子目录按用户选择使用最新文件覆盖');
194
- console.log(' 🔧 已补全 — 已有文件属性与 references 对比后,补充缺失属性');
195
- console.log(' ⏭ 已跳过 — 目录原本存在,按用户选择保留未动');
196
- console.log('');
197
-
198
- // 目录树总览
199
- console.log(chalk.bold('### 目录树总览'));
200
- console.log('');
201
- printDirectoryTree(info.mode, info.frontDemo);
202
-
203
- // 后续步骤
204
- console.log(chalk.bold('### 后续步骤'));
205
- console.log('');
206
- printNextSteps(info.mode, report.targetDir, info.frontDemo);
207
- }
208
-
209
- /**
210
- * 状态图标
211
- */
212
- function statusIcon(status) {
213
- const icons = {
214
- created: '✅',
215
- recreated: '🔄',
216
- updated: '🔄',
217
- skipped: '⏭',
218
- exists: '⏭',
219
- };
220
- return icons[status] || '❓';
221
- }
222
-
223
- /**
224
- * 阶段详情
225
- */
226
- function stageDetail(stage) {
227
- if (stage.fileCount !== undefined) {
228
- return `${stage.fileCount} 个文件`;
229
- }
230
- if (stage.reason) {
231
- return stage.reason;
232
- }
233
- if (stage.detail && stage.detail.created) {
234
- return '已创建';
235
- }
236
- return '';
237
- }
238
-
239
- /**
240
- * 按模式输出 ASCII 目录树
241
- */
242
- function printDirectoryTree(mode, frontDemo) {
243
- const lines = [];
244
- lines.push(' {targetDir}/');
245
- lines.push(' ├── .adspecs/ (插件配置、脚本、模板 + project.json)');
246
- lines.push(' ├── docs/ (11 个子目录)');
247
- if (mode === 'classic' || mode === 'full' || mode === 'demo') {
248
- lines.push(' │ └── 40-project_conventions/');
249
- if (mode === 'demo') {
250
- lines.push(' │ ├── frontend-standard/ (Ant Design React 前端规范)');
251
- lines.push(' │ └── end-standard/ (Python 后端规范)');
252
- } else {
253
- const fdLabel = frontDemo === 'antd' ? 'Ant Design React' : 'Vue';
254
- lines.push(` │ └── frontend-standard/ (${fdLabel} 前端规范)`);
255
- }
256
- }
257
- lines.push(' ├── wiki/ (基线文件目录)');
258
-
259
- if (mode === 'full' || mode === 'demo') {
260
- lines.push(' ├── web-ui/ (前端生产项目)');
261
- lines.push(' ├── service/ (后端服务模块)');
262
- }
263
- if (mode === 'classic' || mode === 'full' || mode === 'demo') {
264
- if (frontDemo !== 'skip') {
265
- const fdLabel = frontDemo === 'antd' ? 'Ant Design React' : 'Vue';
266
- lines.push(` └── front-demo/ (${fdLabel} 前端演示项目)`);
267
- }
268
- }
269
-
270
- if (mode === 'demo') {
271
- lines.push('');
272
- lines.push(' 技术栈:Python + SQLite + React + Ant Design 6 全栈 demo');
273
- }
274
-
275
- console.log(lines.join('\n'));
276
- console.log('');
277
- }
278
-
279
- /**
280
- * 按模式输出后续步骤
281
- */
282
- function printNextSteps(mode, targetDir, frontDemo = 'vue') {
283
- if (mode === 'simple') {
284
- console.log(' 1. 执行 `git init` 初始化 Git 仓库');
285
- console.log(' 2. 根据需要调整 .adspecs/templates/ 下的文档模板');
286
- console.log(' 3. 根据需要编辑 .adspecs/project.json 补充公司 Logo 路径等信息');
287
- console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
288
- } else if (mode === 'classic') {
289
- console.log(' 1. 执行 `git init` 初始化 Git 仓库');
290
- if (frontDemo !== 'skip') {
291
- console.log(' 2. 在前端演示目录执行 `npm install` 安装依赖:');
292
- console.log(` cd ${targetDir}/front-demo && npm install`);
293
- console.log(' 3. 根据需要调整文档模板和编码规范');
294
- console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
295
- } else {
296
- console.log(' 2. 根据需要调整文档模板和编码规范');
297
- console.log(' 3. 执行 `adspecs plugin install` 注册 Claude Code 插件');
298
- }
299
- } else if (mode === 'full') {
300
- console.log(' 1. 执行 `git init` 初始化 Git 仓库');
301
- console.log(' 2. 安装前端依赖:');
302
- console.log(` cd ${targetDir}/web-ui && npm install`);
303
- if (frontDemo !== 'skip') {
304
- console.log(` cd ${targetDir}/front-demo && npm install`);
305
- }
306
- console.log(' 3. 根据需要调整文档模板和编码规范');
307
- console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
308
- } else if (mode === 'demo') {
309
- console.log(' 1. 执行 `git init` 初始化 Git 仓库');
310
- console.log(' 2. 安装前端依赖:');
311
- console.log(` cd ${targetDir}/web-ui && npm install`);
312
- console.log(` cd ${targetDir}/front-demo && npm install`);
313
- console.log(' 3. 安装后端依赖(Python 环境):');
314
- console.log(` cd ${targetDir}/service`);
315
- console.log(' python -m venv venv && source venv/bin/activate # Windows: venv\\Scripts\\activate');
316
- console.log(' pip install -r requirements.txt');
317
- console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
318
- }
319
- console.log('');
320
- }
321
-
322
- module.exports = {
323
- printPlanSummary,
324
- printFinalReport,
325
- modeLabel,
326
- frontDemoLabel,
327
- };
1
+ 'use strict';
2
+
3
+ const chalk = require('chalk');
4
+ const { areaDisplayName, decisionLabel, decisionIcon } = require('./area-scanner');
5
+
6
+ /**
7
+ * 报告生成
8
+ *
9
+ * 提供两种报告:
10
+ * 1. 计划摘要(执行前)— 让用户确认即将执行的操作
11
+ * 2. 最终详细报告(执行后)— 含表格、目录树、状态说明、后续步骤
12
+ */
13
+
14
+ // ============================================================
15
+ // 模式标签
16
+ // ============================================================
17
+
18
+ function modeLabel(mode) {
19
+ const labels = {
20
+ simple: '简易模式',
21
+ classic: '经典模式',
22
+ full: '完整模式',
23
+ demo: '轻量模式',
24
+ };
25
+ return labels[mode] || mode;
26
+ }
27
+
28
+ function frontDemoLabel(frontDemo) {
29
+ if (frontDemo === 'antd') return 'Ant Design React';
30
+ if (frontDemo === 'vue') return 'Vue';
31
+ if (frontDemo === 'skip') return '跳过';
32
+ return frontDemo;
33
+ }
34
+
35
+ // ============================================================
36
+ // 计划摘要(执行前)
37
+ // ============================================================
38
+
39
+ /**
40
+ * 输出计划摘要
41
+ *
42
+ * @param {object} info - 项目信息
43
+ * @param {object} decisions - 每区域决策
44
+ * @param {object} subDecisions - 子目录决策
45
+ * @param {object} modeUpgrade - 模式升级检测
46
+ * @param {string} targetDir - 目标路径
47
+ */
48
+ function printPlanSummary(info, decisions, subDecisions, modeUpgrade, targetDir) {
49
+ console.log('');
50
+ console.log(chalk.bold('📋 初始化计划:'));
51
+ console.log('');
52
+ console.log(chalk.gray(' 项目信息'));
53
+ console.log(` 项目名称: ${chalk.green(info.project_name)}`);
54
+ console.log(` 项目短名称: ${chalk.green(info.project_short_name)}`);
55
+ console.log(` 公司名称: ${info.company_name}`);
56
+ console.log(` 文件密级: ${info.confidentiality}`);
57
+ console.log(` 初始化模式: ${chalk.yellow(modeLabel(info.mode))}`);
58
+
59
+ if (info.mode === 'classic' || info.mode === 'full') {
60
+ console.log(` 前端技术栈: ${frontDemoLabel(info.frontDemo)}`);
61
+ }
62
+ if (info.mode === 'demo') {
63
+ console.log(` 前端技术栈: ${frontDemoLabel('antd')} (固定)`);
64
+ }
65
+
66
+ console.log(` 目标路径: ${chalk.blue(targetDir)}`);
67
+ console.log('');
68
+
69
+ // 区域操作预览
70
+ console.log(chalk.gray(' 区域操作'));
71
+ const areaEntries = Object.entries(decisions);
72
+ const maxNameLen = Math.max(...areaEntries.map(([k]) => areaDisplayName(k).length));
73
+
74
+ for (const [areaKey, decision] of areaEntries) {
75
+ // frontDemo='skip' 覆盖决策显示
76
+ let displayDecision = decision;
77
+ let displayIcon = decisionIcon(decision);
78
+ let displayLabel = decisionLabel(decision);
79
+
80
+ if (areaKey === 'frontDemo' && info.frontDemo === 'skip') {
81
+ displayIcon = '⏭';
82
+ displayLabel = '跳过(用户选择不创建)';
83
+ }
84
+
85
+ const name = areaDisplayName(areaKey).padEnd(maxNameLen);
86
+ console.log(` ${displayIcon} ${name}/ ${chalk.gray('→')} ${displayLabel}`);
87
+
88
+ // 子决策预览
89
+ if (areaKey === 'docs' && decision === 'skip' && subDecisions.conventions !== 'skip') {
90
+ console.log(
91
+ ` ${chalk.gray('└')} 40-project_conventions/ ${chalk.gray('→')} ${decisionLabel(subDecisions.conventions)}`
92
+ );
93
+ }
94
+ if (areaKey === 'adspecs' && decision === 'skip') {
95
+ const tmplMap = subDecisions.templates || {};
96
+ const tmplEntries = Object.entries(tmplMap);
97
+ const replaceCount = tmplEntries.filter(([, v]) => v === 'replace').length;
98
+ const keepCount = tmplEntries.filter(([, v]) => v === 'keep').length;
99
+ if (replaceCount > 0 || keepCount > 0) {
100
+ console.log(
101
+ ` ${chalk.gray('└')} templates/ ${chalk.gray('→')} 逐文件确认(${replaceCount} 个替换,${keepCount} 个保留)`
102
+ );
103
+ }
104
+ }
105
+ }
106
+
107
+ // 模式升级提示
108
+ if (modeUpgrade.detected) {
109
+ console.log('');
110
+ console.log(
111
+ chalk.yellow(
112
+ ` 📦 模式升级检测:检测到 40-project_conventions/ 为空目录(简易模式产物),将自动补充 frontend-standard/`
113
+ )
114
+ );
115
+ }
116
+
117
+ console.log('');
118
+ }
119
+
120
+ // ============================================================
121
+ // 最终详细报告(执行后)
122
+ // ============================================================
123
+
124
+ /**
125
+ * 输出最终详细报告
126
+ *
127
+ * @param {object} report - scaffolder 返回的执行报告
128
+ * @param {object} info - 项目信息
129
+ * @param {object} decisions - 每区域决策
130
+ * @param {object} subDecisions - 子目录决策
131
+ * @param {object} modeUpgrade - 模式升级检测
132
+ */
133
+ function printFinalReport(report, info, decisions, subDecisions, modeUpgrade) {
134
+ console.log('');
135
+ console.log(chalk.green.bold('## 项目初始化完成 🎉'));
136
+ console.log('');
137
+
138
+ // 项目信息段
139
+ console.log(chalk.bold('项目名称:'), info.project_name);
140
+ console.log(chalk.bold('项目短名称:'), info.project_short_name);
141
+ console.log(chalk.bold('公司名称:'), info.company_name);
142
+ console.log(chalk.bold('文件密级:'), info.confidentiality);
143
+ console.log(chalk.bold('初始化模式:'), modeLabel(info.mode));
144
+ if (info.mode === 'classic' || info.mode === 'full' || info.mode === 'demo') {
145
+ const fdLabel = info.mode === 'demo' ? frontDemoLabel('antd') : frontDemoLabel(info.frontDemo);
146
+ console.log(chalk.bold('前端技术栈:'), fdLabel);
147
+ }
148
+ console.log(chalk.bold('目标路径:'), report.targetDir);
149
+ console.log('');
150
+
151
+ // 区域执行结果表格
152
+ console.log(chalk.bold('### 区域执行结果'));
153
+ console.log('');
154
+ console.log(' | 区域 | 状态 | 说明 |');
155
+ console.log(' | --- | --- | --- |');
156
+
157
+ for (const stage of report.stages) {
158
+ const icon = statusIcon(stage.status);
159
+ const detail = stageDetail(stage);
160
+ console.log(` | ${stage.name} | ${icon} ${stage.status} | ${detail} |`);
161
+
162
+ // 子行:templates/(逐文件确认结果)
163
+ if (stage.name === '.adspecs/' && stage.subDecisions && stage.subDecisions.templates) {
164
+ const tmpl = stage.subDecisions.templates;
165
+ if (typeof tmpl === 'object' && (tmpl.replaced > 0 || tmpl.kept > 0 || tmpl.added > 0)) {
166
+ const parts = [];
167
+ if (tmpl.added > 0) parts.push(`${tmpl.added} 个新增`);
168
+ if (tmpl.replaced > 0) parts.push(`${tmpl.replaced} 个替换`);
169
+ if (tmpl.kept > 0) parts.push(`${tmpl.kept} 个保留`);
170
+ console.log(` | ${chalk.gray('└ templates/')} | ${chalk.green('🔄')} 部分更新 | ${parts.join(',')} |`);
171
+ }
172
+ }
173
+
174
+ // 子行:feature.json
175
+ if (stage.name === '.adspecs/' && stage.featureResult) {
176
+ if (stage.featureResult.addedKeys && stage.featureResult.addedKeys.length > 0) {
177
+ console.log(
178
+ ` | ${chalk.gray('└ feature.json')} | ${chalk.yellow('🔧')} 已补全 | 补充缺失属性 [${stage.featureResult.addedKeys.join(', ')}] |`
179
+ );
180
+ }
181
+ }
182
+
183
+ // 子行:40-project_conventions/
184
+ if (stage.name === 'docs/' && stage.subDecisions) {
185
+ if (stage.subDecisions.conventions === 'update') {
186
+ console.log(
187
+ ` | ${chalk.gray('└ 40-project_conventions/')} | ${chalk.green('🔄')} 已更新 | 按前端技术栈覆盖更新 |`
188
+ );
189
+ }
190
+ if (stage.modeUpgrade && stage.modeUpgrade.detected) {
191
+ console.log(
192
+ ` | ${chalk.gray(' 40-project_conventions/')} | ${chalk.yellow('📦')} 已补充 | 模式升级自动补充 frontend-standard/ |`
193
+ );
194
+ }
195
+ }
196
+ }
197
+
198
+ console.log('');
199
+
200
+ // 状态说明
201
+ console.log(chalk.bold('状态说明:'));
202
+ console.log(' ✅ 已创建 — 目录原本不存在,新建完成');
203
+ console.log(' 🔄 已重建 — 目录原本存在,按用户选择删除后重新创建');
204
+ console.log(' 🔄 已更新 — 父目录跳过,但子目录按用户选择使用最新文件覆盖(templates/ 支持逐文件确认,部分替换部分保留)');
205
+ console.log(' 🔧 已补全 — 已有文件属性与 references 对比后,补充缺失属性');
206
+ console.log(' ⏭ 已跳过 — 目录原本存在,按用户选择保留未动');
207
+ console.log('');
208
+
209
+ // 目录树总览
210
+ console.log(chalk.bold('### 目录树总览'));
211
+ console.log('');
212
+ printDirectoryTree(info.mode, info.frontDemo);
213
+
214
+ // 后续步骤
215
+ console.log(chalk.bold('### 后续步骤'));
216
+ console.log('');
217
+ printNextSteps(info.mode, report.targetDir, info.frontDemo);
218
+ }
219
+
220
+ /**
221
+ * 状态图标
222
+ */
223
+ function statusIcon(status) {
224
+ const icons = {
225
+ created: '✅',
226
+ recreated: '🔄',
227
+ updated: '🔄',
228
+ skipped: '⏭',
229
+ exists: '⏭',
230
+ };
231
+ return icons[status] || '❓';
232
+ }
233
+
234
+ /**
235
+ * 阶段详情
236
+ */
237
+ function stageDetail(stage) {
238
+ if (stage.fileCount !== undefined) {
239
+ return `${stage.fileCount} 个文件`;
240
+ }
241
+ if (stage.reason) {
242
+ return stage.reason;
243
+ }
244
+ if (stage.detail && stage.detail.created) {
245
+ return '已创建';
246
+ }
247
+ return '';
248
+ }
249
+
250
+ /**
251
+ * 按模式输出 ASCII 目录树
252
+ */
253
+ function printDirectoryTree(mode, frontDemo) {
254
+ const lines = [];
255
+ lines.push(' {targetDir}/');
256
+ lines.push(' ├── .adspecs/ (插件配置、脚本、模板 + project.json)');
257
+ lines.push(' ├── docs/ (11 个子目录)');
258
+ if (mode === 'classic' || mode === 'full' || mode === 'demo') {
259
+ lines.push(' │ └── 40-project_conventions/');
260
+ if (mode === 'demo') {
261
+ lines.push(' ├── frontend-standard/ (Ant Design React 前端规范)');
262
+ lines.push(' │ └── end-standard/ (Python 后端规范)');
263
+ } else {
264
+ const fdLabel = frontDemo === 'antd' ? 'Ant Design React' : 'Vue';
265
+ lines.push(` │ └── frontend-standard/ (${fdLabel} 前端规范)`);
266
+ }
267
+ }
268
+ lines.push(' ├── wiki/ (基线文件目录)');
269
+
270
+ if (mode === 'full' || mode === 'demo') {
271
+ lines.push(' ├── web-ui/ (前端生产项目)');
272
+ lines.push(' ├── service/ (后端服务模块)');
273
+ }
274
+ if (mode === 'classic' || mode === 'full' || mode === 'demo') {
275
+ if (frontDemo !== 'skip') {
276
+ const fdLabel = frontDemo === 'antd' ? 'Ant Design React' : 'Vue';
277
+ lines.push(` └── front-demo/ (${fdLabel} 前端演示项目)`);
278
+ }
279
+ }
280
+
281
+ if (mode === 'demo') {
282
+ lines.push('');
283
+ lines.push(' 技术栈:Python + SQLite + React + Ant Design 6 全栈 demo');
284
+ }
285
+
286
+ console.log(lines.join('\n'));
287
+ console.log('');
288
+ }
289
+
290
+ /**
291
+ * 按模式输出后续步骤
292
+ */
293
+ function printNextSteps(mode, targetDir, frontDemo = 'vue') {
294
+ if (mode === 'simple') {
295
+ console.log(' 1. 执行 `git init` 初始化 Git 仓库');
296
+ console.log(' 2. 根据需要调整 .adspecs/templates/ 下的文档模板');
297
+ console.log(' 3. 根据需要编辑 .adspecs/project.json 补充公司 Logo 路径等信息');
298
+ console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
299
+ } else if (mode === 'classic') {
300
+ console.log(' 1. 执行 `git init` 初始化 Git 仓库');
301
+ if (frontDemo !== 'skip') {
302
+ console.log(' 2. 在前端演示目录执行 `npm install` 安装依赖:');
303
+ console.log(` cd ${targetDir}/front-demo && npm install`);
304
+ console.log(' 3. 根据需要调整文档模板和编码规范');
305
+ console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
306
+ } else {
307
+ console.log(' 2. 根据需要调整文档模板和编码规范');
308
+ console.log(' 3. 执行 `adspecs plugin install` 注册 Claude Code 插件');
309
+ }
310
+ } else if (mode === 'full') {
311
+ console.log(' 1. 执行 `git init` 初始化 Git 仓库');
312
+ console.log(' 2. 安装前端依赖:');
313
+ console.log(` cd ${targetDir}/web-ui && npm install`);
314
+ if (frontDemo !== 'skip') {
315
+ console.log(` cd ${targetDir}/front-demo && npm install`);
316
+ }
317
+ console.log(' 3. 根据需要调整文档模板和编码规范');
318
+ console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
319
+ } else if (mode === 'demo') {
320
+ console.log(' 1. 执行 `git init` 初始化 Git 仓库');
321
+ console.log(' 2. 安装前端依赖:');
322
+ console.log(` cd ${targetDir}/web-ui && npm install`);
323
+ console.log(` cd ${targetDir}/front-demo && npm install`);
324
+ console.log(' 3. 安装后端依赖(Python 环境):');
325
+ console.log(` cd ${targetDir}/service`);
326
+ console.log(' python -m venv venv && source venv/bin/activate # Windows: venv\\Scripts\\activate');
327
+ console.log(' pip install -r requirements.txt');
328
+ console.log(' 4. 执行 `adspecs plugin install` 注册 Claude Code 插件');
329
+ }
330
+ console.log('');
331
+ }
332
+
333
+ module.exports = {
334
+ printPlanSummary,
335
+ printFinalReport,
336
+ modeLabel,
337
+ frontDemoLabel,
338
+ };