speccore 6.18.4 → 6.31.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.
- package/.agents/skills/spec-execute/SKILL.md +7 -3
- package/dist/cli.js +1 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/analyze.d.ts +1 -0
- package/dist/commands/analyze.d.ts.map +1 -1
- package/dist/commands/analyze.js +234 -47
- package/dist/commands/analyze.js.map +1 -1
- package/dist/commands/context-output.d.ts.map +1 -1
- package/dist/commands/context-output.js +19 -10
- package/dist/commands/context-output.js.map +1 -1
- package/dist/commands/doc2spec.js +1 -1
- package/dist/commands/doc2spec.js.map +1 -1
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +373 -91
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/global-status.js +2 -2
- package/dist/commands/global-status.js.map +1 -1
- package/dist/commands/import.d.ts +1 -1
- package/dist/commands/import.js +2 -4
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +59 -296
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/iteration/create.js +7 -10
- package/dist/commands/iteration/create.js.map +1 -1
- package/dist/commands/iteration/split.js +296 -131
- package/dist/commands/iteration/split.js.map +1 -1
- package/dist/commands/iteration-from-global.js +1 -1
- package/dist/commands/retro.d.ts.map +1 -1
- package/dist/commands/retro.js +60 -3
- package/dist/commands/retro.js.map +1 -1
- package/dist/commands/status-panel.d.ts.map +1 -1
- package/dist/commands/status-panel.js +24 -8
- package/dist/commands/status-panel.js.map +1 -1
- package/dist/commands/synthesize.d.ts +2 -2
- package/dist/commands/synthesize.js +146 -196
- package/dist/commands/synthesize.js.map +1 -1
- package/dist/commands/task/new.js +43 -26
- package/dist/commands/task/new.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +7 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/core/ai-context-generator.js +1 -1
- package/dist/core/analyze-engine.d.ts.map +1 -1
- package/dist/core/analyze-engine.js +307 -9
- package/dist/core/analyze-engine.js.map +1 -1
- package/dist/core/execution-state.d.ts +24 -0
- package/dist/core/execution-state.d.ts.map +1 -1
- package/dist/core/execution-state.js +135 -0
- package/dist/core/execution-state.js.map +1 -1
- package/dist/core/knowledge-graph.d.ts.map +1 -1
- package/dist/core/knowledge-graph.js +190 -65
- package/dist/core/knowledge-graph.js.map +1 -1
- package/dist/core/next-steps.js +2 -2
- package/dist/core/next-steps.js.map +1 -1
- package/dist/core/prompt-builder.d.ts.map +1 -1
- package/dist/core/prompt-builder.js +43 -22
- package/dist/core/prompt-builder.js.map +1 -1
- package/dist/core/quality-audit.d.ts +41 -0
- package/dist/core/quality-audit.d.ts.map +1 -0
- package/dist/core/quality-audit.js +345 -0
- package/dist/core/quality-audit.js.map +1 -0
- package/dist/core/rag-engine.d.ts.map +1 -1
- package/dist/core/rag-engine.js +38 -6
- package/dist/core/rag-engine.js.map +1 -1
- package/dist/core/spec-merger.d.ts.map +1 -1
- package/dist/core/spec-merger.js +22 -27
- package/dist/core/spec-merger.js.map +1 -1
- package/dist/core/validator.d.ts.map +1 -1
- package/dist/core/validator.js +43 -0
- package/dist/core/validator.js.map +1 -1
- package/dist/core/verify-engine.d.ts +1 -1
- package/dist/core/verify-engine.d.ts.map +1 -1
- package/dist/core/verify-engine.js +71 -8
- package/dist/core/verify-engine.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,7 +44,6 @@ const task_utils_1 = require("../../utils/task-utils");
|
|
|
44
44
|
const next_steps_1 = require("../../core/next-steps");
|
|
45
45
|
const readline_1 = require("readline");
|
|
46
46
|
const prompt_builder_1 = require("../../core/prompt-builder");
|
|
47
|
-
const platform_registry_1 = require("../../core/platform-registry");
|
|
48
47
|
const index_guard_1 = require("../../core/index-guard");
|
|
49
48
|
/** 将名称转为目录安全的短 slug(2-4 词) */
|
|
50
49
|
function slugify(name) {
|
|
@@ -525,7 +524,7 @@ async function iterationSplitCommand(options) {
|
|
|
525
524
|
const specDir2 = (0, path_1.join)(iterationDir, '020-specs');
|
|
526
525
|
const specContents = [];
|
|
527
526
|
// 读取全局层扁平文件(迭代级跨端综合)
|
|
528
|
-
for (const f of ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md']) {
|
|
527
|
+
for (const f of ['ANALYSIS.md', 'TECH.md', 'TEST.md', 'REVIEW.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'UI_SPEC.md']) {
|
|
529
528
|
const fp = (0, path_1.join)(specDir2, f);
|
|
530
529
|
if (await (0, fs_extra_1.pathExists)(fp)) {
|
|
531
530
|
const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
|
|
@@ -891,58 +890,55 @@ async function createTaskFromSection(iterationDir, taskId, section, allPlatforms
|
|
|
891
890
|
for (const p of taskPlatforms) {
|
|
892
891
|
subtaskIdMap.set(p, generateSubtaskId(taskNum, p));
|
|
893
892
|
}
|
|
894
|
-
// ── 1.
|
|
895
|
-
|
|
896
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.
|
|
897
|
-
|
|
898
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'owner'), owner);
|
|
899
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'created-at'), today);
|
|
900
|
-
// ── 1a. Git 配置模板(默认继承迭代级配置) ──
|
|
901
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.meta', 'git-config'), `# 任务级 Git 配置
|
|
902
|
-
# 以下配置覆盖迭代级 PROJECT_GRAPH.md,未配置项自动继承上一级。
|
|
903
|
-
# 修改时去掉注释符 #,填入具体值即可。
|
|
893
|
+
// ── 1. 任务目录指引(功能模块分组) ──
|
|
894
|
+
const isResearch = taskType === 'research';
|
|
895
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'README.md'), isResearch
|
|
896
|
+
? `# ${section.name}
|
|
904
897
|
|
|
905
|
-
|
|
906
|
-
# 分支前缀: 继承迭代配置
|
|
907
|
-
# 分支格式: 继承迭代配置
|
|
908
|
-
# 自动拉取: 继承迭代配置
|
|
909
|
-
# 远程名称: 继承迭代配置
|
|
910
|
-
`);
|
|
911
|
-
// ── 2. 任务目录指引 ──
|
|
912
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'README.md'), `# ${section.name}
|
|
913
|
-
|
|
914
|
-
> 任务模块分组 — 按功能单元组织,各端并行开发
|
|
898
|
+
> 技术调研任务 — 产出调研文档,不产出代码
|
|
915
899
|
|
|
916
900
|
## 目录结构
|
|
917
901
|
|
|
918
902
|
\`\`\`
|
|
919
903
|
${taskId}/
|
|
920
|
-
├── README.md
|
|
921
|
-
├──
|
|
922
|
-
├──
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
│ ├── SCHEMA.md <-- 数据库设计(如有)
|
|
927
|
-
│ ├── API_CONTRACT.yaml <-- API 契约
|
|
928
|
-
│ └── CHANGELOG.md <-- 变更记录
|
|
929
|
-
├── {端名}/ <-- 各端独立子任务(backend/admin/web/h5/...)
|
|
930
|
-
│ ├── TASK.md <-- 子任务追踪(含子任务表、负责人、状态)
|
|
931
|
-
│ ├── src/ <-- 源码实现
|
|
932
|
-
│ └── tests/ <-- 测试用例
|
|
933
|
-
├── 99-artifacts/ <-- 执行产出
|
|
934
|
-
│ ├── TEST.md <-- 测试用例
|
|
935
|
-
│ ├── REVIEW.md <-- 评审清单
|
|
936
|
-
│ ├── DEPLOY.md <-- 部署清单
|
|
937
|
-
│ └── ...
|
|
938
|
-
└── .issues.md <-- 问题追踪
|
|
904
|
+
├── README.md ← 本文件(调研任务说明)
|
|
905
|
+
├── _shared/ ← 共享上下文(CONTEXT.md)
|
|
906
|
+
├── 00-specs/ ← 核心规格(REQ.md/TECH.md)
|
|
907
|
+
├── RESEARCH.md ← 调研报告
|
|
908
|
+
├── COMPARISON.md ← 方案对比
|
|
909
|
+
└── .issues.md ← 问题追踪
|
|
939
910
|
\`\`\`
|
|
940
911
|
|
|
941
|
-
##
|
|
912
|
+
## AI 执行时读取规则
|
|
913
|
+
|
|
914
|
+
运行 \`speccore execute -t ${taskId}\` 时:
|
|
915
|
+
- 读取 \`00-specs/REQ.md\` — 调研目标
|
|
916
|
+
- 读取 \`_shared/CONTEXT.md\` — 任务上下文
|
|
917
|
+
- 产出写入 \`RESEARCH.md\` 和 \`COMPARISON.md\`
|
|
918
|
+
`
|
|
919
|
+
: `# ${section.name}
|
|
942
920
|
|
|
943
|
-
|
|
921
|
+
> 功能模块分组 — 聚合相关子任务,共享规格与契约
|
|
944
922
|
|
|
945
|
-
|
|
923
|
+
## 目录结构
|
|
924
|
+
|
|
925
|
+
\`\`\`
|
|
926
|
+
${taskId}/
|
|
927
|
+
├── README.md ← 本文件
|
|
928
|
+
├── _shared/ ← 共享契约(API_CONTRACT.yaml + CONTEXT.md)
|
|
929
|
+
├── 00-specs/ ← 模块级核心规格(REQ/TECH/SCHEMA/CHANGELOG)
|
|
930
|
+
├── 10-backend/ ← 后端
|
|
931
|
+
│ └── {服务名}/ ← 端(如 api)
|
|
932
|
+
│ └── {子任务}/ ← 执行单元(.meta/TASK.md/src/tests + 产出)
|
|
933
|
+
├── 20-frontend/ ← 前端
|
|
934
|
+
│ └── {端名}/ ← 端(如 h5/admin)
|
|
935
|
+
│ └── {子任务}/ ← 执行单元(.meta/TASK.md/src/tests + 前端设计 + 产出)
|
|
936
|
+
└── .issues.md ← 问题追踪
|
|
937
|
+
\`\`\`
|
|
938
|
+
|
|
939
|
+
## 子任务列表
|
|
940
|
+
|
|
941
|
+
| 子任务 ID | 所属端/服务 | 负责人 | 状态 |
|
|
946
942
|
| :--- | :--- | :--- | :--- |
|
|
947
943
|
${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲 待开发 |`).join('\n')}
|
|
948
944
|
|
|
@@ -951,35 +947,37 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
951
947
|
运行 \`speccore execute -t ${taskId} --platform {端}\` 时:
|
|
952
948
|
|
|
953
949
|
### 必读(自动嵌入)
|
|
950
|
+
- \`00-specs/REQ.md\` — 模块需求描述
|
|
951
|
+
- \`00-specs/TECH.md\` — 模块技术方案
|
|
954
952
|
- \`_shared/CONTEXT.md\` — 任务上下文(来源 + 关联)
|
|
955
|
-
- \`_shared/
|
|
956
|
-
- \`
|
|
957
|
-
- \`
|
|
953
|
+
- \`_shared/API_CONTRACT.yaml\` — API 契约
|
|
954
|
+
- \`10-backend/{端}/{子任务}/TASK.md\` — 子任务详情
|
|
955
|
+
- \`.meta/type\` + \`.meta/status\` — 子任务元信息
|
|
958
956
|
|
|
959
957
|
### 参考(按需读取)
|
|
960
|
-
- \`020-specs/\`
|
|
958
|
+
- \`020-specs/\` 下的迭代全局文档
|
|
961
959
|
- \`.speccore/GLOBAL/\` 下的全局知识库
|
|
962
960
|
|
|
963
961
|
### 不会被读取
|
|
964
|
-
- \`{
|
|
965
|
-
- \`
|
|
962
|
+
- \`{子任务}/src/\` 和 \`{子任务}/tests/\` — AI **输出**代码的地方
|
|
963
|
+
- \`{子任务}/TEST.md\` 等执行产出 — 执行完成后自动更新
|
|
966
964
|
`);
|
|
967
|
-
// ── 3. 共享契约 ──
|
|
965
|
+
// ── 3. 共享契约 + 核心规格目录 ──
|
|
968
966
|
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '_shared'));
|
|
967
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '00-specs'));
|
|
969
968
|
const contractYaml = generateApiContract(section);
|
|
970
969
|
if (contractYaml) {
|
|
971
970
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml'), contractYaml);
|
|
972
971
|
}
|
|
973
|
-
// ──
|
|
974
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '_shared'));
|
|
972
|
+
// ── 4. 核心规格写入 00-specs/(REQ/TECH/SCHEMA/CHANGELOG) ──
|
|
975
973
|
const acItems = generateAcceptanceCriteria(section);
|
|
976
974
|
const aiReqContent = section._reqContent;
|
|
977
975
|
// REQ.md: 优先用 AI 生成的实际内容,回退到模板
|
|
978
976
|
if (aiReqContent && aiReqContent.length > 50) {
|
|
979
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
977
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n${aiReqContent}\n\n## 验收标准\n\n${acItems}\n`);
|
|
980
978
|
}
|
|
981
979
|
else {
|
|
982
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
980
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'REQ.md'), `# ${section.name}\n\n## 需求描述\n\n${section.content}\n\n## 验收标准\n\n${acItems}\n`);
|
|
983
981
|
}
|
|
984
982
|
const apiLines = section.content.split('\n').filter(l => l.includes('| GET') || l.includes('| POST') || l.includes('| PUT') || l.includes('| DELETE') || l.includes('| PATCH'));
|
|
985
983
|
const apiDesc = apiLines.length > 0 ? apiLines.map(l => `- ${l.trim()}`).join('\n') : '- 待补充(从 REQ.md 提取接口列表)';
|
|
@@ -988,13 +986,13 @@ ${taskPlatforms.map((p) => `| ${subtaskIdMap.get(p)} | ${p} | ${owner} | 🔲
|
|
|
988
986
|
const specTechContent = extractTaskTechContent(specContents, section);
|
|
989
987
|
// TECH.md: 优先 AI 生成 → 回退 analyze 提取 → 回退模板
|
|
990
988
|
if (aiTechContent && aiTechContent.length > 50) {
|
|
991
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
989
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n${aiTechContent}\n`);
|
|
992
990
|
}
|
|
993
991
|
else if (specTechContent && specTechContent.length > 30) {
|
|
994
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
992
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案\n\n> 来源: analyze → TECH.md(自动提取)\n\n${specTechContent}\n`);
|
|
995
993
|
}
|
|
996
994
|
else {
|
|
997
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
995
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'TECH.md'), `# ${section.name} - 技术方案
|
|
998
996
|
|
|
999
997
|
> ⚠️ 本文档由 split 自动生成框架,AI 执行时会自动填充。
|
|
1000
998
|
|
|
@@ -1020,13 +1018,24 @@ ${apiDesc}
|
|
|
1020
1018
|
- 单元测试覆盖核心 Service 逻辑
|
|
1021
1019
|
- 接口测试覆盖正常/异常/边界
|
|
1022
1020
|
- 自动化测试通过后方可提 PR
|
|
1021
|
+
|
|
1022
|
+
## 6. 前端 UI 设计
|
|
1023
|
+
<!-- AI-FILL: 页面结构、组件清单、状态管理、路由设计 -->
|
|
1024
|
+
### 6.1 页面/路由
|
|
1025
|
+
<!-- AI-FILL: 路由表 -->
|
|
1026
|
+
|
|
1027
|
+
### 6.2 组件清单
|
|
1028
|
+
<!-- AI-FILL: 组件列表及职责 -->
|
|
1029
|
+
|
|
1030
|
+
### 6.3 状态管理
|
|
1031
|
+
<!-- AI-FILL: 状态字段及枚举值,需与后端保持一致 -->
|
|
1023
1032
|
`);
|
|
1024
1033
|
}
|
|
1025
1034
|
if (section.content.match(/数据库|数据表|表结构|DDL|ALTER|建表|索引/)) {
|
|
1026
1035
|
const schemaMaterial = extractRelevantSection(specContents['TECH.md'] || '', section.name, 'DDL 建表 表结构 索引 CREATE TABLE');
|
|
1027
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
1036
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'SCHEMA.md'), generateSchemaTemplate(section, schemaMaterial));
|
|
1028
1037
|
}
|
|
1029
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
1038
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'CHANGELOG.md'), `# ${section.name} - 变更记录
|
|
1030
1039
|
|
|
1031
1040
|
| 时间 | 版本 | 变更内容 | 变更人 |
|
|
1032
1041
|
| :--- | :--- | :--- | :--- |
|
|
@@ -1034,100 +1043,223 @@ ${apiDesc}
|
|
|
1034
1043
|
`);
|
|
1035
1044
|
// API_CONTRACT.yaml 统一放 _shared/
|
|
1036
1045
|
if (apiLines.length > 0) {
|
|
1046
|
+
// 从 section 内容提取每个 API 的请求/响应字段
|
|
1047
|
+
const sectionContent = section.content || '';
|
|
1048
|
+
const apiFieldMap = {};
|
|
1049
|
+
for (const l of apiLines) {
|
|
1050
|
+
const parts = l.split('|').map(p => p.trim()).filter(Boolean);
|
|
1051
|
+
const path = parts[1] || '/api/unknown';
|
|
1052
|
+
const apiIdx = sectionContent.indexOf(path);
|
|
1053
|
+
if (apiIdx >= 0) {
|
|
1054
|
+
const after = sectionContent.slice(apiIdx, apiIdx + 800);
|
|
1055
|
+
const fields = [];
|
|
1056
|
+
const fieldRegex = /[`']([a-zA-Z]\w{1,25})[`']\s*[((]?[::]?\s*(?:类型[::])?\s*([\u4e00-\u9fa5A-Za-z]+)/g;
|
|
1057
|
+
let fm;
|
|
1058
|
+
while ((fm = fieldRegex.exec(after)) !== null) {
|
|
1059
|
+
if (!['api', 'data', 'code', 'message', 'msg', 'status', 'ok', 'err'].includes(fm[1].toLowerCase())) {
|
|
1060
|
+
fields.push(`${fm[1]}: ${fm[2]}`);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (fields.length > 0)
|
|
1064
|
+
apiFieldMap[path] = fields.slice(0, 10);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1037
1067
|
const contracts = apiLines.map(l => {
|
|
1038
1068
|
const parts = l.split('|').map(p => p.trim()).filter(Boolean);
|
|
1039
1069
|
const method = (parts[0] || 'GET').toUpperCase();
|
|
1040
1070
|
const path = parts[1] || '/api/unknown';
|
|
1041
1071
|
const desc = parts[2] || path;
|
|
1042
|
-
|
|
1072
|
+
let yaml = ` ${path}:\n ${method}:\n summary: "${desc}"`;
|
|
1073
|
+
if (apiFieldMap[path]) {
|
|
1074
|
+
yaml += `\n fields:`;
|
|
1075
|
+
for (const f of apiFieldMap[path]) {
|
|
1076
|
+
yaml += `\n - "${f}"`;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
yaml += `\n responses:\n "200":\n description: Success`;
|
|
1080
|
+
return yaml;
|
|
1043
1081
|
}).join('\n');
|
|
1044
1082
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml'), `# ${section.name} - API Contract\n# Auto-generated from split\n\npaths:\n${contracts}\n`);
|
|
1045
1083
|
}
|
|
1046
|
-
// ── 4.
|
|
1047
|
-
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1084
|
+
// ── 4. 子任务目录(按任务类型差异化) ──
|
|
1085
|
+
// 预加载执行产出模板材料(每个子任务都需要)
|
|
1086
|
+
const testMaterial = extractRelevantSection(specContents['TEST.md'] || '', section.name);
|
|
1087
|
+
const riskMaterial = extractRelevantSection(specContents['RISK.md'] || '', section.name);
|
|
1088
|
+
const depsMaterial = extractRelevantSection(specContents['DEPS.md'] || '', section.name);
|
|
1089
|
+
const monitorMaterial = extractRelevantSection(specContents['MONITOR.md'] || '', section.name);
|
|
1090
|
+
// ── 4a. research 类型:不创建平台/子任务目录,直接产出调研文档 ──
|
|
1091
|
+
if (isResearch) {
|
|
1092
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'RESEARCH.md'), `# ${section.name} — 调研报告
|
|
1093
|
+
|
|
1094
|
+
## 调研目标
|
|
1095
|
+
${section.content}
|
|
1096
|
+
|
|
1097
|
+
## 调研结果
|
|
1098
|
+
<!-- AI-FILL: 调研发现、技术选型分析 -->
|
|
1099
|
+
|
|
1100
|
+
## 推荐方案
|
|
1101
|
+
<!-- AI-FILL: 推荐方案及理由 -->
|
|
1102
|
+
|
|
1103
|
+
## 风险与建议
|
|
1104
|
+
<!-- AI-FILL -->
|
|
1105
|
+
`);
|
|
1106
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, 'COMPARISON.md'), `# ${section.name} — 方案对比
|
|
1107
|
+
|
|
1108
|
+
| 维度 | 方案 A | 方案 B | 方案 C |
|
|
1109
|
+
|:---|:---|:---|:---|
|
|
1110
|
+
| 技术栈 | | | |
|
|
1111
|
+
| 开发成本 | | | |
|
|
1112
|
+
| 性能 | | | |
|
|
1113
|
+
| 可维护性 | | | |
|
|
1114
|
+
| 社区生态 | | | |
|
|
1115
|
+
|
|
1116
|
+
## 综合评价
|
|
1117
|
+
<!-- AI-FILL -->
|
|
1118
|
+
`);
|
|
1119
|
+
}
|
|
1120
|
+
else {
|
|
1121
|
+
// ── 4b. feature/bugfix/refactor:三级嵌套(前后端大类 → 端 → 子任务) ──
|
|
1122
|
+
// 分类平台:后端 vs 前端
|
|
1123
|
+
const isBackendPlatform = (p) => p === 'backend' || p.startsWith('后台');
|
|
1124
|
+
const getServiceName = (p) => isBackendPlatform(p) && p === 'backend' ? 'api' : p;
|
|
1125
|
+
const backendPlatforms = taskPlatforms.filter(isBackendPlatform);
|
|
1126
|
+
const frontendPlatforms = taskPlatforms.filter((p) => !isBackendPlatform(p));
|
|
1127
|
+
// 创建子任务的通用函数
|
|
1128
|
+
const createSubtask = async (subtaskDir, subtaskId, platformName, platformLabel, isBk, hours) => {
|
|
1129
|
+
// .meta/
|
|
1130
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(subtaskDir, '.meta'));
|
|
1131
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, '.meta', 'type'), taskType);
|
|
1132
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, '.meta', 'status'), 'todo');
|
|
1133
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, '.meta', 'owner'), owner);
|
|
1134
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, '.meta', 'created-at'), today);
|
|
1135
|
+
// git-config
|
|
1136
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, '.meta', 'git-config'), `# 子任务级 Git 配置(${platformLabel})
|
|
1137
|
+
# 以下配置覆盖迭代级 PROJECT_GRAPH.md,未配置项自动继承上一级。
|
|
1138
|
+
# 修改时去掉注释符 #,填入具体值即可。
|
|
1139
|
+
|
|
1140
|
+
# 源分支: 继承迭代配置
|
|
1141
|
+
# 分支前缀: 继承迭代配置
|
|
1142
|
+
# 分支格式: 继承迭代配置
|
|
1143
|
+
# 自动拉取: 继承迭代配置
|
|
1144
|
+
# 远程名称: 继承迭代配置
|
|
1145
|
+
`);
|
|
1146
|
+
// src/ + tests/
|
|
1147
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(subtaskDir, 'src'));
|
|
1148
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(subtaskDir, 'tests'));
|
|
1149
|
+
// TASK.md
|
|
1150
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'TASK.md'), `# ${section.name} — ${platformLabel}
|
|
1059
1151
|
|
|
1060
1152
|
## 子任务信息
|
|
1061
1153
|
- **子任务 ID**: \`${subtaskId}\`
|
|
1062
1154
|
- **所属模块**: \`${taskId}\`
|
|
1063
|
-
- **端**: ${
|
|
1155
|
+
- **端**: ${platformName}
|
|
1064
1156
|
- **负责人**: ${owner}
|
|
1065
|
-
- **状态**:
|
|
1066
|
-
- **预计耗时**: ${
|
|
1067
|
-
|
|
1068
|
-
## 子任务列表
|
|
1069
|
-
|
|
1070
|
-
| 子任务 ID | 名称 | 负责人 | 状态 | 预估工时 | 依赖 |
|
|
1071
|
-
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
1072
|
-
| \`${subtaskId}-01\` | ${section.name} - ${platformLabel}实现 | ${owner} | 🔲 待开发 | ${subtaskHours}h | 无 |
|
|
1157
|
+
- **状态**: 待开发
|
|
1158
|
+
- **预计耗时**: ${hours}h
|
|
1073
1159
|
|
|
1074
1160
|
## 共享规格引用
|
|
1075
|
-
- REQ.md →
|
|
1076
|
-
- TECH.md →
|
|
1161
|
+
- REQ.md → ../../../00-specs/REQ.md
|
|
1162
|
+
- TECH.md → ../../../00-specs/TECH.md
|
|
1077
1163
|
|
|
1078
1164
|
## 产出物
|
|
1079
1165
|
| 产出物 | 状态 | 路径 |
|
|
1080
1166
|
| :--- | :--- | :--- |
|
|
1081
|
-
| TASK.md | ✅ |
|
|
1082
|
-
| src/ | ⏳ |
|
|
1083
|
-
| tests/ | ⏳ |
|
|
1167
|
+
| TASK.md | ✅ | ./TASK.md |
|
|
1168
|
+
| src/ | ⏳ | ./src/ |
|
|
1169
|
+
| tests/ | ⏳ | ./tests/ |
|
|
1170
|
+
| TEST.md | ⏳ | ./TEST.md |
|
|
1171
|
+
| RISK.md | ⏳ | ./RISK.md |
|
|
1084
1172
|
|
|
1085
1173
|
## 变更履历
|
|
1086
1174
|
| 时间 | 变更内容 | 变更人 |
|
|
1087
1175
|
| :--- | :--- | :--- |
|
|
1088
1176
|
| ${today} | 创建子任务 | CLI |
|
|
1089
1177
|
`);
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1178
|
+
// 前端设计文档(仅非 backend 端)
|
|
1179
|
+
if (!isBk) {
|
|
1180
|
+
const feContent = extractTaskTechContent(specContents, section, platformName);
|
|
1181
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'COMPONENT_TREE.md'), generateComponentTree(section, platformName, feContent));
|
|
1182
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'ROUTES.md'), generateRoutesDoc(section, platformName, feContent));
|
|
1183
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'STATE.md'), generateStateDoc(section, platformName, feContent));
|
|
1184
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'STYLE_GUIDE.md'), generateStyleGuide(section, platformName, feContent));
|
|
1185
|
+
}
|
|
1186
|
+
// 执行产出文档
|
|
1187
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'TEST.md'), generateTestOutline(section, testMaterial));
|
|
1188
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'REVIEW.md'), generateReviewChecklist(section));
|
|
1189
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'DEPLOY.md'), generateDeployChecklist(section));
|
|
1190
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'ERROR_CODES.md'), generateErrorCodes(section));
|
|
1191
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'RISK.md'), generateRiskTemplate(section, riskMaterial));
|
|
1192
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'DEPS.md'), generateDepsTemplate(section, depsMaterial));
|
|
1193
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'MONITOR.md'), generateMonitorTemplate(section, monitorMaterial));
|
|
1194
|
+
const adr = generateAdr(section);
|
|
1195
|
+
if (adr) {
|
|
1196
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(subtaskDir, 'ADR.md'), adr);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
// ── 后端:10-backend/{服务名}/{子任务}/ ──
|
|
1200
|
+
if (backendPlatforms.length > 0) {
|
|
1201
|
+
const backendCategoryDir = (0, path_1.join)(taskDir, '10-backend');
|
|
1202
|
+
for (const platform of backendPlatforms) {
|
|
1203
|
+
const serviceName = getServiceName(platform);
|
|
1204
|
+
const platformDir = (0, path_1.join)(backendCategoryDir, serviceName);
|
|
1205
|
+
const subtaskId = subtaskIdMap.get(platform);
|
|
1206
|
+
const subtaskName = slugify(section.name) || 'impl';
|
|
1207
|
+
const subtaskDir = (0, path_1.join)(platformDir, subtaskName);
|
|
1208
|
+
const subtaskHours = section._hoursByPlatform?.[platform] || Math.ceil(complexity.estimatedHours / taskPlatforms.length);
|
|
1209
|
+
await createSubtask(subtaskDir, subtaskId, platform, '后端', true, subtaskHours);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
// ── 前端:20-frontend/{端名}/{子任务}/ ──
|
|
1213
|
+
if (frontendPlatforms.length > 0) {
|
|
1214
|
+
const frontendCategoryDir = (0, path_1.join)(taskDir, '20-frontend');
|
|
1215
|
+
for (const platform of frontendPlatforms) {
|
|
1216
|
+
const platformDir = (0, path_1.join)(frontendCategoryDir, platform);
|
|
1217
|
+
const subtaskId = subtaskIdMap.get(platform);
|
|
1218
|
+
const subtaskName = slugify(section.name) || 'impl';
|
|
1219
|
+
const subtaskDir = (0, path_1.join)(platformDir, subtaskName);
|
|
1220
|
+
const subtaskHours = section._hoursByPlatform?.[platform] || Math.ceil(complexity.estimatedHours / taskPlatforms.length);
|
|
1221
|
+
await createSubtask(subtaskDir, subtaskId, platform, platform, false, subtaskHours);
|
|
1222
|
+
}
|
|
1097
1223
|
}
|
|
1098
1224
|
}
|
|
1099
|
-
// 确保至少有 backend
|
|
1100
|
-
if (!taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
|
|
1101
|
-
|
|
1102
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(
|
|
1225
|
+
// 确保至少有 backend 子任务(AI 未输出 backend 时自动补充,仅非 research)
|
|
1226
|
+
if (!isResearch && !taskPlatforms.some((p) => p === 'backend' || p.startsWith('后台'))) {
|
|
1227
|
+
const autoSubtaskDir = (0, path_1.join)(taskDir, '10-backend', 'api', 'impl');
|
|
1228
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(autoSubtaskDir, '.meta'));
|
|
1229
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(autoSubtaskDir, 'src'));
|
|
1230
|
+
await (0, fs_extra_1.ensureDir)((0, path_1.join)(autoSubtaskDir, 'tests'));
|
|
1231
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, '.meta', 'type'), taskType);
|
|
1232
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, '.meta', 'status'), 'todo');
|
|
1233
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, '.meta', 'owner'), owner);
|
|
1234
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, '.meta', 'created-at'), today);
|
|
1235
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, '.meta', 'git-config'), `# 子任务级 Git 配置(后端)
|
|
1236
|
+
# 以下配置覆盖迭代级 PROJECT_GRAPH.md,未配置项自动继承上一级。
|
|
1237
|
+
`);
|
|
1238
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'TASK.md'), `# ${section.name} — 后端(自动补充)
|
|
1239
|
+
|
|
1240
|
+
## 子任务信息
|
|
1241
|
+
- **子任务 ID**: \`${taskId}-backend-auto\`
|
|
1242
|
+
- **所属模块**: \`${taskId}\`
|
|
1243
|
+
- **端**: backend
|
|
1244
|
+
- **负责人**: ${owner}
|
|
1245
|
+
- **状态**: 待开发
|
|
1246
|
+
|
|
1247
|
+
## 共享规格引用
|
|
1248
|
+
- REQ.md → ../../../00-specs/REQ.md
|
|
1249
|
+
- TECH.md → ../../../00-specs/TECH.md
|
|
1250
|
+
`);
|
|
1251
|
+
// 自动补充的后端也需要执行产出文档
|
|
1252
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'TEST.md'), generateTestOutline(section, testMaterial));
|
|
1253
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'REVIEW.md'), generateReviewChecklist(section));
|
|
1254
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'DEPLOY.md'), generateDeployChecklist(section));
|
|
1255
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'ERROR_CODES.md'), generateErrorCodes(section));
|
|
1256
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'RISK.md'), generateRiskTemplate(section, riskMaterial));
|
|
1257
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'DEPS.md'), generateDepsTemplate(section, depsMaterial));
|
|
1258
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(autoSubtaskDir, 'MONITOR.md'), generateMonitorTemplate(section, monitorMaterial));
|
|
1103
1259
|
taskPlatforms.push('backend');
|
|
1104
1260
|
subtaskIdMap.set('backend', `${taskId}-backend-auto`);
|
|
1105
1261
|
}
|
|
1106
|
-
//
|
|
1107
|
-
const platformEntries = taskPlatforms.map((p) => ({
|
|
1108
|
-
name: p,
|
|
1109
|
-
subtaskId: subtaskIdMap.get(p),
|
|
1110
|
-
owner,
|
|
1111
|
-
}));
|
|
1112
|
-
await (0, platform_registry_1.generatePlatformsRegistry)(taskDir, taskId, platformEntries);
|
|
1113
|
-
// ── 5. 执行产出 99-artifacts/ ──
|
|
1114
|
-
await (0, fs_extra_1.ensureDir)((0, path_1.join)(taskDir, '99-artifacts'));
|
|
1115
|
-
const testMaterial = extractRelevantSection(specContents['TEST.md'] || '', section.name);
|
|
1116
|
-
const riskMaterial = extractRelevantSection(specContents['RISK.md'] || '', section.name);
|
|
1117
|
-
const depsMaterial = extractRelevantSection(specContents['DEPS.md'] || '', section.name);
|
|
1118
|
-
const monitorMaterial = extractRelevantSection(specContents['MONITOR.md'] || '', section.name);
|
|
1119
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'TEST.md'), generateTestOutline(section, testMaterial));
|
|
1120
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md'), generateReviewChecklist(section));
|
|
1121
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPLOY.md'), generateDeployChecklist(section));
|
|
1122
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ERROR_CODES.md'), generateErrorCodes(section));
|
|
1123
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'RISK.md'), generateRiskTemplate(section, riskMaterial));
|
|
1124
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'DEPS.md'), generateDepsTemplate(section, depsMaterial));
|
|
1125
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'MONITOR.md'), generateMonitorTemplate(section, monitorMaterial));
|
|
1126
|
-
const adr = generateAdr(section);
|
|
1127
|
-
if (adr) {
|
|
1128
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '99-artifacts', 'ADR.md'), adr);
|
|
1129
|
-
}
|
|
1130
|
-
// ── 6. 任务上下文 CONTEXT.md ──
|
|
1262
|
+
// ─ 5. 任务上下文 CONTEXT.md ─
|
|
1131
1263
|
const sourceFile = section._sourceFile || '';
|
|
1132
1264
|
const topic = section._topic || slugify(section.name);
|
|
1133
1265
|
// 推导 010-requirements 源路径
|
|
@@ -1153,7 +1285,7 @@ ${apiDesc}
|
|
|
1153
1285
|
})
|
|
1154
1286
|
: [];
|
|
1155
1287
|
// 提取原始描述摘要(从 REQ.md 内容截取前 500 字)
|
|
1156
|
-
const reqMdPath = (0, path_1.join)(taskDir, '
|
|
1288
|
+
const reqMdPath = (0, path_1.join)(taskDir, '00-specs', 'REQ.md');
|
|
1157
1289
|
let originalDesc = '';
|
|
1158
1290
|
if (await (0, fs_extra_1.pathExists)(reqMdPath)) {
|
|
1159
1291
|
const reqContent = await (0, fs_extra_1.readFile)(reqMdPath, 'utf-8');
|
|
@@ -1161,7 +1293,7 @@ ${apiDesc}
|
|
|
1161
1293
|
const body = reqContent.replace(/^#[^\n]*\n/, '').trim();
|
|
1162
1294
|
originalDesc = body.length > 500 ? body.slice(0, 500) + '...' : body;
|
|
1163
1295
|
}
|
|
1164
|
-
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '
|
|
1296
|
+
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '00-specs', 'CONTEXT.md'), `# 任务上下文
|
|
1165
1297
|
|
|
1166
1298
|
## 来源追溯
|
|
1167
1299
|
|
|
@@ -1419,8 +1551,10 @@ async function generateImpactGraph(iterationDir, sections, platforms) {
|
|
|
1419
1551
|
const taskType = s._taskType || 'feature';
|
|
1420
1552
|
const taskDir = (0, path_1.join)(iterationDir, '030-tasks', taskType, taskId);
|
|
1421
1553
|
if (await (0, fs_extra_1.pathExists)(taskDir)) {
|
|
1422
|
-
// 生成风险报告并嵌入
|
|
1423
|
-
const taskMdPath = (0, path_1.join)(taskDir, '
|
|
1554
|
+
// 生成风险报告并嵌入 00-specs/TECH.md(去重:只写一次)
|
|
1555
|
+
const taskMdPath = (await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '00-specs', 'TECH.md')))
|
|
1556
|
+
? (0, path_1.join)(taskDir, '00-specs', 'TECH.md')
|
|
1557
|
+
: (0, path_1.join)(taskDir, '_shared', 'TECH.md');
|
|
1424
1558
|
const riskReport = (0, risk_scorer_1.generateRiskReport)(risk);
|
|
1425
1559
|
await (0, fs_extra_1.writeFile)((0, path_1.join)(taskDir, '.risk'), riskReport);
|
|
1426
1560
|
if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
|
|
@@ -1572,7 +1706,9 @@ async function injectTechFromAnalysis(iterationDir, taskDir, sectionName) {
|
|
|
1572
1706
|
const depSection = analysis.match(/### 接口依赖[\s\S]*?(?=###|$)/);
|
|
1573
1707
|
if (!techSection && !dbSection && !depSection)
|
|
1574
1708
|
return;
|
|
1575
|
-
const techPath = (0, path_1.join)(taskDir, '
|
|
1709
|
+
const techPath = (await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '00-specs', 'TECH.md')))
|
|
1710
|
+
? (0, path_1.join)(taskDir, '00-specs', 'TECH.md')
|
|
1711
|
+
: (0, path_1.join)(taskDir, '_shared', 'TECH.md');
|
|
1576
1712
|
let tech = await (0, fs_extra_1.readFile)(techPath, 'utf-8');
|
|
1577
1713
|
const note = '\n\n> 以下内容自动从 ANALYSIS.md 注入\n\n';
|
|
1578
1714
|
if (techSection && !tech.includes(techSection[0].trim())) {
|
|
@@ -1597,6 +1733,25 @@ function generateApiContract(section) {
|
|
|
1597
1733
|
}
|
|
1598
1734
|
if (apis.length === 0)
|
|
1599
1735
|
return '';
|
|
1736
|
+
// 从 section 内容提取每个 API 的请求/响应字段
|
|
1737
|
+
const fullContent = section.content || '';
|
|
1738
|
+
const apiFieldMap = {};
|
|
1739
|
+
for (const api of apis) {
|
|
1740
|
+
const apiIdx = fullContent.indexOf(api.path);
|
|
1741
|
+
if (apiIdx >= 0) {
|
|
1742
|
+
const after = fullContent.slice(apiIdx, apiIdx + 800);
|
|
1743
|
+
const fields = [];
|
|
1744
|
+
const fieldRegex = /[`']([a-zA-Z]\w{1,25})[`']\s*[((]?[::]?\s*(?:类型[::])?\s*([\u4e00-\u9fa5A-Za-z]+)/g;
|
|
1745
|
+
let fm;
|
|
1746
|
+
while ((fm = fieldRegex.exec(after)) !== null) {
|
|
1747
|
+
if (!['api', 'data', 'code', 'message', 'msg', 'status', 'ok', 'err'].includes(fm[1].toLowerCase())) {
|
|
1748
|
+
fields.push(`${fm[1]}: ${fm[2]}`);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
if (fields.length > 0)
|
|
1752
|
+
apiFieldMap[api.path] = fields.slice(0, 10);
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1600
1755
|
let yaml = `# ${section.name} — API Contract
|
|
1601
1756
|
# Auto-generated from REQ.md
|
|
1602
1757
|
|
|
@@ -1613,7 +1768,17 @@ paths:
|
|
|
1613
1768
|
${api.method.toLowerCase()}:
|
|
1614
1769
|
tags: [${tag}]
|
|
1615
1770
|
summary: "${api.desc}"
|
|
1616
|
-
|
|
1771
|
+
`;
|
|
1772
|
+
// 添加字段定义
|
|
1773
|
+
if (apiFieldMap[api.path]) {
|
|
1774
|
+
yaml += ` fields:
|
|
1775
|
+
`;
|
|
1776
|
+
for (const f of apiFieldMap[api.path]) {
|
|
1777
|
+
yaml += ` - "${f}"
|
|
1778
|
+
`;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
yaml += ` responses:
|
|
1617
1782
|
"200":
|
|
1618
1783
|
description: Success
|
|
1619
1784
|
`;
|
|
@@ -1725,7 +1890,7 @@ async function loadSpecContents(iterationDir) {
|
|
|
1725
1890
|
const specDir = (0, path_1.join)(iterationDir, '020-specs');
|
|
1726
1891
|
if (!(await (0, fs_extra_1.pathExists)(specDir)))
|
|
1727
1892
|
return specs;
|
|
1728
|
-
for (const f of ['TECH.md', 'TEST.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'ANALYSIS.md', 'REQUIREMENT.md']) {
|
|
1893
|
+
for (const f of ['TECH.md', 'TEST.md', 'RISK.md', 'DEPS.md', 'MONITOR.md', 'ANALYSIS.md', 'REQUIREMENT.md', 'UI_SPEC.md']) {
|
|
1729
1894
|
const fp = (0, path_1.join)(specDir, f);
|
|
1730
1895
|
if (await (0, fs_extra_1.pathExists)(fp)) {
|
|
1731
1896
|
const content = await (0, fs_extra_1.readFile)(fp, 'utf-8');
|