speccore 6.18.4 → 6.30.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 +36 -282
- 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/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
package/dist/commands/execute.js
CHANGED
|
@@ -460,7 +460,7 @@ async function executeWithProgress(tasks, iteration, base, skip, options) {
|
|
|
460
460
|
const taskDir = (0, path_1.join)(iterDirForRetro, '030-tasks', task.id);
|
|
461
461
|
const taskCodePath = (await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, 'code'))) ? (0, path_1.join)(taskDir, 'code') : absCodePath;
|
|
462
462
|
// 读取上一轮修复状态(如果有)
|
|
463
|
-
const statePath = (0, path_1.join)(taskDir, '
|
|
463
|
+
const statePath = (0, path_1.join)(taskDir, '.verify-state.json');
|
|
464
464
|
let currentRound = 1;
|
|
465
465
|
if (await (0, fs_extra_1.pathExists)(statePath)) {
|
|
466
466
|
try {
|
|
@@ -491,8 +491,6 @@ async function executeWithProgress(tasks, iteration, base, skip, options) {
|
|
|
491
491
|
const MAX_ROUNDS = 3;
|
|
492
492
|
if (currentRound < MAX_ROUNDS) {
|
|
493
493
|
// 保存轮次状态 + 输出 [SPECCORE_EXEC] 让 AI 修复
|
|
494
|
-
const { ensureDir } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
495
|
-
await ensureDir((0, path_1.join)(taskDir, '99-artifacts'));
|
|
496
494
|
await (0, fs_extra_1.writeFile)(statePath, JSON.stringify({ round: currentRound, taskId: task.id, timestamp: new Date().toISOString() }));
|
|
497
495
|
logger_1.logger.info(` 🤖 请求 AI 修复(第 ${currentRound}/${MAX_ROUNDS} 轮)...`);
|
|
498
496
|
(0, verify_engine_1.outputFixTag)(gate.report, taskDir, currentRound);
|
|
@@ -500,7 +498,7 @@ async function executeWithProgress(tasks, iteration, base, skip, options) {
|
|
|
500
498
|
else {
|
|
501
499
|
// 3 轮都失败
|
|
502
500
|
logger_1.logger.error(` 💀 ${task.id}: ${MAX_ROUNDS} 轮修复后质量门禁仍未通过`);
|
|
503
|
-
logger_1.logger.info(` 📄 报告: ${(0, path_1.join)(taskDir, '
|
|
501
|
+
logger_1.logger.info(` 📄 报告: ${(0, path_1.join)(taskDir, 'VERIFY_REPORT.md')}`);
|
|
504
502
|
logger_1.logger.info(` 💡 请人工检查并修复`);
|
|
505
503
|
}
|
|
506
504
|
}
|
|
@@ -608,7 +606,7 @@ async function processBatch(tasks, state, iteration) {
|
|
|
608
606
|
for (const task of tasks) {
|
|
609
607
|
logger_1.logger.info(` ${task.id}:`);
|
|
610
608
|
const tDir = await resolveTaskDir(iterDir, task.id);
|
|
611
|
-
for (const specPath of ['
|
|
609
|
+
for (const specPath of ['00-specs/REQ.md', '00-specs/TECH.md', '00-specs/CONTEXT.md', '_shared/REQ.md', '_shared/TECH.md']) {
|
|
612
610
|
const p = (0, path_1.join)(tDir, specPath);
|
|
613
611
|
if (await (0, fs_extra_1.pathExists)(p)) {
|
|
614
612
|
const content = await (0, fs_extra_1.readFile)(p, 'utf-8');
|
|
@@ -635,6 +633,18 @@ async function processBatch(tasks, state, iteration) {
|
|
|
635
633
|
logger_1.logger.info(` 🔄 执行中...`);
|
|
636
634
|
await generateTaskSkeleton(task, iteration);
|
|
637
635
|
completed.push(task.id);
|
|
636
|
+
// 写入任务摘要到 execution-state(文件即记忆)
|
|
637
|
+
const taskSummary = {
|
|
638
|
+
taskId: task.id,
|
|
639
|
+
taskName: task.name || task.id,
|
|
640
|
+
type: task.type || 'feature',
|
|
641
|
+
status: 'completed',
|
|
642
|
+
summary: `${task.name || task.id} 骨架已生成`,
|
|
643
|
+
outputs: ['00-specs/', '10-backend/', '20-frontend/'],
|
|
644
|
+
dependencies: task.dependencies || [],
|
|
645
|
+
completedAt: new Date().toISOString(),
|
|
646
|
+
};
|
|
647
|
+
(0, execution_state_1.addTaskSummary)(state, taskSummary);
|
|
638
648
|
logger_1.logger.info(` ✅ ${task.id || task} completed`);
|
|
639
649
|
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
640
650
|
const estRemaining = Math.round((elapsed / (i + 1)) * (total - i - 1));
|
|
@@ -642,8 +652,11 @@ async function processBatch(tasks, state, iteration) {
|
|
|
642
652
|
}
|
|
643
653
|
// Mark batch complete
|
|
644
654
|
(0, execution_state_1.completeBatch)(state, batchNum, completed);
|
|
655
|
+
// 写入上下文摘要文件(供新会话快速恢复)
|
|
656
|
+
const summaryPath = await (0, execution_state_1.writeContextSummaryFile)(state);
|
|
645
657
|
logger_1.logger.info(``);
|
|
646
658
|
logger_1.logger.info(`✅ Batch ${batchNum} complete (${completed.length} tasks)`);
|
|
659
|
+
logger_1.logger.info(`📄 上下文摘要: ${summaryPath}`);
|
|
647
660
|
// Context reset note
|
|
648
661
|
logger_1.logger.info(`🔄 Resetting context for next batch...`);
|
|
649
662
|
logger_1.logger.info(``);
|
|
@@ -687,12 +700,35 @@ async function generateTaskSkeleton(task, iteration) {
|
|
|
687
700
|
const specRules = await (0, spec_rules_1.loadSpecRules)();
|
|
688
701
|
const techStack = await (0, spec_rules_1.loadTechStack)();
|
|
689
702
|
logger_1.logger.info(` Tech Stack: ${techStack.backendFramework} + ${techStack.frontendFramework}`);
|
|
690
|
-
// 读取后端 Spec
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
703
|
+
// 读取后端 Spec 生成代码骨架(扫描 10-backend/{服务}/{子任务}/ 三级嵌套)
|
|
704
|
+
const { readdir: rd } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
705
|
+
const taskEntries = await rd(taskDir, { withFileTypes: true });
|
|
706
|
+
const backendSubtaskDirs = [];
|
|
707
|
+
// 新结构: 10-backend/{服务名}/{子任务}/
|
|
708
|
+
const backendCategoryDir = (0, path_1.join)(taskDir, '10-backend');
|
|
709
|
+
if (await (0, fs_extra_1.pathExists)(backendCategoryDir)) {
|
|
710
|
+
const serviceEntries = await rd(backendCategoryDir, { withFileTypes: true });
|
|
711
|
+
for (const service of serviceEntries) {
|
|
712
|
+
if (!service.isDirectory())
|
|
713
|
+
continue;
|
|
714
|
+
const serviceDir = (0, path_1.join)(backendCategoryDir, service.name);
|
|
715
|
+
const subtaskEntries = await rd(serviceDir, { withFileTypes: true });
|
|
716
|
+
for (const st of subtaskEntries) {
|
|
717
|
+
if (st.isDirectory() && !st.name.startsWith('.')) {
|
|
718
|
+
backendSubtaskDirs.push((0, path_1.join)(serviceDir, st.name));
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
// 回退: 旧结构 10-*/ 直接在任务根目录
|
|
724
|
+
if (backendSubtaskDirs.length === 0) {
|
|
725
|
+
const legacy = taskEntries
|
|
726
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('10-'))
|
|
727
|
+
.map((d) => (0, path_1.join)(taskDir, d.name));
|
|
728
|
+
backendSubtaskDirs.push(...legacy);
|
|
729
|
+
}
|
|
730
|
+
for (const backendDir of backendSubtaskDirs) {
|
|
731
|
+
const reqPath = (0, path_1.join)(taskDir, '00-specs', 'REQ.md');
|
|
696
732
|
let className = convertToClassName(task.name || task.id);
|
|
697
733
|
// 从 CONSTITUTION.md 读取包名,回退到默认值
|
|
698
734
|
let packageName = `com.example.${className.toLowerCase()}`;
|
|
@@ -724,8 +760,8 @@ async function generateTaskSkeleton(task, iteration) {
|
|
|
724
760
|
const repoPath = (0, path_1.join)(srcDir, `${className}Repository.java`);
|
|
725
761
|
tx.write(repoPath, repoCode);
|
|
726
762
|
filesUpdated++;
|
|
727
|
-
//
|
|
728
|
-
const taskMdPath = (0, path_1.join)(
|
|
763
|
+
// 更新子任务 TASK.md 状态
|
|
764
|
+
const taskMdPath = (0, path_1.join)(backendDir, 'TASK.md');
|
|
729
765
|
if (await (0, fs_extra_1.pathExists)(taskMdPath)) {
|
|
730
766
|
const content = await (0, fs_extra_1.readFile)(taskMdPath, 'utf-8');
|
|
731
767
|
const updated = content.replace('状态: 🔲 待开发', '状态: 🔄 进行中');
|
|
@@ -733,30 +769,47 @@ async function generateTaskSkeleton(task, iteration) {
|
|
|
733
769
|
filesUpdated++;
|
|
734
770
|
}
|
|
735
771
|
}
|
|
736
|
-
//
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const ftaskPath = (0, path_1.join)(frontendDir, pd.name, 'TASK.md');
|
|
751
|
-
if (await (0, fs_extra_1.pathExists)(ftaskPath)) {
|
|
752
|
-
const content = await (0, fs_extra_1.readFile)(ftaskPath, 'utf-8');
|
|
753
|
-
const updated = content.replace('状态: 🔲 待开发', '状态: 🔄 进行中');
|
|
754
|
-
tx.write(ftaskPath, updated);
|
|
755
|
-
filesUpdated++;
|
|
772
|
+
// 前端各平台代码生成(扫描 20-frontend/{端名}/{子任务}/ 三级嵌套)
|
|
773
|
+
const frontendSubtaskDirs = [];
|
|
774
|
+
// 新结构: 20-frontend/{端名}/{子任务}/
|
|
775
|
+
const frontendCategoryDir = (0, path_1.join)(taskDir, '20-frontend');
|
|
776
|
+
if (await (0, fs_extra_1.pathExists)(frontendCategoryDir)) {
|
|
777
|
+
const platformEntries = await rd(frontendCategoryDir, { withFileTypes: true });
|
|
778
|
+
for (const plat of platformEntries) {
|
|
779
|
+
if (!plat.isDirectory())
|
|
780
|
+
continue;
|
|
781
|
+
const platformDir = (0, path_1.join)(frontendCategoryDir, plat.name);
|
|
782
|
+
const subtaskEntries = await rd(platformDir, { withFileTypes: true });
|
|
783
|
+
for (const st of subtaskEntries) {
|
|
784
|
+
if (st.isDirectory() && !st.name.startsWith('.')) {
|
|
785
|
+
frontendSubtaskDirs.push((0, path_1.join)(platformDir, st.name));
|
|
756
786
|
}
|
|
757
787
|
}
|
|
758
788
|
}
|
|
759
789
|
}
|
|
790
|
+
// 回退: 旧结构 20-*/ 直接在任务根目录
|
|
791
|
+
if (frontendSubtaskDirs.length === 0) {
|
|
792
|
+
const legacy = taskEntries
|
|
793
|
+
.filter((d) => d.isDirectory() && d.name.startsWith('20-'))
|
|
794
|
+
.map((d) => (0, path_1.join)(taskDir, d.name));
|
|
795
|
+
frontendSubtaskDirs.push(...legacy);
|
|
796
|
+
}
|
|
797
|
+
for (const frontendDir of frontendSubtaskDirs) {
|
|
798
|
+
const componentName = convertToClassName(task.name || task.id);
|
|
799
|
+
const vueCode = generateVueComponent(componentName);
|
|
800
|
+
const srcDir = (0, path_1.join)(frontendDir, 'src');
|
|
801
|
+
await (0, fs_extra_1.ensureDir)(srcDir);
|
|
802
|
+
const vuePath = (0, path_1.join)(srcDir, `${componentName}.vue`);
|
|
803
|
+
tx.write(vuePath, vueCode);
|
|
804
|
+
filesUpdated++;
|
|
805
|
+
const ftaskPath = (0, path_1.join)(frontendDir, 'TASK.md');
|
|
806
|
+
if (await (0, fs_extra_1.pathExists)(ftaskPath)) {
|
|
807
|
+
const content = await (0, fs_extra_1.readFile)(ftaskPath, 'utf-8');
|
|
808
|
+
const updated = content.replace('状态: 🔲 待开发', '状态: 🔄 进行中');
|
|
809
|
+
tx.write(ftaskPath, updated);
|
|
810
|
+
filesUpdated++;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
760
813
|
if (tx.length > 0) {
|
|
761
814
|
await tx.commit();
|
|
762
815
|
}
|
|
@@ -946,16 +999,40 @@ async function filterByPlatform(tasks, iteration, platform) {
|
|
|
946
999
|
filtered.push(task);
|
|
947
1000
|
continue;
|
|
948
1001
|
}
|
|
949
|
-
// 新结构: 用 resolveTaskDir 查找任务目录(支持类型子目录),再查 {
|
|
1002
|
+
// 新结构: 用 resolveTaskDir 查找任务目录(支持类型子目录),再查 10-backend/{端}/ 或 20-frontend/{端}/
|
|
950
1003
|
const taskDir = await resolveTaskDir(iterDir, task.id);
|
|
951
|
-
const
|
|
1004
|
+
const isBackend = platform === 'backend' || platform.startsWith('后台');
|
|
1005
|
+
const categoryDir = isBackend ? (0, path_1.join)(taskDir, '10-backend') : (0, path_1.join)(taskDir, '20-frontend');
|
|
1006
|
+
const serviceName = isBackend && platform === 'backend' ? 'api' : platform;
|
|
1007
|
+
// 新结构: 10-backend/{端}/ 或 20-frontend/{端}/
|
|
1008
|
+
const platformDir = (0, path_1.join)(categoryDir, serviceName);
|
|
952
1009
|
if (await (0, fs_extra_1.pathExists)(platformDir)) {
|
|
953
1010
|
filtered.push(task);
|
|
954
1011
|
continue;
|
|
955
1012
|
}
|
|
956
|
-
//
|
|
957
|
-
|
|
958
|
-
|
|
1013
|
+
// 回退: 扫描 category 目录下是否有匹配的子目录
|
|
1014
|
+
try {
|
|
1015
|
+
if (await (0, fs_extra_1.pathExists)(categoryDir)) {
|
|
1016
|
+
const { readdir: readDir } = await Promise.resolve().then(() => __importStar(require('fs-extra')));
|
|
1017
|
+
const entries = await readDir(categoryDir, { withFileTypes: true });
|
|
1018
|
+
const matched = entries.find((d) => d.isDirectory() && d.name === serviceName);
|
|
1019
|
+
if (matched) {
|
|
1020
|
+
filtered.push(task);
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
catch { /* ignore */ }
|
|
1026
|
+
// 旧结构回退: 10-{端}/ 或 20-{端}/ 直接在任务根目录
|
|
1027
|
+
const subtaskPrefix = isBackend ? '10-' : '20-';
|
|
1028
|
+
const legacyDir = (0, path_1.join)(taskDir, `${subtaskPrefix}${serviceName}`);
|
|
1029
|
+
if (await (0, fs_extra_1.pathExists)(legacyDir)) {
|
|
1030
|
+
filtered.push(task);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
// 更旧结构回退: frontend/{platform}/
|
|
1034
|
+
const legacyDir2 = (0, path_1.join)(taskDir, 'frontend', platform);
|
|
1035
|
+
if (await (0, fs_extra_1.pathExists)(legacyDir2))
|
|
959
1036
|
filtered.push(task);
|
|
960
1037
|
}
|
|
961
1038
|
return filtered;
|
|
@@ -1024,25 +1101,142 @@ async function preFlightCheck(tasks, iteration, options) {
|
|
|
1024
1101
|
else {
|
|
1025
1102
|
issues.push('缺少 TECH.md');
|
|
1026
1103
|
}
|
|
1027
|
-
// 3. Test cases
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
const
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1104
|
+
// 3. Test cases(扫描子任务目录)
|
|
1105
|
+
let testFound = false;
|
|
1106
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1107
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1108
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1109
|
+
try {
|
|
1110
|
+
for (const svc of await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true })) {
|
|
1111
|
+
if (!svc.isDirectory())
|
|
1112
|
+
continue;
|
|
1113
|
+
for (const sub of await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
1114
|
+
if (!sub.isDirectory())
|
|
1115
|
+
continue;
|
|
1116
|
+
const tp = (0, path_1.join)(catPath, svc.name, sub.name, 'TEST.md');
|
|
1117
|
+
if (await (0, fs_extra_1.pathExists)(tp)) {
|
|
1118
|
+
const test = await (0, fs_extra_1.readFile)(tp, 'utf-8');
|
|
1119
|
+
const n = (test.match(/⬜|✅|❌/g) || []).length;
|
|
1120
|
+
logger_1.logger.info(` 3. 测试[${svc.name}/${sub.name}]: ${n} 用例`);
|
|
1121
|
+
testFound = true;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
catch { /* 跳过 */ }
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (!testFound) {
|
|
1130
|
+
// 旧结构回退
|
|
1131
|
+
const testPath = (0, path_1.join)(taskDir, '99-artifacts', 'TEST.md');
|
|
1132
|
+
if (await (0, fs_extra_1.pathExists)(testPath)) {
|
|
1133
|
+
const test = await (0, fs_extra_1.readFile)(testPath, 'utf-8');
|
|
1134
|
+
const n = (test.match(/⬜|✅|❌/g) || []).length;
|
|
1135
|
+
logger_1.logger.info(` 3. 测试: ${n} 用例`);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
// 4. Review(扫描子任务目录)
|
|
1139
|
+
let reviewFound = false;
|
|
1140
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1141
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1142
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1143
|
+
try {
|
|
1144
|
+
for (const svc of await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true })) {
|
|
1145
|
+
if (!svc.isDirectory())
|
|
1146
|
+
continue;
|
|
1147
|
+
for (const sub of await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
1148
|
+
if (!sub.isDirectory())
|
|
1149
|
+
continue;
|
|
1150
|
+
if (await (0, fs_extra_1.pathExists)((0, path_1.join)(catPath, svc.name, sub.name, 'REVIEW.md'))) {
|
|
1151
|
+
logger_1.logger.info(` 4. 审查[${svc.name}/${sub.name}]: ✅`);
|
|
1152
|
+
reviewFound = true;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
catch { /* 跳过 */ }
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (!reviewFound) {
|
|
1161
|
+
logger_1.logger.info(` 4. 审查: ${await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md')) ? '✅' : '❌'}`);
|
|
1162
|
+
}
|
|
1163
|
+
// 5. API(00-specs/ 优先,_shared/ 回退)
|
|
1164
|
+
const hasApiContract = await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '00-specs', 'API_CONTRACT.yaml')) || await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml'));
|
|
1165
|
+
logger_1.logger.info(` 5. 契约: ${hasApiContract ? '✅' : '⚠️'}`);
|
|
1166
|
+
// 6. Platform(扫描 10-backend/*/ 和 20-frontend/*/ 子任务目录)
|
|
1167
|
+
const subtaskDirs = [];
|
|
1168
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1169
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1170
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1171
|
+
try {
|
|
1172
|
+
const platEntries = (0, fs_1.readdirSync)(catPath, { withFileTypes: true });
|
|
1173
|
+
for (const pe of platEntries) {
|
|
1174
|
+
if (pe.isDirectory()) {
|
|
1175
|
+
const stEntries = (0, fs_1.readdirSync)((0, path_1.join)(catPath, pe.name), { withFileTypes: true });
|
|
1176
|
+
for (const st of stEntries) {
|
|
1177
|
+
if (st.isDirectory() && !st.name.startsWith('.')) {
|
|
1178
|
+
subtaskDirs.push(`${catDir}/${pe.name}/${st.name}`);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
catch { /* ignore */ }
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
// 回退: 旧结构 10-*/20-*
|
|
1188
|
+
if (subtaskDirs.length === 0) {
|
|
1189
|
+
const legacy = (0, fs_1.readdirSync)(taskDir, { withFileTypes: true })
|
|
1190
|
+
.filter((d) => d.isDirectory() && (d.name.startsWith('10-') || d.name.startsWith('20-')))
|
|
1191
|
+
.map((d) => d.name);
|
|
1192
|
+
subtaskDirs.push(...legacy);
|
|
1193
|
+
}
|
|
1194
|
+
if (subtaskDirs.length > 0) {
|
|
1195
|
+
logger_1.logger.info(` 6. 子任务: ${subtaskDirs.join(', ')}`);
|
|
1043
1196
|
}
|
|
1044
1197
|
// 7. Constitution
|
|
1045
1198
|
logger_1.logger.info(` 7. 合规: 待 validate ${issues.length > 0 ? '⚠️ ' + issues.join(', ') : ''}`);
|
|
1199
|
+
// 8. UI 规格检查(00-specs/ 优先,_shared/ 回退)
|
|
1200
|
+
const uiSpecPath = (0, path_1.join)(taskDir, '00-specs', 'UI_SPEC.md');
|
|
1201
|
+
const uiSpecFallback = (0, path_1.join)(taskDir, '_shared', 'UI_SPEC.md');
|
|
1202
|
+
const iterUiSpec = (0, path_1.join)(iterDir, '020-specs', 'UI_SPEC.md');
|
|
1203
|
+
let uiSpecContent = '';
|
|
1204
|
+
if (await (0, fs_extra_1.pathExists)(uiSpecPath)) {
|
|
1205
|
+
uiSpecContent = await (0, fs_extra_1.readFile)(uiSpecPath, 'utf-8');
|
|
1206
|
+
}
|
|
1207
|
+
else if (await (0, fs_extra_1.pathExists)(uiSpecFallback)) {
|
|
1208
|
+
uiSpecContent = await (0, fs_extra_1.readFile)(uiSpecFallback, 'utf-8');
|
|
1209
|
+
}
|
|
1210
|
+
else if (await (0, fs_extra_1.pathExists)(iterUiSpec)) {
|
|
1211
|
+
uiSpecContent = await (0, fs_extra_1.readFile)(iterUiSpec, 'utf-8');
|
|
1212
|
+
}
|
|
1213
|
+
if (uiSpecContent) {
|
|
1214
|
+
const uiPages = (uiSpecContent.match(/页面/g) || []).length;
|
|
1215
|
+
const uiComponents = (uiSpecContent.match(/组件/g) || []).length;
|
|
1216
|
+
logger_1.logger.info(` 8. UI规格: ${uiPages} 页面引用 / ${uiComponents} 组件引用`);
|
|
1217
|
+
}
|
|
1218
|
+
else {
|
|
1219
|
+
logger_1.logger.info(` 8. UI规格: ⚠️ 无 UI_SPEC.md`);
|
|
1220
|
+
}
|
|
1221
|
+
// 9. 前后端契约对标(00-specs/ 优先,_shared/ 回退)
|
|
1222
|
+
const apiContractPath = await (0, fs_extra_1.pathExists)((0, path_1.join)(taskDir, '00-specs', 'API_CONTRACT.yaml'))
|
|
1223
|
+
? (0, path_1.join)(taskDir, '00-specs', 'API_CONTRACT.yaml')
|
|
1224
|
+
: (0, path_1.join)(taskDir, '_shared', 'API_CONTRACT.yaml');
|
|
1225
|
+
if (await (0, fs_extra_1.pathExists)(apiContractPath) && uiSpecContent) {
|
|
1226
|
+
const contract = await (0, fs_extra_1.readFile)(apiContractPath, 'utf-8');
|
|
1227
|
+
const contractFields = (contract.match(/fields:/g) || []).length;
|
|
1228
|
+
const uiFields = (uiSpecContent.match(/字段/g) || []).length;
|
|
1229
|
+
if (contractFields === 0 && uiFields > 0) {
|
|
1230
|
+
issues.push('API_CONTRACT.yaml 缺少字段定义,但 UI_SPEC.md 有字段映射 — 前后端契约未对齐');
|
|
1231
|
+
logger_1.logger.info(` 9. 契约: ⚠️ API 无字段定义但 UI 有 ${uiFields} 处字段引用`);
|
|
1232
|
+
}
|
|
1233
|
+
else {
|
|
1234
|
+
logger_1.logger.info(` 9. 契约: API ${contractFields} 组字段 / UI ${uiFields} 处引用`);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
logger_1.logger.info(` 9. 契约: ⏭️ 跳过(缺 API_CONTRACT 或 UI_SPEC)`);
|
|
1239
|
+
}
|
|
1046
1240
|
// 写入问题文件,供 AI 辅助修复
|
|
1047
1241
|
if (issues.length > 0) {
|
|
1048
1242
|
const issuesMd = ['# 执行问题清单', '', `## ${task.id}`, ''];
|
|
@@ -1217,17 +1411,18 @@ function buildAgentContext(tasks, agent) {
|
|
|
1217
1411
|
ctx += `Status: ${task.status} | Priority: ${task.priority} | Type: ${task.type}\n\n`;
|
|
1218
1412
|
// Progressive loading order
|
|
1219
1413
|
ctx += `**Read in this order (progressive disclosure):**\n`;
|
|
1220
|
-
ctx += `1. \`Iteration-*/${task.id}/
|
|
1221
|
-
ctx += `2. \`Iteration-*/${task.id}/
|
|
1222
|
-
ctx += `3. \`Iteration-*/${task.id}/00-specs/
|
|
1223
|
-
ctx += `4. \`Iteration-*/${task.id}/
|
|
1414
|
+
ctx += `1. \`Iteration-*/${task.id}/10-backend/{service}/{subtask}/TASK.md\` — 后端子任务概览\n`;
|
|
1415
|
+
ctx += `2. \`Iteration-*/${task.id}/20-frontend/{platform}/{subtask}/TASK.md\` — 前端子任务概览\n`;
|
|
1416
|
+
ctx += `3. \`Iteration-*/${task.id}/00-specs/REQ.md\` — 需求 + 验收标准\n`;
|
|
1417
|
+
ctx += `4. \`Iteration-*/${task.id}/00-specs/TECH.md\` — 技术设计\n`;
|
|
1418
|
+
ctx += `5. \`Iteration-*/${task.id}/00-specs/API_CONTRACT.yaml\` — API 契约 (if exists)\n\n`;
|
|
1224
1419
|
ctx += `**Supplementary (read only if needed):**\n`;
|
|
1225
|
-
ctx += `-
|
|
1226
|
-
ctx += `- 00-specs/SCHEMA.md (DB schema) |
|
|
1227
|
-
ctx += `-
|
|
1228
|
-
ctx += `-
|
|
1229
|
-
ctx += `**Frontend (if exists):**\n`;
|
|
1230
|
-
ctx += `- 20-
|
|
1420
|
+
ctx += `- 子任务目录/TEST.md (测试用例) | 子任务目录/REVIEW.md (评审清单)\n`;
|
|
1421
|
+
ctx += `- 00-specs/SCHEMA.md (DB schema) | 子任务目录/ADR.md (架构决策)\n`;
|
|
1422
|
+
ctx += `- 子任务目录/RISK.md (风险+回滚) | 子任务目录/DEPS.md (依赖) | 子任务目录/MONITOR.md (监控)\n`;
|
|
1423
|
+
ctx += `- 子任务目录/ERROR_CODES.md (错误码) | 子任务目录/DEPLOY.md (部署)\n\n`;
|
|
1424
|
+
ctx += `**Frontend design docs (if exists):**\n`;
|
|
1425
|
+
ctx += `- 20-{platform}/COMPONENT_TREE.md | ROUTES.md | STATE.md | STYLE_GUIDE.md\n\n`;
|
|
1231
1426
|
}
|
|
1232
1427
|
// 3. Global rules (load last, only if needed)
|
|
1233
1428
|
ctx += `### Step N: Global rules (load last)\n`;
|
|
@@ -1251,52 +1446,126 @@ async function executionVerifyLoop(tasks, iteration, options) {
|
|
|
1251
1446
|
if (round > 1)
|
|
1252
1447
|
logger_1.logger.info(` 🔄 第 ${round} 轮修复...`);
|
|
1253
1448
|
allPassed = true;
|
|
1254
|
-
// 1. 检查 TEST.md
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1449
|
+
// 1. 检查 TEST.md(扫描子任务目录,合并结果)
|
|
1450
|
+
const testFiles = [];
|
|
1451
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1452
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1453
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1454
|
+
try {
|
|
1455
|
+
for (const svc of await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true })) {
|
|
1456
|
+
if (!svc.isDirectory())
|
|
1457
|
+
continue;
|
|
1458
|
+
for (const sub of await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
1459
|
+
if (!sub.isDirectory())
|
|
1460
|
+
continue;
|
|
1461
|
+
const fp = (0, path_1.join)(catPath, svc.name, sub.name, 'TEST.md');
|
|
1462
|
+
if (await (0, fs_extra_1.pathExists)(fp))
|
|
1463
|
+
testFiles.push(fp);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
catch { /* 跳过 */ }
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
if (testFiles.length === 0) {
|
|
1471
|
+
// 旧结构回退
|
|
1472
|
+
const legacy = (0, path_1.join)(taskDir, '99-artifacts', 'TEST.md');
|
|
1473
|
+
if (await (0, fs_extra_1.pathExists)(legacy))
|
|
1474
|
+
testFiles.push(legacy);
|
|
1475
|
+
}
|
|
1476
|
+
for (const testPath of testFiles) {
|
|
1257
1477
|
const testContent = await (0, fs_extra_1.readFile)(testPath, 'utf-8');
|
|
1258
1478
|
const total = (testContent.match(/\[[ x]\]/g) || []).length;
|
|
1259
1479
|
const done = (testContent.match(/\[x\]/g) || []).length;
|
|
1480
|
+
const label = testPath.replace(taskDir + '/', '');
|
|
1260
1481
|
if (done < total) {
|
|
1261
|
-
logger_1.logger.info(` 🧪
|
|
1262
|
-
if (round === maxRounds)
|
|
1263
|
-
logger_1.logger.warn(` ⚠️ 仍有 ${total - done}
|
|
1264
|
-
}
|
|
1482
|
+
logger_1.logger.info(` 🧪 ${label}: ${done}/${total} 通过`);
|
|
1483
|
+
if (round === maxRounds)
|
|
1484
|
+
logger_1.logger.warn(` ⚠️ 仍有 ${total - done} 项未通过`);
|
|
1265
1485
|
allPassed = false;
|
|
1266
1486
|
}
|
|
1267
1487
|
else {
|
|
1268
|
-
logger_1.logger.info(` 🧪
|
|
1488
|
+
logger_1.logger.info(` 🧪 ${label}: ${done}/${total} ✅`);
|
|
1269
1489
|
}
|
|
1270
1490
|
}
|
|
1271
|
-
// 2. 检查 REVIEW.md
|
|
1272
|
-
const
|
|
1273
|
-
|
|
1491
|
+
// 2. 检查 REVIEW.md(扫描子任务目录)
|
|
1492
|
+
const reviewFiles = [];
|
|
1493
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1494
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1495
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1496
|
+
try {
|
|
1497
|
+
for (const svc of await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true })) {
|
|
1498
|
+
if (!svc.isDirectory())
|
|
1499
|
+
continue;
|
|
1500
|
+
for (const sub of await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
1501
|
+
if (!sub.isDirectory())
|
|
1502
|
+
continue;
|
|
1503
|
+
const fp = (0, path_1.join)(catPath, svc.name, sub.name, 'REVIEW.md');
|
|
1504
|
+
if (await (0, fs_extra_1.pathExists)(fp))
|
|
1505
|
+
reviewFiles.push(fp);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
catch { /* 跳过 */ }
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
if (reviewFiles.length === 0) {
|
|
1513
|
+
const legacy = (0, path_1.join)(taskDir, '99-artifacts', 'REVIEW.md');
|
|
1514
|
+
if (await (0, fs_extra_1.pathExists)(legacy))
|
|
1515
|
+
reviewFiles.push(legacy);
|
|
1516
|
+
}
|
|
1517
|
+
for (const reviewPath of reviewFiles) {
|
|
1274
1518
|
const reviewContent = await (0, fs_extra_1.readFile)(reviewPath, 'utf-8');
|
|
1275
1519
|
const total = (reviewContent.match(/\[[ x]\]/g) || []).length;
|
|
1276
1520
|
const doneR = (reviewContent.match(/\[x\]/gi) || []).length;
|
|
1521
|
+
const label = reviewPath.replace(taskDir + '/', '');
|
|
1277
1522
|
if (doneR < total) {
|
|
1278
|
-
logger_1.logger.info(` 📋
|
|
1279
|
-
if (round === maxRounds)
|
|
1280
|
-
logger_1.logger.warn(` ⚠️ 仍有 ${total - doneR}
|
|
1281
|
-
}
|
|
1523
|
+
logger_1.logger.info(` 📋 ${label}: ${doneR}/${total} 通过`);
|
|
1524
|
+
if (round === maxRounds)
|
|
1525
|
+
logger_1.logger.warn(` ⚠️ 仍有 ${total - doneR} 项未审查`);
|
|
1282
1526
|
allPassed = false;
|
|
1283
1527
|
}
|
|
1284
1528
|
else {
|
|
1285
|
-
logger_1.logger.info(` 📋
|
|
1529
|
+
logger_1.logger.info(` 📋 ${label}: ${doneR}/${total} ✅`);
|
|
1286
1530
|
}
|
|
1287
1531
|
}
|
|
1288
|
-
// 3. 检查 DEPLOY.md
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1532
|
+
// 3. 检查 DEPLOY.md(扫描子任务目录)
|
|
1533
|
+
const deployFiles = [];
|
|
1534
|
+
for (const catDir of ['10-backend', '20-frontend']) {
|
|
1535
|
+
const catPath = (0, path_1.join)(taskDir, catDir);
|
|
1536
|
+
if (await (0, fs_extra_1.pathExists)(catPath)) {
|
|
1537
|
+
try {
|
|
1538
|
+
for (const svc of await (0, fs_extra_1.readdir)(catPath, { withFileTypes: true })) {
|
|
1539
|
+
if (!svc.isDirectory())
|
|
1540
|
+
continue;
|
|
1541
|
+
for (const sub of await (0, fs_extra_1.readdir)((0, path_1.join)(catPath, svc.name), { withFileTypes: true })) {
|
|
1542
|
+
if (!sub.isDirectory())
|
|
1543
|
+
continue;
|
|
1544
|
+
const fp = (0, path_1.join)(catPath, svc.name, sub.name, 'DEPLOY.md');
|
|
1545
|
+
if (await (0, fs_extra_1.pathExists)(fp))
|
|
1546
|
+
deployFiles.push(fp);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
catch { /* 跳过 */ }
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
if (deployFiles.length === 0) {
|
|
1554
|
+
const legacy = (0, path_1.join)(taskDir, '99-artifacts', 'DEPLOY.md');
|
|
1555
|
+
if (await (0, fs_extra_1.pathExists)(legacy))
|
|
1556
|
+
deployFiles.push(legacy);
|
|
1557
|
+
}
|
|
1558
|
+
for (const deployPath of deployFiles) {
|
|
1291
1559
|
const depContent = await (0, fs_extra_1.readFile)(deployPath, 'utf-8');
|
|
1292
1560
|
const total = (depContent.match(/\[[ x]\]/g) || []).length;
|
|
1293
1561
|
const doneD = (depContent.match(/\[x\]/g) || []).length;
|
|
1562
|
+
const label = deployPath.replace(taskDir + '/', '');
|
|
1294
1563
|
if (doneD < total) {
|
|
1295
|
-
logger_1.logger.info(` 🚀
|
|
1564
|
+
logger_1.logger.info(` 🚀 ${label}: ${doneD}/${total} 通过`);
|
|
1296
1565
|
allPassed = false;
|
|
1297
1566
|
}
|
|
1298
1567
|
else {
|
|
1299
|
-
logger_1.logger.info(` 🚀
|
|
1568
|
+
logger_1.logger.info(` 🚀 ${label}: ${doneD}/${total} ✅`);
|
|
1300
1569
|
}
|
|
1301
1570
|
}
|
|
1302
1571
|
if (allPassed)
|
|
@@ -1425,18 +1694,22 @@ async function runPromptMode(iteration, options) {
|
|
|
1425
1694
|
}
|
|
1426
1695
|
const taskDir = await resolveTaskDir(await (0, context_1.getIterationDir)(iteration), task);
|
|
1427
1696
|
// ── 前置检查:任务必须有有效需求或分析内容 ──
|
|
1428
|
-
// 路径优先级:
|
|
1697
|
+
// 路径优先级: 00-specs/(标准)→ _shared/(兼容)→ 子任务目录 → 根目录(legacy)
|
|
1429
1698
|
const candidates = [
|
|
1699
|
+
(0, path_1.join)(taskDir, '00-specs', 'REQ.md'),
|
|
1700
|
+
(0, path_1.join)(taskDir, '00-specs', 'TECH.md'),
|
|
1430
1701
|
(0, path_1.join)(taskDir, '_shared', 'REQ.md'),
|
|
1431
1702
|
(0, path_1.join)(taskDir, '_shared', 'TECH.md'),
|
|
1432
1703
|
(0, path_1.join)(taskDir, '00-specs', 'ANALYSIS.md'),
|
|
1433
|
-
(0, path_1.join)(taskDir, '00-specs', 'REQ.md'),
|
|
1434
1704
|
(0, path_1.join)(taskDir, 'ANALYSIS.md'),
|
|
1435
1705
|
(0, path_1.join)(taskDir, 'REQUIREMENT.md'),
|
|
1436
1706
|
];
|
|
1437
|
-
//
|
|
1707
|
+
// 按端执行时,额外检查该端子任务的 TASK.md
|
|
1438
1708
|
if (options.platform) {
|
|
1439
|
-
|
|
1709
|
+
const isBackend = options.platform === 'backend' || options.platform.startsWith('后台');
|
|
1710
|
+
const prefix = isBackend ? '10-' : '20-';
|
|
1711
|
+
const subtaskName = isBackend && options.platform === 'backend' ? 'api' : options.platform;
|
|
1712
|
+
candidates.unshift((0, path_1.join)(taskDir, `${prefix}${subtaskName}`, 'TASK.md'));
|
|
1440
1713
|
}
|
|
1441
1714
|
// 读取文件内容验证有效性(接受 ANALYSIS.md / REQ.md / REQUIREMENT.md)
|
|
1442
1715
|
// Bug 任务的 REQ.md(问题描述)本身就可以作为AI生成代码的依据
|
|
@@ -1456,7 +1729,7 @@ async function runPromptMode(iteration, options) {
|
|
|
1456
1729
|
}
|
|
1457
1730
|
}
|
|
1458
1731
|
if (!effectiveAnalysis) {
|
|
1459
|
-
const reason = `任务 ${task} 缺少有效的需求或分析内容\n(
|
|
1732
|
+
const reason = `任务 ${task} 缺少有效的需求或分析内容\n(00-specs/REQ.md、00-specs/TECH.md、00-specs/ANALYSIS.md 均不存在或内容无效)`;
|
|
1460
1733
|
(0, prompt_builder_1.outputNeedsInfo)({
|
|
1461
1734
|
command: 'execute',
|
|
1462
1735
|
missing: ['analysis'],
|
|
@@ -1525,9 +1798,18 @@ async function runPromptMode(iteration, options) {
|
|
|
1525
1798
|
promptText += `- 当前批次: ${currentBatch}/${totalBatches}\n`;
|
|
1526
1799
|
promptText += `- 批次大小: ${bs}\n`;
|
|
1527
1800
|
if (isBatchEnd && nextTask) {
|
|
1528
|
-
|
|
1801
|
+
// 写入上下文摘要文件(如果 execution-state 存在)
|
|
1802
|
+
let summaryPath = '';
|
|
1803
|
+
const existingState = (0, execution_state_1.loadExecutionState)();
|
|
1804
|
+
if (existingState) {
|
|
1805
|
+
summaryPath = await (0, execution_state_1.writeContextSummaryFile)(existingState);
|
|
1806
|
+
}
|
|
1807
|
+
promptText += `\n[SPECCORE_CONTINUE${summaryPath ? `: ${summaryPath}` : ''}]\n`;
|
|
1529
1808
|
promptText += `本批次已完成。请开始新的对话,然后执行:\n`;
|
|
1530
1809
|
promptText += `speccore execute --prompt --task=${nextTask.id} -i ${iteration}\n`;
|
|
1810
|
+
if (summaryPath) {
|
|
1811
|
+
promptText += `\n💡 新会话会自动读取摘要文件恢复上下文\n`;
|
|
1812
|
+
}
|
|
1531
1813
|
}
|
|
1532
1814
|
else if (nextTask) {
|
|
1533
1815
|
promptText += `\n下一个任务: ${nextTask.id}(继续当前对话)\n`;
|