speccore 5.4.3 → 5.5.0

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 (117) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +4 -4
  3. package/dist/cli.js +19 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/audit.d.ts +10 -0
  6. package/dist/commands/audit.d.ts.map +1 -0
  7. package/dist/commands/audit.js +278 -0
  8. package/dist/commands/audit.js.map +1 -0
  9. package/dist/commands/baseline.d.ts +13 -0
  10. package/dist/commands/baseline.d.ts.map +1 -0
  11. package/dist/commands/baseline.js +227 -0
  12. package/dist/commands/baseline.js.map +1 -0
  13. package/dist/commands/bugfix.d.ts +13 -0
  14. package/dist/commands/bugfix.d.ts.map +1 -0
  15. package/dist/commands/bugfix.js +181 -0
  16. package/dist/commands/bugfix.js.map +1 -0
  17. package/dist/commands/change.d.ts +14 -0
  18. package/dist/commands/change.d.ts.map +1 -0
  19. package/dist/commands/change.js +208 -0
  20. package/dist/commands/change.js.map +1 -0
  21. package/dist/commands/context.d.ts +8 -0
  22. package/dist/commands/context.d.ts.map +1 -0
  23. package/dist/commands/context.js +180 -0
  24. package/dist/commands/context.js.map +1 -0
  25. package/dist/commands/dashboard.d.ts +9 -0
  26. package/dist/commands/dashboard.d.ts.map +1 -0
  27. package/dist/commands/dashboard.js +209 -0
  28. package/dist/commands/dashboard.js.map +1 -0
  29. package/dist/commands/delete.d.ts +16 -0
  30. package/dist/commands/delete.d.ts.map +1 -0
  31. package/dist/commands/delete.js +177 -0
  32. package/dist/commands/delete.js.map +1 -0
  33. package/dist/commands/demo.d.ts +10 -0
  34. package/dist/commands/demo.d.ts.map +1 -0
  35. package/dist/commands/demo.js +151 -0
  36. package/dist/commands/demo.js.map +1 -0
  37. package/dist/commands/global-status.d.ts +9 -0
  38. package/dist/commands/global-status.d.ts.map +1 -0
  39. package/dist/commands/global-status.js +135 -0
  40. package/dist/commands/global-status.js.map +1 -0
  41. package/dist/commands/goal.d.ts +15 -0
  42. package/dist/commands/goal.d.ts.map +1 -0
  43. package/dist/commands/goal.js +232 -0
  44. package/dist/commands/goal.js.map +1 -0
  45. package/dist/commands/handover.d.ts +11 -0
  46. package/dist/commands/handover.d.ts.map +1 -0
  47. package/dist/commands/handover.js +169 -0
  48. package/dist/commands/handover.js.map +1 -0
  49. package/dist/commands/help.d.ts +10 -0
  50. package/dist/commands/help.d.ts.map +1 -0
  51. package/dist/commands/help.js +93 -0
  52. package/dist/commands/help.js.map +1 -0
  53. package/dist/commands/history.d.ts +9 -0
  54. package/dist/commands/history.d.ts.map +1 -0
  55. package/dist/commands/history.js +87 -0
  56. package/dist/commands/history.js.map +1 -0
  57. package/dist/commands/impact.d.ts +12 -0
  58. package/dist/commands/impact.d.ts.map +1 -0
  59. package/dist/commands/impact.js +238 -0
  60. package/dist/commands/impact.js.map +1 -0
  61. package/dist/commands/index-update.d.ts +8 -0
  62. package/dist/commands/index-update.d.ts.map +1 -0
  63. package/dist/commands/index-update.js +180 -0
  64. package/dist/commands/index-update.js.map +1 -0
  65. package/dist/commands/iteration-from-global.d.ts +11 -0
  66. package/dist/commands/iteration-from-global.d.ts.map +1 -0
  67. package/dist/commands/iteration-from-global.js +294 -0
  68. package/dist/commands/iteration-from-global.js.map +1 -0
  69. package/dist/commands/new-task.d.ts +15 -0
  70. package/dist/commands/new-task.d.ts.map +1 -0
  71. package/dist/commands/new-task.js +208 -0
  72. package/dist/commands/new-task.js.map +1 -0
  73. package/dist/commands/platform-add.d.ts +12 -0
  74. package/dist/commands/platform-add.d.ts.map +1 -0
  75. package/dist/commands/platform-add.js +111 -0
  76. package/dist/commands/platform-add.js.map +1 -0
  77. package/dist/commands/rename.d.ts +14 -0
  78. package/dist/commands/rename.d.ts.map +1 -0
  79. package/dist/commands/rename.js +298 -0
  80. package/dist/commands/rename.js.map +1 -0
  81. package/dist/commands/research.d.ts +14 -0
  82. package/dist/commands/research.d.ts.map +1 -0
  83. package/dist/commands/research.js +181 -0
  84. package/dist/commands/research.js.map +1 -0
  85. package/dist/commands/retro.d.ts +10 -0
  86. package/dist/commands/retro.d.ts.map +1 -0
  87. package/dist/commands/retro.js +130 -0
  88. package/dist/commands/retro.js.map +1 -0
  89. package/dist/commands/spec.d.ts +8 -0
  90. package/dist/commands/spec.d.ts.map +1 -0
  91. package/dist/commands/spec.js +155 -0
  92. package/dist/commands/spec.js.map +1 -0
  93. package/dist/commands/sync-global.d.ts +13 -0
  94. package/dist/commands/sync-global.d.ts.map +1 -0
  95. package/dist/commands/sync-global.js +218 -0
  96. package/dist/commands/sync-global.js.map +1 -0
  97. package/dist/commands/sync.d.ts +13 -0
  98. package/dist/commands/sync.d.ts.map +1 -0
  99. package/dist/commands/sync.js +216 -0
  100. package/dist/commands/sync.js.map +1 -0
  101. package/dist/commands/template-add.d.ts +12 -0
  102. package/dist/commands/template-add.d.ts.map +1 -0
  103. package/dist/commands/template-add.js +65 -0
  104. package/dist/commands/template-add.js.map +1 -0
  105. package/dist/commands/welcome.d.ts +9 -0
  106. package/dist/commands/welcome.d.ts.map +1 -0
  107. package/dist/commands/welcome.js +166 -0
  108. package/dist/commands/welcome.js.map +1 -0
  109. package/dist/core/global-layer.d.ts +124 -0
  110. package/dist/core/global-layer.d.ts.map +1 -0
  111. package/dist/core/global-layer.js +456 -0
  112. package/dist/core/global-layer.js.map +1 -0
  113. package/dist/core/intent-recognition.d.ts +48 -0
  114. package/dist/core/intent-recognition.d.ts.map +1 -0
  115. package/dist/core/intent-recognition.js +524 -0
  116. package/dist/core/intent-recognition.js.map +1 -0
  117. package/package.json +1 -1
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ /**
3
+ * change - 需求变更联动命令
4
+ * 修改任务需求时,自动更新关联 Spec 文件
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.changeCommand = changeCommand;
41
+ const logger_1 = require("../utils/logger");
42
+ const context_1 = require("../core/context");
43
+ const fs_extra_1 = require("fs-extra");
44
+ const path_1 = require("path");
45
+ const transaction_1 = require("../core/transaction");
46
+ async function changeCommand(options) {
47
+ if (!options.task && !options.global) {
48
+ logger_1.logger.error('请指定要变更的 Task 或使用 --global。用法: speccore change --task=<Task编号> --desc="<变更描述>"');
49
+ return;
50
+ }
51
+ if (!options.desc) {
52
+ logger_1.logger.error('请提供变更描述。用法: speccore change --desc="<变更描述>"');
53
+ return;
54
+ }
55
+ const spinner = new logger_1.Spinner('正在分析变更影响...');
56
+ spinner.start();
57
+ try {
58
+ const iteration = await (0, context_1.getDefaultIteration)(options.iteration);
59
+ if (!iteration && !options.global) {
60
+ spinner.fail('未找到活跃期次。请先运行: speccore iteration create --name <名称>');
61
+ return;
62
+ }
63
+ if (options.dryRun) {
64
+ await dryRunChange(options, iteration);
65
+ spinner.stop('变更预览完成(--dry-run 模式,未实际修改)');
66
+ return;
67
+ }
68
+ if (options.global) {
69
+ await applyGlobalChange(options);
70
+ }
71
+ else {
72
+ await applyTaskChange(options, iteration);
73
+ }
74
+ spinner.stop('需求变更已生效');
75
+ logger_1.logger.info('');
76
+ logger_1.logger.info('下一步:');
77
+ logger_1.logger.info(' 1. 运行 speccore validate --task=' + (options.task || '') + ' 验证完整性');
78
+ logger_1.logger.info(' 2. 检查受影响的下游任务是否需要回归');
79
+ }
80
+ catch (error) {
81
+ spinner.fail(`变更失败: ${error}`);
82
+ throw error;
83
+ }
84
+ }
85
+ async function dryRunChange(options, iteration) {
86
+ if (options.global) {
87
+ logger_1.logger.info('🔍 全局层变更影响分析:');
88
+ logger_1.logger.info('');
89
+ logger_1.logger.info('| 文件 | 影响描述 |');
90
+ logger_1.logger.info('| :--- | :--- |');
91
+ logger_1.logger.info('| .speccore/CONSTITUTION.md | 全局配置变更 |');
92
+ logger_1.logger.info('| 所有期次的 TECH.md | 架构方案需同步 |');
93
+ logger_1.logger.info('');
94
+ return;
95
+ }
96
+ logger_1.logger.info(`🔍 ${options.task} 变更影响分析:`);
97
+ logger_1.logger.info('');
98
+ logger_1.logger.info(`变更描述: ${options.desc}`);
99
+ if (iteration) {
100
+ const taskDir = (0, path_1.join)(process.cwd(), iteration, options.task || '');
101
+ logger_1.logger.info('| 文件 | 影响描述 |');
102
+ logger_1.logger.info('| :--- | :--- |');
103
+ logger_1.logger.info(`| ${options.task}/backend/REQ.md | 需求变更 |`);
104
+ logger_1.logger.info(`| ${options.task}/backend/TECH.md | 方案需调整 |`);
105
+ logger_1.logger.info(`| ${options.task}/_shared/API_CONTRACT.yaml | 接口契约可能需更新 |`);
106
+ // 查找受影响的依赖任务
107
+ if (await (0, fs_extra_1.pathExists)(taskDir)) {
108
+ const graphPath = (0, path_1.join)(process.cwd(), iteration, '00-期次总览', 'PROJECT_GRAPH.md');
109
+ if (await (0, fs_extra_1.pathExists)(graphPath)) {
110
+ const content = await (0, fs_extra_1.readFile)(graphPath, 'utf-8');
111
+ const deps = findDependentTasks(content, options.task || '');
112
+ if (deps.length > 0) {
113
+ logger_1.logger.info('');
114
+ logger_1.logger.warn('🔗 受影响下游任务:');
115
+ for (const dep of deps) {
116
+ logger_1.logger.info(` ${dep} → 🔶 待回归`);
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ async function applyTaskChange(options, iteration) {
124
+ if (!options.task) {
125
+ logger_1.logger.error('请指定 --task');
126
+ return;
127
+ }
128
+ const taskDir = (0, path_1.join)(process.cwd(), iteration, options.task);
129
+ if (!await (0, fs_extra_1.pathExists)(taskDir)) {
130
+ logger_1.logger.error(`任务目录不存在: ${taskDir}`);
131
+ return;
132
+ }
133
+ const tx = new transaction_1.FileTransaction();
134
+ const now = new Date().toISOString().split('T')[0];
135
+ // 更新 REQ.md(事务保护)
136
+ const reqPath = (0, path_1.join)(taskDir, 'backend', 'REQ.md');
137
+ if (await (0, fs_extra_1.pathExists)(reqPath)) {
138
+ let content = await (0, fs_extra_1.readFile)(reqPath, 'utf-8');
139
+ const changeNote = `\n## 变更记录\n\n| ${now} | v1.1 | ${options.desc} | SpecCore |\n`;
140
+ tx.write(reqPath, content + changeNote);
141
+ }
142
+ // 更新 TASK.md 变更履历(事务保护)
143
+ const taskMdPath = (0, path_1.join)(taskDir, 'backend', 'TASK.md');
144
+ if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
145
+ let content = await (0, fs_extra_1.readFile)(taskMdPath, 'utf-8');
146
+ const changeEntry = `| ${now} | v1.1 | 需求变更: ${options.desc} | SpecCore |\n`;
147
+ const updated = content.replace(/(\| :--- \| :--- \| :--- \| :--- \|)/, `$1\n${changeEntry}`);
148
+ tx.write(taskMdPath, updated);
149
+ }
150
+ // 同步前端各平台 TASK.md(事务保护)
151
+ const frontendDir = (0, path_1.join)(taskDir, 'frontend');
152
+ if (await (0, fs_extra_1.pathExists)(frontendDir)) {
153
+ const { readdir: rd } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
154
+ const platformDirs = await rd(frontendDir, { withFileTypes: true });
155
+ for (const pd of platformDirs) {
156
+ if (pd.isDirectory()) {
157
+ const ftaskPath = (0, path_1.join)(frontendDir, pd.name, 'TASK.md');
158
+ if (await (0, fs_extra_1.pathExists)(ftaskPath)) {
159
+ let content = await (0, fs_extra_1.readFile)(ftaskPath, 'utf-8');
160
+ const changeEntry = `| ${now} | v1.1 | 需求变更: ${options.desc} | SpecCore |\n`;
161
+ const updated = content.replace(/(\| :--- \| :--- \| :--- \| :--- \|)/, `$1\n${changeEntry}`);
162
+ tx.write(ftaskPath, updated);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ // 提交事务 — 原子写入,失败回滚
168
+ if (tx.length > 0) {
169
+ await tx.commit();
170
+ logger_1.logger.info(`✅ 已更新 ${tx.length} 个文件(事务保护)`);
171
+ logger_1.logger.info(` ${options.task}/backend/REQ.md`);
172
+ logger_1.logger.info(` ${options.task}/backend/TASK.md`);
173
+ }
174
+ }
175
+ async function applyGlobalChange(options) {
176
+ const tx = new transaction_1.FileTransaction();
177
+ // 更新 CONSTITUTION.md 的变更记录(事务保护)
178
+ const constPath = (0, path_1.join)(process.cwd(), '.speccore', 'CONSTITUTION.md');
179
+ if (await (0, fs_extra_1.pathExists)(constPath)) {
180
+ let content = await (0, fs_extra_1.readFile)(constPath, 'utf-8');
181
+ const now = new Date().toISOString().split('T')[0];
182
+ content += `\n## 变更记录\n\n| ${now} | ${options.desc} | SpecCore |\n`;
183
+ tx.write(constPath, content);
184
+ }
185
+ if (tx.length > 0) {
186
+ await tx.commit();
187
+ logger_1.logger.info('✅ 已更新: .speccore/CONSTITUTION.md(事务保护)');
188
+ }
189
+ }
190
+ function findDependentTasks(graphContent, taskName) {
191
+ const deps = [];
192
+ const lines = graphContent.split('\n');
193
+ for (const line of lines) {
194
+ // 查找依赖关系:Task 行中包含对目标 Task 的引用
195
+ if (line.includes(taskName) && !line.includes(`| ${taskName} |`)) {
196
+ const match = line.match(/Task-\d+/g);
197
+ if (match) {
198
+ for (const t of match) {
199
+ if (t !== taskName && !deps.includes(t)) {
200
+ deps.push(t);
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ return deps;
207
+ }
208
+ //# sourceMappingURL=change.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change.js","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * context — 查看当前 Task 的上下文加载状态和 Spec 依赖链
3
+ */
4
+ export interface ContextOptions {
5
+ task?: string;
6
+ }
7
+ export declare function contextCommand(options: ContextOptions): Promise<void>;
8
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqE3E"}
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ /**
3
+ * context — 查看当前 Task 的上下文加载状态和 Spec 依赖链
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.contextCommand = contextCommand;
7
+ const fs_extra_1 = require("fs-extra");
8
+ const path_1 = require("path");
9
+ const logger_1 = require("../utils/logger");
10
+ const context_1 = require("../core/context");
11
+ async function contextCommand(options) {
12
+ try {
13
+ const ctx = await (0, context_1.loadContext)();
14
+ const taskName = options.task || ctx.currentTask;
15
+ if (!taskName) {
16
+ logger_1.logger.info('📋 SpecCore 上下文信息');
17
+ logger_1.logger.info('');
18
+ logger_1.logger.info('当前状态:');
19
+ logger_1.logger.info(` 当前期次:${ctx.currentIteration || '无'}`);
20
+ logger_1.logger.info(` 当前任务:${ctx.currentTask || '无'}`);
21
+ logger_1.logger.info(` 执行人:${ctx.currentAssignee || '无'}`);
22
+ logger_1.logger.info(` 期次状态:${ctx.iterationStatus || '无'}`);
23
+ logger_1.logger.info(` 待处理:${ctx.pendingTasks} 进行中:${ctx.inProgressTasks} 已完成:${ctx.completedTasks} 阻塞:${ctx.blockedTasks}`);
24
+ logger_1.logger.info('');
25
+ logger_1.logger.info(` 最后操作:${ctx.lastAction || '无'}`);
26
+ logger_1.logger.info(` 最后意图:${ctx.lastIntent || '无'}`);
27
+ logger_1.logger.info(` 最后更新:${ctx.lastUpdated || '无'}`);
28
+ return;
29
+ }
30
+ // 查找 Task 目录
31
+ const taskDir = await findTaskDir(taskName);
32
+ if (!taskDir) {
33
+ logger_1.logger.error(`Task not found: ${taskName}`);
34
+ return;
35
+ }
36
+ // 分析上下文
37
+ const analysis = await analyzeTaskContext(taskDir);
38
+ logger_1.logger.info('📋 Task 上下文分析');
39
+ logger_1.logger.info('');
40
+ logger_1.logger.info(`📂 Task: ${taskName}`);
41
+ logger_1.logger.info(`📍 路径: ${taskDir}`);
42
+ logger_1.logger.info('');
43
+ // Spec 文件状态
44
+ logger_1.logger.info('📄 Spec 文件加载状态:');
45
+ for (const file of analysis.specFiles) {
46
+ const icon = file.exists ? '✅' : '❌';
47
+ logger_1.logger.info(` ${icon} ${file.name} [${file.type}] ${file.size ? `(${file.size})` : ''}`);
48
+ }
49
+ // 平台
50
+ if (analysis.platforms.length > 0) {
51
+ logger_1.logger.info('');
52
+ logger_1.logger.info('📱 平台覆盖:');
53
+ for (const platform of analysis.platforms) {
54
+ logger_1.logger.info(` - ${platform}`);
55
+ }
56
+ }
57
+ // 依赖分析
58
+ if (analysis.dependencies.length > 0) {
59
+ logger_1.logger.info('');
60
+ logger_1.logger.info('🔗 跨平台依赖:');
61
+ for (const dep of analysis.dependencies) {
62
+ logger_1.logger.info(` - ${dep}`);
63
+ }
64
+ }
65
+ // 验收标准
66
+ logger_1.logger.info('');
67
+ logger_1.logger.info('📊 验收标准:');
68
+ logger_1.logger.info(` 总计: ${analysis.totalAC} 通过: ${analysis.passedAC} 未通过: ${analysis.failedAC}`);
69
+ }
70
+ catch (error) {
71
+ logger_1.logger.error(`Context analysis failed: ${error}`);
72
+ }
73
+ }
74
+ /**
75
+ * 查找 Task 目录
76
+ */
77
+ async function findTaskDir(taskName) {
78
+ const cwd = process.cwd();
79
+ const entries = await (0, fs_extra_1.readdir)(cwd, { withFileTypes: true });
80
+ // 直接匹配
81
+ const directPaths = [
82
+ (0, path_1.join)(cwd, taskName),
83
+ (0, path_1.join)(cwd, `期次-*/*${taskName}*`),
84
+ ];
85
+ const exactMatch = (0, path_1.join)(cwd, taskName);
86
+ if (await (0, fs_extra_1.pathExists)(exactMatch))
87
+ return exactMatch;
88
+ // 遍历期次目录查找
89
+ for (const entry of entries) {
90
+ if (entry.isDirectory() && entry.name.startsWith('期次-')) {
91
+ const iterPath = (0, path_1.join)(cwd, entry.name);
92
+ const tasks = await (0, fs_extra_1.readdir)(iterPath, { withFileTypes: true });
93
+ for (const task of tasks) {
94
+ if (task.isDirectory() && (task.name === taskName ||
95
+ task.name.startsWith(taskName) ||
96
+ task.name.includes(taskName))) {
97
+ return (0, path_1.join)(iterPath, task.name);
98
+ }
99
+ }
100
+ }
101
+ }
102
+ return null;
103
+ }
104
+ /**
105
+ * 分析 Task 上下文
106
+ */
107
+ async function analyzeTaskContext(taskDir) {
108
+ const result = {
109
+ specFiles: [],
110
+ platforms: [],
111
+ dependencies: [],
112
+ totalAC: 0,
113
+ passedAC: 0,
114
+ failedAC: 0,
115
+ };
116
+ // 检查 _shared/
117
+ const shared = (0, path_1.join)(taskDir, '_shared');
118
+ if (await (0, fs_extra_1.pathExists)(shared)) {
119
+ const sharedFiles = await (0, fs_extra_1.readdir)(shared);
120
+ for (const file of sharedFiles) {
121
+ const filePath = (0, path_1.join)(shared, file);
122
+ const stats = await (0, fs_extra_1.readFile)(filePath, 'utf-8').then(s => s.length).catch(() => 0);
123
+ result.specFiles.push({ name: file, type: '共享', exists: true, size: formatSize(stats) });
124
+ }
125
+ }
126
+ // 检查 backend/
127
+ const backend = (0, path_1.join)(taskDir, 'backend');
128
+ if (await (0, fs_extra_1.pathExists)(backend)) {
129
+ const files = await (0, fs_extra_1.readdir)(backend);
130
+ for (const file of files) {
131
+ const filePath = (0, path_1.join)(backend, file);
132
+ const stats = await (0, fs_extra_1.readFile)(filePath, 'utf-8').then(s => s.length).catch(() => 0);
133
+ result.specFiles.push({ name: file, type: '后端', exists: true, size: formatSize(stats) });
134
+ // 统计验收标准
135
+ if (file === 'REQ.md') {
136
+ const content = await (0, fs_extra_1.readFile)(filePath, 'utf-8').catch(() => '');
137
+ result.totalAC += (content.match(/- \[[ x]\] AC-/g) || []).length;
138
+ result.passedAC += (content.match(/- \[x\] AC-/g) || []).length;
139
+ result.failedAC = result.totalAC - result.passedAC;
140
+ }
141
+ }
142
+ }
143
+ else {
144
+ result.specFiles.push({ name: 'backend/', type: '后端', exists: false, size: '-' });
145
+ }
146
+ // 检查 frontend/{platform}/
147
+ const frontend = (0, path_1.join)(taskDir, 'frontend');
148
+ if (await (0, fs_extra_1.pathExists)(frontend)) {
149
+ const platformDirs = await (0, fs_extra_1.readdir)(frontend, { withFileTypes: true });
150
+ for (const pd of platformDirs) {
151
+ if (pd.isDirectory()) {
152
+ result.platforms.push(pd.name);
153
+ const pDir = (0, path_1.join)(frontend, pd.name);
154
+ const files = await (0, fs_extra_1.readdir)(pDir);
155
+ for (const file of files) {
156
+ const filePath = (0, path_1.join)(pDir, file);
157
+ const stats = await (0, fs_extra_1.readFile)(filePath, 'utf-8').then(s => s.length).catch(() => 0);
158
+ result.specFiles.push({ name: file, type: `前端/${pd.name}`, exists: true, size: formatSize(stats) });
159
+ }
160
+ }
161
+ }
162
+ }
163
+ // 检查跨平台依赖
164
+ if (result.platforms.length > 0) {
165
+ for (let i = 0; i < result.platforms.length; i++) {
166
+ for (let j = i + 1; j < result.platforms.length; j++) {
167
+ result.dependencies.push(`${result.platforms[i]} ↔ ${result.platforms[j]} (共享 API_CONTRACT.yaml)`);
168
+ }
169
+ }
170
+ }
171
+ return result;
172
+ }
173
+ function formatSize(bytes) {
174
+ if (bytes < 1024)
175
+ return `${bytes}B`;
176
+ if (bytes < 1024 * 1024)
177
+ return `${(bytes / 1024).toFixed(1)}KB`;
178
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
179
+ }
180
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/commands/context.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAWH,wCAqEC;AA9ED,uCAAyD;AACzD,+BAA4B;AAC5B,4CAAyC;AACzC,6CAA8C;AAMvC,KAAK,UAAU,cAAc,CAAC,OAAuB;IAC1D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAA,qBAAW,GAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC;QAEjD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,eAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YACjC,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACrB,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,gBAAgB,IAAI,GAAG,EAAE,CAAC,CAAC;YACrD,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;YAChD,eAAM,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,eAAe,IAAI,GAAG,EAAE,CAAC,CAAC;YACnD,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,eAAe,IAAI,GAAG,EAAE,CAAC,CAAC;YACpD,eAAM,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,YAAY,SAAS,GAAG,CAAC,eAAe,SAAS,GAAG,CAAC,cAAc,QAAQ,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;YACxH,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC;YAC/C,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC,CAAC;YAC/C,eAAM,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;YAChD,OAAO;QACT,CAAC;QAED,aAAa;QACb,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,eAAM,CAAC,KAAK,CAAC,mBAAmB,QAAQ,EAAE,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;QAED,QAAQ;QACR,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAEnD,eAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QAC7B,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,eAAM,CAAC,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC,CAAC;QACpC,eAAM,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;QACjC,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEhB,YAAY;QACZ,eAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACrC,eAAM,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,KAAK;QACL,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,eAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACxB,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;gBAC1C,eAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,OAAO;QACP,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAChB,eAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACzB,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;gBACxC,eAAM,CAAC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,OAAO;QACP,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,eAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACxB,eAAM,CAAC,IAAI,CAAC,SAAS,QAAQ,CAAC,OAAO,SAAS,QAAQ,CAAC,QAAQ,UAAU,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,WAAW,CAAC,QAAgB;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,MAAM,IAAA,kBAAO,EAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5D,OAAO;IACP,MAAM,WAAW,GAAG;QAClB,IAAA,WAAI,EAAC,GAAG,EAAE,QAAQ,CAAC;QACnB,IAAA,WAAI,EAAC,GAAG,EAAE,SAAS,QAAQ,GAAG,CAAC;KAChC,CAAC;IAEF,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACvC,IAAI,MAAM,IAAA,qBAAU,EAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAEpD,WAAW;IACX,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,MAAM,KAAK,GAAG,MAAM,IAAA,kBAAO,EAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,WAAW,EAAE,IAAI,CACxB,IAAI,CAAC,IAAI,KAAK,QAAQ;oBACtB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;oBAC9B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC7B,EAAE,CAAC;oBACF,OAAO,IAAA,WAAI,EAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,kBAAkB,CAAC,OAAe;IAQ/C,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,EAAqE;QAChF,SAAS,EAAE,EAAc;QACzB,YAAY,EAAE,EAAc;QAC5B,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;KACZ,CAAC;IAEF,cAAc;IACd,MAAM,MAAM,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,IAAA,qBAAU,EAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,IAAA,kBAAO,EAAC,MAAM,CAAC,CAAC;QAC1C,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;YAC/B,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YACnF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACH,CAAC;IAED,cAAc;IACd,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACzC,IAAI,MAAM,IAAA,qBAAU,EAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,IAAA,kBAAO,EAAC,OAAO,CAAC,CAAC;QACrC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;YACnF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAEzF,SAAS;YACT,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;gBAClE,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAClE,MAAM,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAChE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACpF,CAAC;IAED,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAC3C,IAAI,MAAM,IAAA,qBAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,MAAM,YAAY,GAAG,MAAM,IAAA,kBAAO,EAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,KAAK,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC9B,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;gBACrB,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;gBAC/B,MAAM,IAAI,GAAG,IAAA,WAAI,EAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;gBACrC,MAAM,KAAK,GAAG,MAAM,IAAA,kBAAO,EAAC,IAAI,CAAC,CAAC;gBAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAClC,MAAM,KAAK,GAAG,MAAM,IAAA,mBAAQ,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;oBACnF,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACtG,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,UAAU;IACV,IAAI,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACrD,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;YACrG,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,GAAG,CAAC;IACrC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * dashboard - 生成可视化仪表盘命令
3
+ * 基于全量层数据生成静态 HTML 仪表盘,含 Chart.js 图表
4
+ */
5
+ export interface DashboardOptions {
6
+ output?: string;
7
+ }
8
+ export declare function dashboardCommand(options: DashboardOptions): Promise<void>;
9
+ //# sourceMappingURL=dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4D/E"}
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * dashboard - 生成可视化仪表盘命令
4
+ * 基于全量层数据生成静态 HTML 仪表盘,含 Chart.js 图表
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.dashboardCommand = dashboardCommand;
8
+ const logger_1 = require("../utils/logger");
9
+ const global_layer_1 = require("../core/global-layer");
10
+ const fs_extra_1 = require("fs-extra");
11
+ const path_1 = require("path");
12
+ async function dashboardCommand(options) {
13
+ const spinner = new logger_1.Spinner('采集全量层数据...');
14
+ spinner.start();
15
+ try {
16
+ const index = await (0, global_layer_1.readGlobalIndex)();
17
+ if (index.projects.length === 0 && index.reqs.length === 0) {
18
+ spinner.fail('全量层为空,无法生成仪表盘。请先导入项目。');
19
+ return;
20
+ }
21
+ spinner.stop('数据采集完成,正在生成仪表盘...');
22
+ // 统计数据
23
+ const totalReqs = index.reqs.length;
24
+ const implemented = index.reqs.filter((r) => r.status === '✅ 已实现' || r.status === '📦 已有实现').length;
25
+ const inProgress = index.reqs.filter((r) => r.status === '🔄 进行中').length;
26
+ const pending = totalReqs - implemented - inProgress;
27
+ const completionRate = totalReqs > 0 ? Math.round((implemented / totalReqs) * 100) : 0;
28
+ // 项目分布数据
29
+ const projectLabels = index.projects.map((p) => p.name);
30
+ const projectReqs = index.projects.map((p) => p.reqCount);
31
+ // 生成 HTML
32
+ const html = generateDashboardHtml(index.projects.length, totalReqs, implemented, inProgress, pending, completionRate, projectLabels, projectReqs, index);
33
+ const outputPath = options.output || (0, path_1.join)(process.cwd(), 'speccore-dashboard.html');
34
+ await (0, fs_extra_1.writeFile)(outputPath, html);
35
+ logger_1.logger.info('');
36
+ logger_1.logger.success('✅ 仪表盘已生成!');
37
+ logger_1.logger.info('');
38
+ logger_1.logger.info(`📁 输出文件: ${outputPath}`);
39
+ logger_1.logger.info('');
40
+ logger_1.logger.info('📊 包含内容:');
41
+ logger_1.logger.info(' - 统计卡片:总需求、已实现、进行中、待开发');
42
+ logger_1.logger.info(' - 需求状态分布(饼图)');
43
+ logger_1.logger.info(' - 项目需求分布(柱状图)');
44
+ logger_1.logger.info(' - 项目详细列表');
45
+ logger_1.logger.info(' - 期次关联状态');
46
+ logger_1.logger.info('');
47
+ logger_1.logger.info('💡 在浏览器中打开即可查看。支持移动端自适应布局。');
48
+ }
49
+ catch (error) {
50
+ spinner.fail(`生成仪表盘失败: ${error}`);
51
+ throw error;
52
+ }
53
+ }
54
+ function generateDashboardHtml(projectCount, totalReqs, implemented, inProgress, pending, completionRate, projectLabels, projectReqs, index) {
55
+ const now = new Date().toISOString().split('T')[0];
56
+ // 生成项目表格行
57
+ let projectRows = '';
58
+ for (const proj of index.projects) {
59
+ projectRows += `<tr><td>${proj.name}</td><td><span class="badge badge-${proj.type}">${proj.type}</span></td><td>${proj.reqCount}</td><td>${proj.lastImport}</td></tr>`;
60
+ }
61
+ // 生成需求表格行
62
+ let reqRows = '';
63
+ for (const req of index.reqs.slice(0, 20)) {
64
+ const statusClass = req.status.includes('✅') ? 'done' : req.status.includes('🔄') ? 'progress' : 'pending';
65
+ reqRows += `<tr><td>${req.id}</td><td>${req.name}</td><td>${req.project}</td><td><span class="badge badge-${statusClass}">${req.status}</span></td><td>${req.iteration || '-'}</td></tr>`;
66
+ }
67
+ return `<!DOCTYPE html>
68
+ <html lang="zh-CN">
69
+ <head>
70
+ <meta charset="UTF-8">
71
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
72
+ <title>SpecCore 项目仪表盘</title>
73
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
74
+ <style>
75
+ * { margin: 0; padding: 0; box-sizing: border-box; }
76
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', sans-serif; background: #f0f2f5; padding: 24px; color: #1a1a2e; }
77
+ .container { max-width: 1400px; margin: 0 auto; }
78
+ .header { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: white; padding: 28px 32px; border-radius: 16px; margin-bottom: 24px; }
79
+ .header h1 { font-size: 26px; font-weight: 700; letter-spacing: -0.5px; }
80
+ .header .meta { opacity: 0.6; margin-top: 6px; font-size: 14px; }
81
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
82
+ .stat-card { background: white; border-radius: 12px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); text-align: center; }
83
+ .stat-card .label { font-size: 13px; color: #64748b; margin-bottom: 8px; font-weight: 500; }
84
+ .stat-card .value { font-size: 36px; font-weight: 800; letter-spacing: -1px; }
85
+ .stat-card .sub { font-size: 13px; color: #94a3b8; margin-top: 4px; }
86
+ .stat-card .value.green { color: #22c55e; }
87
+ .stat-card .value.blue { color: #3b82f6; }
88
+ .stat-card .value.amber { color: #f59e0b; }
89
+ .stat-card .value.gray { color: #64748b; }
90
+ .charts { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 24px; }
91
+ @media (max-width: 768px) { .charts { grid-template-columns: 1fr; } .stats { grid-template-columns: 1fr 1fr; } }
92
+ .chart-card { background: white; border-radius: 12px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }
93
+ .chart-card h3 { font-size: 16px; font-weight: 600; margin-bottom: 16px; color: #334155; }
94
+ .chart-card canvas { max-height: 300px; }
95
+ .full { grid-column: 1 / -1; }
96
+ .table-card { background: white; border-radius: 12px; padding: 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-bottom: 24px; overflow-x: auto; }
97
+ .table-card h3 { font-size: 16px; font-weight: 600; margin-bottom: 16px; color: #334155; }
98
+ table { width: 100%; border-collapse: collapse; font-size: 14px; }
99
+ thead tr { border-bottom: 2px solid #e2e8f0; }
100
+ th { text-align: left; padding: 10px 8px; color: #64748b; font-weight: 600; font-size: 13px; }
101
+ td { padding: 10px 8px; border-bottom: 1px solid #f1f5f9; }
102
+ .badge { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: 12px; font-weight: 500; }
103
+ .badge-backend { background: #dbeafe; color: #1e40af; }
104
+ .badge-web { background: #dcfce7; color: #166534; }
105
+ .badge-h5 { background: #fef3c7; color: #92400e; }
106
+ .badge-miniapp { background: #f3e8ff; color: #6b21a8; }
107
+ .badge-done { background: #dcfce7; color: #166534; }
108
+ .badge-progress { background: #dbeafe; color: #1e40af; }
109
+ .badge-pending { background: #f1f5f9; color: #64748b; }
110
+ .footer { text-align: center; color: #94a3b8; font-size: 12px; margin-top: 32px; padding: 16px; }
111
+ </style>
112
+ </head>
113
+ <body>
114
+ <div class="container">
115
+ <div class="header">
116
+ <h1>📊 SpecCore 项目仪表盘</h1>
117
+ <div class="meta">生成时间: ${now} | 全量层版本: ${index.version} | 项目数: ${projectCount}</div>
118
+ </div>
119
+
120
+ <div class="stats">
121
+ <div class="stat-card">
122
+ <div class="label">📋 总需求数</div>
123
+ <div class="value blue">${totalReqs}</div>
124
+ <div class="sub">${projectCount} 个项目</div>
125
+ </div>
126
+ <div class="stat-card">
127
+ <div class="label">✅ 已完成</div>
128
+ <div class="value green">${implemented}</div>
129
+ <div class="sub">完成率 ${completionRate}%</div>
130
+ </div>
131
+ <div class="stat-card">
132
+ <div class="label">🔄 进行中</div>
133
+ <div class="value amber">${inProgress}</div>
134
+ <div class="sub">${Math.round((inProgress / (totalReqs || 1)) * 100)}%</div>
135
+ </div>
136
+ <div class="stat-card">
137
+ <div class="label">🔲 待开发</div>
138
+ <div class="value gray">${pending}</div>
139
+ <div class="sub">${Math.round((pending / (totalReqs || 1)) * 100)}%</div>
140
+ </div>
141
+ </div>
142
+
143
+ <div class="charts">
144
+ <div class="chart-card">
145
+ <h3>📊 需求状态分布</h3>
146
+ <canvas id="statusChart"></canvas>
147
+ </div>
148
+ <div class="chart-card">
149
+ <h3>📈 项目需求分布</h3>
150
+ <canvas id="projectChart"></canvas>
151
+ </div>
152
+ </div>
153
+
154
+ <div class="table-card">
155
+ <h3>📋 项目列表</h3>
156
+ <table>
157
+ <thead><tr><th>项目名称</th><th>类型</th><th>需求数</th><th>最后导入</th></tr></thead>
158
+ <tbody>${projectRows || '<tr><td colspan="4">暂无项目</td></tr>'}</tbody>
159
+ </table>
160
+ </div>
161
+
162
+ <div class="table-card">
163
+ <h3>📝 需求详情</h3>
164
+ <table>
165
+ <thead><tr><th>需求 ID</th><th>名称</th><th>项目</th><th>状态</th><th>关联期次</th></tr></thead>
166
+ <tbody>${reqRows || '<tr><td colspan="5">暂无需求</td></tr>'}</tbody>
167
+ </table>
168
+ </div>
169
+
170
+ <div class="footer">Powered by SpecCore | Generated ${now}</div>
171
+ </div>
172
+
173
+ <script>
174
+ new Chart(document.getElementById('statusChart'), {
175
+ type: 'doughnut',
176
+ data: {
177
+ labels: ['已实现', '进行中', '待开发'],
178
+ datasets: [{
179
+ data: [${implemented}, ${inProgress}, ${pending}],
180
+ backgroundColor: ['#22c55e', '#3b82f6', '#e2e8f0'],
181
+ borderWidth: 2,
182
+ borderColor: '#fff'
183
+ }]
184
+ },
185
+ options: { responsive: true, plugins: { legend: { position: 'bottom' } } }
186
+ });
187
+
188
+ new Chart(document.getElementById('projectChart'), {
189
+ type: 'bar',
190
+ data: {
191
+ labels: ${JSON.stringify(projectLabels)},
192
+ datasets: [{
193
+ label: '需求数',
194
+ data: ${JSON.stringify(projectReqs)},
195
+ backgroundColor: '#3b82f6',
196
+ borderRadius: 8
197
+ }]
198
+ },
199
+ options: {
200
+ responsive: true,
201
+ plugins: { legend: { display: false } },
202
+ scales: { y: { beginAtZero: true, ticks: { stepSize: 1 } } }
203
+ }
204
+ });
205
+ </script>
206
+ </body>
207
+ </html>`;
208
+ }
209
+ //# sourceMappingURL=dashboard.js.map