speccore 5.11.2 → 5.12.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 (52) hide show
  1. package/README.md +8 -4
  2. package/dist/cli.js +40 -3
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/analyze.d.ts +7 -0
  5. package/dist/commands/analyze.d.ts.map +1 -0
  6. package/dist/commands/analyze.js +369 -0
  7. package/dist/commands/analyze.js.map +1 -0
  8. package/dist/commands/change.d.ts +2 -0
  9. package/dist/commands/change.d.ts.map +1 -1
  10. package/dist/commands/change.js +72 -0
  11. package/dist/commands/change.js.map +1 -1
  12. package/dist/commands/done.d.ts +8 -0
  13. package/dist/commands/done.d.ts.map +1 -0
  14. package/dist/commands/done.js +97 -0
  15. package/dist/commands/done.js.map +1 -0
  16. package/dist/commands/execute.d.ts +1 -0
  17. package/dist/commands/execute.d.ts.map +1 -1
  18. package/dist/commands/execute.js +104 -0
  19. package/dist/commands/execute.js.map +1 -1
  20. package/dist/commands/init.d.ts.map +1 -1
  21. package/dist/commands/init.js +3 -0
  22. package/dist/commands/init.js.map +1 -1
  23. package/dist/commands/iteration/create.d.ts.map +1 -1
  24. package/dist/commands/iteration/create.js +6 -4
  25. package/dist/commands/iteration/create.js.map +1 -1
  26. package/dist/commands/iteration/split.d.ts +2 -0
  27. package/dist/commands/iteration/split.d.ts.map +1 -1
  28. package/dist/commands/iteration/split.js +492 -11
  29. package/dist/commands/iteration/split.js.map +1 -1
  30. package/dist/commands/lifecycle.d.ts +9 -0
  31. package/dist/commands/lifecycle.d.ts.map +1 -0
  32. package/dist/commands/lifecycle.js +223 -0
  33. package/dist/commands/lifecycle.js.map +1 -0
  34. package/dist/commands/word2spec.d.ts +1 -0
  35. package/dist/commands/word2spec.d.ts.map +1 -1
  36. package/dist/commands/word2spec.js +92 -12
  37. package/dist/commands/word2spec.js.map +1 -1
  38. package/dist/core/global-counters.d.ts +16 -0
  39. package/dist/core/global-counters.d.ts.map +1 -0
  40. package/dist/core/global-counters.js +44 -0
  41. package/dist/core/global-counters.js.map +1 -0
  42. package/dist/core/next-steps.d.ts +2 -0
  43. package/dist/core/next-steps.d.ts.map +1 -0
  44. package/dist/core/next-steps.js +69 -0
  45. package/dist/core/next-steps.js.map +1 -0
  46. package/dist/core/risk-scorer.d.ts +14 -0
  47. package/dist/core/risk-scorer.d.ts.map +1 -0
  48. package/dist/core/risk-scorer.js +61 -0
  49. package/dist/core/risk-scorer.js.map +1 -0
  50. package/dist/locales/en-US.json +19 -2
  51. package/dist/locales/zh-CN.json +19 -2
  52. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/iteration/create.ts"],"names":[],"mappings":";;AAWA,wDAkDC;AA7DD,uCAAsE;AACtE,+BAA4B;AAC5B,+CAAqD;AACrD,gDAAmD;AAQ5C,KAAK,UAAU,sBAAsB,CAAC,OAA+B;IAC1E,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClB,eAAM,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC9D,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,gBAAO,CAAC,uBAAuB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,CAAC;IAEhB,IAAI,CAAC;QACH,uDAAuD;QACvD,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,MAAM,YAAY,GAAG,MAAM,QAAQ,EAAE,CAAC;QAEtC,0BAA0B;QAC1B,IAAI,MAAM,IAAA,qBAAU,EAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC,IAAI,CAAC,6BAA6B,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1D,OAAO;QACT,CAAC;QAED,6BAA6B;QAC7B,MAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;QAC/C,MAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;QAC/C,MAAM,IAAA,oBAAS,EAAC,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC;QAE/C,uBAAuB;QACvB,MAAM,oBAAoB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QAElD,0BAA0B;QAC1B,MAAM,qBAAqB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAEnD,sBAAsB;QACtB,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE/C,4DAA4D;QAC5D,MAAM,IAAA,uBAAa,EAAC;YAClB,gBAAgB,EAAE,QAAQ;YAC1B,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACtC,CAAC,CAAC;QAEH,OAAO,CAAC,IAAI,CAAC,sBAAsB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,eAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChB,eAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC3B,eAAM,CAAC,IAAI,CAAC,aAAa,YAAY,yBAAyB,CAAC,CAAC;QAChE,eAAM,CAAC,IAAI,CAAC,aAAa,YAAY,0BAA0B,CAAC,CAAC;QACjE,eAAM,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,+BAA+B,KAAK,EAAE,CAAC,CAAC;QACrD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,YAAoB,EAAE,OAA+B;IACvF,iBAAiB;IACjB,MAAM,IAAA,oBAAS,EACb,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,EAAE,gBAAgB,CAAC,EAC/C;;OAEG,OAAO,CAAC,IAAI;SACV,OAAO,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BtD,CACE,CAAC;IAEF,kBAAkB;IAClB,MAAM,IAAA,oBAAS,EACb,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAChD;;OAEG,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;CAelB,CACE,CAAC;IAEF,mBAAmB;IACnB,MAAM,IAAA,oBAAS,EACb,IAAA,WAAI,EAAC,YAAY,EAAE,SAAS,EAAE,kBAAkB,CAAC,EACjD;;OAEG,OAAO,CAAC,IAAI;SACV,OAAO,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,KAAK;;;;;;;;;;;;;;;;;;;;;CAqBtD,CACE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,IAAY,EAAE,OAA+B;IAChF,MAAM,SAAS,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC;IAE/D,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,MAAM,IAAA,qBAAU,EAAC,SAAS,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,mFAAmF,CAAC;IAChG,CAAC;IAED,0BAA0B;IAC1B,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,KAAK,EAAE,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,IAAI,MAAM,SAAS,mBAAmB,CAAC;IAE7D,OAAO,IAAI,QAAQ,CAAC;IACpB,MAAM,IAAA,oBAAS,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAY,EAAE,OAA+B;IAC5E,MAAM,eAAe,GAAG,IAAA,WAAI,EAAC,WAAW,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEhE,IAAI,CAAC,CAAC,MAAM,IAAA,qBAAU,EAAC,eAAe,CAAC,CAAC;QAAE,OAAO;IACjD,IAAI,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAEvD,OAAO;IACP,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,CAAC;QAAE,OAAO;IAE5C,MAAM,SAAS,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,MAAM,OAAO,CAAC,EAAE,IAAI,KAAK,EAAE,CAAC;IACtE,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,KAAK,IAAI,mBAAmB,KAAK,IAAI,CAAC;IAEvD,cAAc;IACd,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAChC,wBAAwB;QACxB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QACjE,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YACpB,uBAAuB;YACvB,IAAI,SAAS,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,cAAc;YAC9C,KAAK,IAAI,CAAC,GAAG,SAAS,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC9C,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;oBAAE,SAAS,GAAG,CAAC,GAAG,CAAC,CAAC;qBAC3C,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC;oBAAE,MAAM;YAC1E,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;YACrC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;SAAM,CAAC;QACN,kBAAkB;QAClB,OAAO,IAAI,4EAA4E,QAAQ,IAAI,CAAC;IACtG,CAAC;IAED,MAAM,IAAA,oBAAS,EAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../../src/commands/iteration/create.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,8 @@ export interface IterationSplitOptions {
4
4
  sections?: string;
5
5
  target?: string;
6
6
  dryRun?: boolean;
7
+ platforms?: string;
8
+ strict?: boolean;
7
9
  }
8
10
  export declare function iterationSplitCommand(options: IterationSplitOptions): Promise<void>;
9
11
  //# sourceMappingURL=split.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../src/commands/iteration/split.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmDzF"}
1
+ {"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../src/commands/iteration/split.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA+BD,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8EzF"}
@@ -5,6 +5,37 @@ const fs_extra_1 = require("fs-extra");
5
5
  const path_1 = require("path");
6
6
  const logger_1 = require("../../utils/logger");
7
7
  const context_1 = require("../../core/context");
8
+ const risk_scorer_1 = require("../../core/risk-scorer");
9
+ const global_counters_1 = require("../../core/global-counters");
10
+ const next_steps_1 = require("../../core/next-steps");
11
+ async function detectPlatforms(iterationDir, specified) {
12
+ if (specified)
13
+ return specified.split(',').map(p => p.trim()).filter(Boolean);
14
+ // Auto-detect from INDEX.md (populated by word2spec)
15
+ const indexPath = (0, path_1.join)(iterationDir, '00-需求文档', 'INDEX.md');
16
+ if (await (0, fs_extra_1.pathExists)(indexPath)) {
17
+ const content = await (0, fs_extra_1.readFile)(indexPath, 'utf-8');
18
+ // Parse table rows: skip header and separator lines
19
+ const lines = content.split('\n');
20
+ const platforms = new Set();
21
+ let inTable = false;
22
+ for (const line of lines) {
23
+ if (line.startsWith('|') && !line.includes(':---')) {
24
+ const cols = line.split('|').map(c => c.trim()).filter(Boolean);
25
+ // First column is platform name, skip header row
26
+ if (cols[0] && cols[0] !== '端' && !String(cols[0]).includes('文件')) {
27
+ platforms.add(cols[0]);
28
+ inTable = true;
29
+ }
30
+ }
31
+ }
32
+ // Also check for common date patterns in first col and filter them
33
+ const filtered = [...platforms].filter(p => !/^\d{4}-\d{2}-\d{2}$/.test(p));
34
+ if (filtered.length > 0)
35
+ return filtered;
36
+ }
37
+ return ['web']; // default
38
+ }
8
39
  async function iterationSplitCommand(options) {
9
40
  const spinner = new logger_1.Spinner('Splitting requirements into tasks');
10
41
  spinner.start();
@@ -27,6 +58,23 @@ async function iterationSplitCommand(options) {
27
58
  return;
28
59
  }
29
60
  logger_1.logger.info(`Found ${sections.length} sections to split`);
61
+ const platforms = await detectPlatforms(iterationDir, options.platforms);
62
+ logger_1.logger.info(`Platforms: ${platforms.join(', ')}`);
63
+ // ── Strict mode: preview + confirm each task's split plan ──
64
+ if (options.strict) {
65
+ const approved = await strictSplitPreview(sections, platforms, iterationDir);
66
+ if (approved.length === 0) {
67
+ spinner.stop('已取消,未创建任何任务');
68
+ return;
69
+ }
70
+ for (const section of approved) {
71
+ const idx = sections.indexOf(section);
72
+ const taskId = `Task-${String(idx + 1).padStart(3, '0')}`;
73
+ await createTaskFromSection(iterationDir, taskId, section, platforms);
74
+ }
75
+ spinner.stop(`✅ 创建了 ${approved.length} 个任务`);
76
+ return;
77
+ }
30
78
  if (options.dryRun) {
31
79
  spinner.stop('Dry run complete - no files created');
32
80
  for (const section of sections) {
@@ -36,12 +84,17 @@ async function iterationSplitCommand(options) {
36
84
  }
37
85
  // Create tasks
38
86
  for (let i = 0; i < sections.length; i++) {
39
- const taskId = `Task-${String(i + 1).padStart(3, '0')}`;
40
- await createTaskFromSection(iterationDir, taskId, sections[i]);
87
+ const { id: taskId } = await (0, global_counters_1.nextTaskId)(sections[i].name);
88
+ await createTaskFromSection(iterationDir, taskId, sections[i], platforms);
41
89
  }
90
+ // ── Generate impact graph + risk scores ──
91
+ await generateImpactGraph(iterationDir, sections, platforms);
92
+ // ── Generate .env.example for the iteration ──
93
+ await generateEnvExample(iterationDir, sections);
42
94
  // Update PROJECT_GRAPH.md
43
95
  await updateProjectGraph(iterationDir, sections);
44
96
  spinner.stop(`Created ${sections.length} tasks from requirements`);
97
+ (0, next_steps_1.showNextSteps)('split');
45
98
  }
46
99
  catch (error) {
47
100
  spinner.fail(`Split failed: ${error}`);
@@ -50,6 +103,7 @@ async function iterationSplitCommand(options) {
50
103
  }
51
104
  function extractSections(content, sectionFilter) {
52
105
  const sections = [];
106
+ let currentPlatform;
53
107
  const lines = content.split('\n');
54
108
  let currentSection = null;
55
109
  let currentContent = [];
@@ -65,6 +119,17 @@ function extractSections(content, sectionFilter) {
65
119
  content: '',
66
120
  level: headerMatch[1].length
67
121
  };
122
+ // 检测 "## {X}端需求" 父章节,子章节继承此平台
123
+ const platformMatch = currentSection.name.match(/^(.+)端需求$/);
124
+ if (platformMatch) {
125
+ currentPlatform = platformMatch[1];
126
+ currentSection = null; // 容器章节本身不作为 Task
127
+ continue;
128
+ }
129
+ else if (currentSection.level === 2) {
130
+ currentPlatform = undefined; // 新的 ## 章节重置平台
131
+ }
132
+ currentSection.platform = currentPlatform;
68
133
  currentContent = [];
69
134
  }
70
135
  else if (currentSection) {
@@ -82,15 +147,61 @@ function extractSections(content, sectionFilter) {
82
147
  return filters.some(f => s.name.includes(f));
83
148
  });
84
149
  }
85
- return sections;
150
+ // Filter template noise: skip empty/template placeholder sections
151
+ return filterTemplateNoise(sections);
86
152
  }
87
- async function createTaskFromSection(iterationDir, taskId, section) {
153
+ const TEMPLATE_PATTERNS = [
154
+ /^\d+\.\d+\s*(背景|目标|范围)\s*$/,
155
+ /^\d+\.\d+\s*(性能|安全|兼容性)\s*$/,
156
+ /^\d+\.\s*(需求概述|功能需求|非功能需求|验收标准|附录)\s*$/,
157
+ /^功能模块[一二三四五]\s*$/,
158
+ ];
159
+ function filterTemplateNoise(sections) {
160
+ return sections.filter(s => {
161
+ // Skip sections matching template patterns
162
+ for (const pattern of TEMPLATE_PATTERNS) {
163
+ if (pattern.test(s.name))
164
+ return false;
165
+ }
166
+ // Skip sections with effectively empty content
167
+ const meaningful = (s.content || '').replace(/[\s\n>#*-|]/g, '').length;
168
+ if (meaningful < 3)
169
+ return false;
170
+ return true;
171
+ });
172
+ }
173
+ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms) {
88
174
  const taskDir = (0, path_1.join)(iterationDir, taskId);
89
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend'));
90
- await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'frontend'));
175
+ // 如果 section 有指定平台则只创建该平台,否则创建全部平台
176
+ const taskPlatforms = section.platform ? [section.platform] : allPlatforms;
91
177
  await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '_shared'));
178
+ // Create per-platform directories: backend services under backend/, frontend under frontend/
179
+ for (const platform of taskPlatforms) {
180
+ if (platform.startsWith('后台') || platform === 'backend') {
181
+ // Backend service: e.g., 后台管理端 → backend/管理端
182
+ const service = platform.replace(/^后台/, '').trim() || 'default';
183
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', service || platform));
184
+ }
185
+ else {
186
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'frontend', platform));
187
+ }
188
+ }
189
+ // Always create a common backend directory for shared backend specs
190
+ if (!taskPlatforms.some(p => p.startsWith('后台'))) {
191
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend'));
192
+ }
92
193
  // Write task type
93
194
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.task-type'), 'feature');
195
+ // Write TEST.md — auto-generated test outline
196
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', 'TEST.md'), generateTestOutline(section));
197
+ // Write REVIEW.md — auto-generated code review checklist
198
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', 'REVIEW.md'), generateReviewChecklist(section));
199
+ // Write SCHEMA.md — DB schema template (only if DB content detected)
200
+ if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
201
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', 'SCHEMA.md'), generateSchemaTemplate(section));
202
+ }
203
+ // Write DEPLOY.md — deployment checklist
204
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', 'DEPLOY.md'), generateDeployChecklist(section));
94
205
  // Write REQ.md
95
206
  await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', 'REQ.md'), `# ${section.name}
96
207
 
@@ -138,13 +249,31 @@ ${section.content}
138
249
  | TECH.md | ✅ | ./TECH.md |
139
250
  | TASK.md | ✅ | ./TASK.md |
140
251
  `);
141
- // Copy to frontend
252
+ // Copy to each platform directory (backend services + frontend platforms)
142
253
  const reqContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, 'backend', 'REQ.md'), 'utf-8');
143
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', 'REQ.md'), reqContent);
144
254
  const techContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, 'backend', 'TECH.md'), 'utf-8');
145
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', 'TECH.md'), techContent);
146
255
  const taskContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, 'backend', 'TASK.md'), 'utf-8');
147
- await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', 'TASK.md'), taskContent);
256
+ const testContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, 'backend', 'TEST.md'), 'utf-8');
257
+ const reviewContent = await (0, fs_extra_1.readFile)((0, path_1.join)(taskDir, 'backend', 'REVIEW.md'), 'utf-8');
258
+ for (const platform of taskPlatforms) {
259
+ if (platform.startsWith('后台') || platform === 'backend') {
260
+ const service = platform.replace(/^后台/, '').trim() || platform;
261
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'backend', service));
262
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', service, 'REQ.md'), reqContent);
263
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', service, 'TECH.md'), techContent);
264
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', service, 'TASK.md'), taskContent);
265
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', service, 'TEST.md'), testContent);
266
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'backend', service, 'REVIEW.md'), reviewContent);
267
+ }
268
+ else {
269
+ await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, 'frontend', platform));
270
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', platform, 'REQ.md'), reqContent);
271
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', platform, 'TECH.md'), techContent);
272
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', platform, 'TASK.md'), taskContent);
273
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', platform, 'TEST.md'), testContent);
274
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'frontend', platform, 'REVIEW.md'), reviewContent);
275
+ }
276
+ }
148
277
  }
149
278
  async function updateProjectGraph(iterationDir, sections) {
150
279
  const graphPath = (0, path_1.join)(iterationDir, '00-期次总览', 'PROJECT_GRAPH.md');
@@ -153,7 +282,7 @@ async function updateProjectGraph(iterationDir, sections) {
153
282
  content = await (0, fs_extra_1.readFile)(graphPath, 'utf-8');
154
283
  }
155
284
  for (let i = 0; i < sections.length; i++) {
156
- const taskId = `Task-${String(i + 1).padStart(3, '0')}`;
285
+ const { id: taskId } = await (0, global_counters_1.nextTaskId)(sections[i].name);
157
286
  const taskName = sections[i].name;
158
287
  if (!content.includes(taskId)) {
159
288
  const taskEntry = `| ${taskId} | ${taskName} | feature | 0% | 🔲 待开发 | |\n`;
@@ -162,4 +291,356 @@ async function updateProjectGraph(iterationDir, sections) {
162
291
  }
163
292
  await (0, fs_extra_1.writeFile)(graphPath, content);
164
293
  }
294
+ /**
295
+ * 根据需求内容自动生成测试用例框架
296
+ */
297
+ function generateTestOutline(section) {
298
+ const name = section.name;
299
+ const content = section.content || '';
300
+ const isBackend = section.platform?.startsWith('后台') || false;
301
+ const hasApi = content.includes('/api/') || content.includes('接口');
302
+ const hasDb = content.includes('数据表') || content.includes('数据库') || content.includes('表');
303
+ let outline = `# ${name} — 测试用例\n\n`;
304
+ outline += `> 自动生成于 split,请在编码后补充具体用例\n\n`;
305
+ outline += `## 1. 单元测试\n\n`;
306
+ if (isBackend && hasApi) {
307
+ outline += `| 用例 | 接口 | 输入 | 预期 | 状态 |\n`;
308
+ outline += `| :--- | :--- | :--- | :--- | :--- |\n`;
309
+ outline += `| 正常请求 | | | 200 | ⬜ |\n`;
310
+ outline += `| 参数校验 | | | 400 | ⬜ |\n`;
311
+ outline += `| 未授权 | | | 401 | ⬜ |\n`;
312
+ }
313
+ else {
314
+ outline += `| 用例 | 场景 | 输入 | 预期 | 状态 |\n`;
315
+ outline += `| :--- | :--- | :--- | :--- | :--- |\n`;
316
+ outline += `| 正常渲染 | 默认 | | | ⬜ |\n`;
317
+ outline += `| 空数据 | 无数据 | | | ⬜ |\n`;
318
+ }
319
+ if (hasDb) {
320
+ outline += `\n## 2. 数据库测试\n\n`;
321
+ outline += `| 用例 | 表 | 操作 | 预期 | 状态 |\n`;
322
+ outline += `| :--- | :--- | :--- | :--- | :--- |\n`;
323
+ outline += `| 事务回滚 | | INSERT/UPDATE | 异常时回滚 | ⬜ |\n`;
324
+ outline += `| 唯一约束 | | INSERT 重复 | 约束冲突 | ⬜ |\n`;
325
+ }
326
+ outline += `\n## 3. 集成测试 / E2E\n\n`;
327
+ outline += `| 用例 | 流程 | 预期 | 状态 |\n`;
328
+ outline += `| :--- | :--- | :--- | :--- |\n`;
329
+ outline += `| 正常流程 | 从头到尾走通 | 成功 | ⬜ |\n`;
330
+ outline += `| 异常流程 | 中断/超时 | 优雅降级 | ⬜ |\n`;
331
+ outline += `| 并发 | 多用户同时操作 | 无数据错乱 | ⬜ |\n`;
332
+ outline += `\n## 4. 性能 / 安全\n\n`;
333
+ outline += `| 用例 | 指标 | 阈值 | 状态 |\n`;
334
+ outline += `| :--- | :--- | :--- | :--- |\n`;
335
+ outline += `| 响应时间 | P99 | < 500ms | ⬜ |\n`;
336
+ outline += `| 并发容量 | QPS | 满足预期 | ⬜ |\n`;
337
+ outline += `\n> ⬜ 待编写 | ✅ 通过 | ❌ 失败 | ➖ 不适用\n`;
338
+ return outline;
339
+ }
340
+ /**
341
+ * 根据需求内容自动生成代码审查清单
342
+ */
343
+ function generateReviewChecklist(section) {
344
+ const name = section.name;
345
+ const content = section.content || '';
346
+ const hasApi = content.includes('/api/') || content.includes('接口');
347
+ const hasDb = content.includes('数据库') || content.includes('表');
348
+ const hasBatch = content.includes('批量') || content.includes('导出');
349
+ const hasAuth = content.includes('权限') || content.includes('角色') || content.includes('认证');
350
+ const isBackend = section.platform?.startsWith('后台') || false;
351
+ let checklist = `# ${name} — Code Review Checklist\n\n`;
352
+ checklist += `> 自动生成于 split,请在提交 PR 前逐项确认\n\n`;
353
+ checklist += `## 功能正确性\n\n`;
354
+ checklist += `- [ ] 需求覆盖完整,无遗漏\n`;
355
+ checklist += `- [ ] 边界条件处理(空值、极值、特殊字符)\n`;
356
+ checklist += `- [ ] 错误码统一\n\n`;
357
+ checklist += `## 代码质量\n\n`;
358
+ checklist += `- [ ] 零 ` + '`any`' + ` 类型\n`;
359
+ checklist += `- [ ] 无 console.log 残留\n`;
360
+ checklist += `- [ ] 命名清晰、见名知义\n`;
361
+ checklist += `- [ ] 无重复代码(>3 次提取为函数)\n\n`;
362
+ if (isBackend) {
363
+ checklist += `## 后端专项\n\n`;
364
+ checklist += `- [ ] 接口幂等性\n`;
365
+ checklist += `- [ ] 参数校验(@Valid / DTO)\n`;
366
+ checklist += `- [ ] 防 SQL 注入\n`;
367
+ checklist += `- [ ] 日志脱敏(密码/手机号不打日志)\n`;
368
+ if (hasDb) {
369
+ checklist += `- [ ] 数据库事务边界正确\n`;
370
+ checklist += `- [ ] 索引是否匹配查询条件\n`;
371
+ }
372
+ if (hasBatch) {
373
+ checklist += `- [ ] 批量操作有上限限制\n`;
374
+ checklist += `- [ ] 大数据量分页处理\n`;
375
+ }
376
+ if (hasAuth) {
377
+ checklist += `- [ ] 权限校验在每个接口入口(不是中间件漏掉)\n`;
378
+ }
379
+ checklist += `\n`;
380
+ }
381
+ else {
382
+ checklist += `## 前端专项\n\n`;
383
+ checklist += `- [ ] 组件拆分合理(>200 行考虑拆分)\n`;
384
+ checklist += `- [ ] 无 XSS 漏洞(v-html 审查)\n`;
385
+ checklist += `- [ ] 响应式适配\n`;
386
+ checklist += `- [ ] 加载态 / 空态 / 错误态 / 边界态(四态齐全)\n\n`;
387
+ }
388
+ checklist += `## 测试\n\n`;
389
+ checklist += `- [ ] 核心路径有单元测试\n`;
390
+ checklist += `- [ ] 参照 \`TEST.md\` 逐项验证\n`;
391
+ checklist += `- [ ] \`speccore validate --task=${name}\` 通过\n\n`;
392
+ checklist += `## 自查确认\n\n`;
393
+ checklist += `- [ ] 已在本地完整跑通\n`;
394
+ checklist += `- [ ] 相关的 \`REQ.md\` 已更新(如有变化)\n`;
395
+ checklist += `- [ ] PR 描述写清楚了「做了什么 + 怎么测」\n`;
396
+ return checklist;
397
+ }
398
+ /**
399
+ * 严格模式:预览拆分方案,逐 section 确认
400
+ */
401
+ async function strictSplitPreview(sections, platforms, iterationDir) {
402
+ const ask = (q) => {
403
+ process.stdout.write(q);
404
+ return new Promise((resolve) => {
405
+ process.stdin.resume();
406
+ process.stdin.once('data', (data) => {
407
+ process.stdin.pause();
408
+ resolve(data.toString().split('\n')[0].trim());
409
+ });
410
+ });
411
+ };
412
+ logger_1.logger.info('\n╔══════════════════════════════════════════╗');
413
+ logger_1.logger.info('║ 🔍 Strict Split — 预览拆分方案 ║');
414
+ logger_1.logger.info('╚══════════════════════════════════════════╝\n');
415
+ logger_1.logger.info(`检测到 ${sections.length} 个章节,${platforms.length} 个端: ${platforms.join(', ')}\n`);
416
+ const approved = [];
417
+ for (let i = 0; i < sections.length; i++) {
418
+ const s = sections[i];
419
+ const taskId = `Task-${String(i + 1).padStart(3, '0')}`;
420
+ // Determine target directory
421
+ const target = s.platform
422
+ ? (s.platform.startsWith('后台') ? `backend/${s.platform.replace(/^后台/, '')}` : `frontend/${s.platform}`)
423
+ : platforms.join(' + ');
424
+ logger_1.logger.info(`── ${taskId}: ${s.name} ──`);
425
+ logger_1.logger.info(` 端: ${target}`);
426
+ logger_1.logger.info(` 内容: ${(s.content || '').slice(0, 60).replace(/\n/g, ' ')}...`);
427
+ const answer = (await ask(` → 保留?[y]确认 [e]编辑名称 [a]分配 [N]跳过 [q]取消: `)).toLowerCase();
428
+ if (answer === 'q') {
429
+ logger_1.logger.info(' ❌ 取消全部\n');
430
+ approved.length = 0;
431
+ break;
432
+ }
433
+ if (answer === 'a') {
434
+ const owner = await ask(` → 分配给谁?(如需要多端,用逗号分隔: 张三(后台),李四(Web)): `);
435
+ if (owner) {
436
+ // Store owner info for later use
437
+ s._owner = owner;
438
+ logger_1.logger.info(` 👤 负责人: ${owner}`);
439
+ }
440
+ approved.push(s);
441
+ logger_1.logger.info(` ✅ 保留`);
442
+ }
443
+ else if (answer === 'e') {
444
+ const newName = await ask(` → 新名称: `);
445
+ if (newName) {
446
+ s.name = newName;
447
+ logger_1.logger.info(` 📝 已改名: ${newName}`);
448
+ }
449
+ approved.push(s);
450
+ }
451
+ else if (answer === 'y' || answer === 'yes') {
452
+ approved.push(s);
453
+ logger_1.logger.info(` ✅ 保留`);
454
+ }
455
+ else {
456
+ logger_1.logger.info(` ⏭️ 跳过`);
457
+ }
458
+ logger_1.logger.info('');
459
+ }
460
+ if (approved.length === 0)
461
+ return [];
462
+ logger_1.logger.info(`\n 将创建 ${approved.length}/${sections.length} 个任务`);
463
+ const confirm = await ask(' 确认创建?[y/N] ');
464
+ logger_1.logger.info('\n✅ 确认创建...\n');
465
+ (0, next_steps_1.showNextSteps)('split');
466
+ return approved;
467
+ }
468
+ /**
469
+ * 生成任务间影响关系图 + 风险评分
470
+ */
471
+ async function generateImpactGraph(iterationDir, sections, platforms) {
472
+ const deps = [];
473
+ const sectionApis = sections.map((s, i) => {
474
+ const taskId = `Task-${String(i + 1).padStart(3, '0')}`;
475
+ const apis = (s.content.match(/\/api\/[a-zA-Z0-9\/-]+/g) || []).map(a => a.trim());
476
+ return { name: taskId, apis };
477
+ });
478
+ for (let i = 0; i < sectionApis.length; i++) {
479
+ for (let j = 0; j < sectionApis.length; j++) {
480
+ if (i === j)
481
+ continue;
482
+ for (const api of sectionApis[j].apis) {
483
+ if (sections[i].content.includes(api)) {
484
+ deps.push({ from: sectionApis[i].name, fromName: sections[i].name, to: sectionApis[j].name, toName: sections[j].name, reason: api });
485
+ break;
486
+ }
487
+ }
488
+ }
489
+ }
490
+ const seen = new Set();
491
+ const uniqueDeps = deps.filter(d => { const k = d.from + d.to; if (seen.has(k))
492
+ return false; seen.add(k); return true; });
493
+ let impact = '# IMPACT.md\n\n> auto-generated by split\n\n## Risk Scores\n\n| Task | Risk | Score | Tags | Reasons |\n| :--- | :--- | ---: | :--- | :--- |\n';
494
+ for (let i = 0; i < sections.length; i++) {
495
+ const s = sections[i];
496
+ const taskId = `Task-${String(i + 1).padStart(3, '0')}`;
497
+ const risk = (0, risk_scorer_1.scoreRisk)(s.content + s.name, s.name);
498
+ impact += `| ${taskId}: ${s.name} | ${risk.level} | ${risk.score} | ${risk.tags.join(' ')} | ${risk.reasons.join('; ')} |\n`;
499
+ const taskDir = (0, path_1.join)(iterationDir, taskId);
500
+ if (await (0, fs_extra_1.pathExists)(taskDir)) {
501
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.risk'), `# Risk: ${risk.level}\nscore: ${risk.score}\ntags: ${risk.tags.join(',')}\n`);
502
+ }
503
+ }
504
+ impact += '\n## Dependencies\n\n';
505
+ if (uniqueDeps.length > 0) {
506
+ impact += '| Consumer | -> | Producer | API |\n| :--- | :---: | :--- | :--- |\n';
507
+ for (const d of uniqueDeps)
508
+ impact += `| ${d.from}: ${d.fromName} | -> | ${d.to}: ${d.toName} | \`${d.reason}\` |\n`;
509
+ impact += '\n> Consumer tasks must wait for Producer tasks, or pre-define API contracts.\n';
510
+ }
511
+ else {
512
+ impact += 'No task dependencies detected — all tasks can be developed in parallel.\n';
513
+ }
514
+ await (0, fs_extra_1.writeFile)((0, path_1.join)(iterationDir, 'IMPACT.md'), impact);
515
+ logger_1.logger.info(`\nImpact analysis: ${iterationDir}/IMPACT.md`);
516
+ }
517
+ function generateSchemaTemplate(section) {
518
+ const name = section.name;
519
+ return `# ${name} — Database Schema
520
+
521
+ > Auto-generated. Fill in DDL before development.
522
+
523
+ ## Tables
524
+
525
+ | Table | Purpose | Engine | Charset |
526
+ | :--- | :--- | :--- | :--- |
527
+ | | | InnoDB | utf8mb4 |
528
+
529
+ ## DDL
530
+
531
+ \`\`\`sql
532
+ -- TODO: Write CREATE TABLE statements
533
+
534
+ \`\`\`
535
+
536
+ ## Indexes
537
+
538
+ | Table | Index | Columns | Type |
539
+ | :--- | :--- | :--- | :--- |
540
+ | | | | BTREE |
541
+
542
+ ## Migration Plan
543
+
544
+ - [ ] Dev: Write DDL in local
545
+ - [ ] Review: DBA reviews schema changes
546
+ - [ ] Stage: Run migration on staging
547
+ - [ ] Prod: Run migration during deployment window
548
+
549
+ ## Rollback
550
+
551
+ \`\`\`sql
552
+ -- TODO: Write rollback DDL
553
+ \`\`\`
554
+ `;
555
+ }
556
+ function generateDeployChecklist(section) {
557
+ const name = section.name;
558
+ const hasDb = section.content.match(/数据库|数据表|DDL|ALTER/) !== null;
559
+ return `# ${name} — Deployment Checklist
560
+
561
+ ## Pre-Deploy
562
+
563
+ - [ ] All tests pass (\`speccore lifecycle --task=${name} --check\`)
564
+ - [ ] Code review approved (REVIEW.md all checked)
565
+ - [ ] PR merged to main
566
+ - [ ] CI/CD pipeline green
567
+
568
+ ${hasDb ? '- [ ] DB migration script ready and reviewed\n- [ ] DB backup taken before migration\n' : ''}
569
+ ## Deploy Steps
570
+
571
+ 1. [ ] Merge to release branch
572
+ 2. [ ] Tag version: \`git tag vX.Y.Z\`
573
+ 3. [ ] Deploy to staging
574
+ 4. [ ] Smoke test on staging
575
+ 5. [ ] Deploy to production
576
+ ${hasDb ? '6. [ ] Run DB migration\n7. [ ] Verify data integrity\n' : ''}
577
+ ## Post-Deploy
578
+
579
+ - [ ] Monitor error logs (first 30 min)
580
+ - [ ] Monitor performance metrics
581
+ - [ ] Run \`speccore archive --task=${name}\`
582
+
583
+ ## Rollback Plan
584
+
585
+ - [ ] \`git revert\` the merge commit
586
+ ${hasDb ? '- [ ] Run rollback DDL from SCHEMA.md\n' : ''}- [ ] Notify team on rollback
587
+ `;
588
+ }
589
+ async function generateEnvExample(iterationDir, sections) {
590
+ const envPath = (0, path_1.join)(iterationDir, '.env.example');
591
+ let env = '# Environment Variables — ' + iterationDir + '\n';
592
+ env += '# Copy to .env and fill in values\n\n';
593
+ const needs = new Set();
594
+ for (const s of sections) {
595
+ const c = s.content + s.name;
596
+ if (c.match(/Redis|缓存/))
597
+ needs.add('REDIS_URL=redis://localhost:6379');
598
+ if (c.match(/Kafka|MQ|消息队列/))
599
+ needs.add('KAFKA_BROKERS=localhost:9092');
600
+ if (c.match(/MySQL|数据库|JDBC|数据表/))
601
+ needs.add('DB_URL=jdbc:mysql://localhost:3306/db\nDB_USER=root\nDB_PASS=');
602
+ if (c.match(/OSS|对象存储|S3|文件上传/))
603
+ needs.add('OSS_ENDPOINT=https://oss.example.com\nOSS_KEY=\nOSS_SECRET=');
604
+ if (c.match(/支付|微信|支付宝|wechat|alipay/))
605
+ needs.add('PAYMENT_API_KEY=\nPAYMENT_SECRET=');
606
+ if (c.match(/短信|SMS|验证码/))
607
+ needs.add('SMS_API_KEY=\nSMS_SECRET=');
608
+ if (c.match(/邮件|email|smtp/))
609
+ needs.add('SMTP_HOST=smtp.example.com\nSMTP_PORT=587\nSMTP_USER=\nSMTP_PASS=');
610
+ if (c.match(/token|JWT|OAuth|鉴权|登录/))
611
+ needs.add('JWT_SECRET=\nTOKEN_EXPIRE=3600');
612
+ }
613
+ if (needs.size === 0) {
614
+ needs.add('# No extra environment variables detected.');
615
+ needs.add('# Add required variables here.');
616
+ }
617
+ env += [...needs].join('\n') + '\n';
618
+ await (0, fs_extra_1.writeFile)(envPath, env);
619
+ logger_1.logger.info(`Env example: ${iterationDir}/.env.example`);
620
+ }
621
+ async function injectTechFromAnalysis(iterationDir, taskDir, sectionName) {
622
+ const analysisPath = (0, path_1.join)(iterationDir, '00-需求文档', 'ANALYSIS.md');
623
+ if (!(await (0, fs_extra_1.pathExists)(analysisPath)))
624
+ return;
625
+ const analysis = await (0, fs_extra_1.readFile)(analysisPath, 'utf-8');
626
+ // Extract relevant tech stack section
627
+ const techSection = analysis.match(/### 技术选型[\s\S]*?(?=###|$)/);
628
+ const dbSection = analysis.match(/### 数据库变更[\s\S]*?(?=###|$)/);
629
+ const depSection = analysis.match(/### 接口依赖[\s\S]*?(?=###|$)/);
630
+ if (!techSection && !dbSection && !depSection)
631
+ return;
632
+ const techPath = (0, path_1.join)(taskDir, 'backend', 'TECH.md');
633
+ let tech = await (0, fs_extra_1.readFile)(techPath, 'utf-8');
634
+ const note = '\n\n> 以下内容自动从 ANALYSIS.md 注入\n\n';
635
+ if (techSection && !tech.includes(techSection[0].trim())) {
636
+ tech += note + techSection[0].trim() + '\n';
637
+ }
638
+ if (dbSection && !tech.includes(dbSection[0].trim())) {
639
+ tech += dbSection[0].trim() + '\n';
640
+ }
641
+ if (depSection && !tech.includes(depSection[0].trim())) {
642
+ tech += depSection[0].trim() + '\n';
643
+ }
644
+ await (0, fs_extra_1.writeFile)(techPath, tech);
645
+ }
165
646
  //# sourceMappingURL=split.js.map