@nick848/sf-cli 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +219 -89
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +219 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +219 -89
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -229,23 +229,38 @@ ${resource.analysis}`;
|
|
|
229
229
|
lines.push("");
|
|
230
230
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
231
231
|
lines.push("");
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
activeSession.
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
232
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
233
|
+
loader.start();
|
|
234
|
+
try {
|
|
235
|
+
activeSession.specItems = await generateSpecItemsWithAI(
|
|
236
|
+
activeSession.refinedRequirement,
|
|
237
|
+
activeSession.context,
|
|
238
|
+
activeSession.bddScenarios,
|
|
239
|
+
activeSession.clarificationQuestions,
|
|
240
|
+
activeSession.referenceResources,
|
|
241
|
+
ctx
|
|
242
|
+
);
|
|
243
|
+
loader.stop();
|
|
244
|
+
} catch {
|
|
245
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u89C4\u683C\u62C6\u5206"));
|
|
246
|
+
activeSession.specItems = generateSpecItems(
|
|
247
|
+
activeSession.refinedRequirement,
|
|
248
|
+
activeSession.context,
|
|
249
|
+
activeSession.bddScenarios,
|
|
250
|
+
activeSession.clarificationQuestions,
|
|
251
|
+
activeSession.referenceResources
|
|
252
|
+
);
|
|
253
|
+
}
|
|
239
254
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
240
255
|
lines.push(chalk9.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
241
256
|
lines.push(chalk9.gray(` \u8DEF\u5F84: ${specPath}`));
|
|
242
257
|
lines.push("");
|
|
243
258
|
lines.push(chalk9.cyan(" \u4EFB\u52A1\u6982\u89C8:"));
|
|
244
|
-
for (const item of activeSession.specItems.slice(0,
|
|
259
|
+
for (const item of activeSession.specItems.slice(0, 8)) {
|
|
245
260
|
const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
246
261
|
lines.push(chalk9.gray(` ${icon} [${item.id}] ${item.title}`));
|
|
247
262
|
}
|
|
248
|
-
if (activeSession.specItems.length >
|
|
263
|
+
if (activeSession.specItems.length > 8) {
|
|
249
264
|
lines.push(chalk9.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
|
|
250
265
|
}
|
|
251
266
|
lines.push("");
|
|
@@ -392,13 +407,15 @@ async function handleWorkflowInput(input, ctx) {
|
|
|
392
407
|
activeSession.bddScenarios = generateBDDScenarios(
|
|
393
408
|
activeSession.refinedRequirement,
|
|
394
409
|
activeSession.context,
|
|
395
|
-
activeSession.clarificationQuestions
|
|
410
|
+
activeSession.clarificationQuestions,
|
|
411
|
+
activeSession.referenceResources
|
|
396
412
|
);
|
|
397
413
|
activeSession.specItems = generateSpecItems(
|
|
398
414
|
activeSession.refinedRequirement,
|
|
399
415
|
activeSession.context,
|
|
400
416
|
activeSession.bddScenarios,
|
|
401
|
-
activeSession.clarificationQuestions
|
|
417
|
+
activeSession.clarificationQuestions,
|
|
418
|
+
activeSession.referenceResources
|
|
402
419
|
);
|
|
403
420
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
404
421
|
return {
|
|
@@ -557,107 +574,115 @@ function getCategoryLabel(category) {
|
|
|
557
574
|
async function executeDevelopment(ctx, session) {
|
|
558
575
|
const workingDir = ctx.options.workingDirectory;
|
|
559
576
|
const files = [];
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
577
|
+
console.log("");
|
|
578
|
+
for (let i = 0; i < session.specItems.length; i++) {
|
|
579
|
+
const item = session.specItems[i];
|
|
580
|
+
const prefix = `[${i + 1}/${session.specItems.length}]`;
|
|
581
|
+
if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
const loader = new LoadingIndicator(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
|
|
585
|
+
loader.start();
|
|
586
|
+
try {
|
|
587
|
+
const taskPrompt = buildTaskPrompt(session, item, i);
|
|
588
|
+
const messages = [
|
|
589
|
+
{
|
|
590
|
+
role: "system",
|
|
591
|
+
content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EFB\u52A1\u63CF\u8FF0\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
|
|
568
592
|
|
|
569
593
|
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
570
|
-
1. \
|
|
571
|
-
2. \
|
|
572
|
-
3. \
|
|
573
|
-
|
|
574
|
-
\u4EE3\u7801\u8981\u6C42\uFF1A
|
|
575
|
-
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
576
|
-
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
577
|
-
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
578
|
-
4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
|
|
579
|
-
5. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
594
|
+
1. \u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
|
|
595
|
+
2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
596
|
+
3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
597
|
+
4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
580
598
|
|
|
581
599
|
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
582
600
|
- \u540D\u79F0: ${session.context?.name}
|
|
583
601
|
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
584
602
|
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
585
603
|
|
|
586
|
-
${session.context?.devStandards ? `\u3010\
|
|
587
|
-
${session.context.devStandards.slice(0,
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
604
|
+
${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
|
|
605
|
+
${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
role: "user",
|
|
609
|
+
content: taskPrompt
|
|
610
|
+
}
|
|
611
|
+
];
|
|
612
|
+
const response = await ctx.modelService.sendMessage(messages, {
|
|
613
|
+
temperature: 0.3,
|
|
614
|
+
maxTokens: 4e3,
|
|
615
|
+
// 单个任务减少 token
|
|
616
|
+
agent: "frontend-dev",
|
|
617
|
+
timeout: 12e4
|
|
618
|
+
// 2分钟超时
|
|
619
|
+
});
|
|
620
|
+
const codeBlocks = parseCodeBlocks(response.content);
|
|
621
|
+
if (codeBlocks.length > 0) {
|
|
622
|
+
for (const block of codeBlocks) {
|
|
623
|
+
const filePath = path5.join(workingDir, block.filename);
|
|
624
|
+
const dir = path5.dirname(filePath);
|
|
625
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
626
|
+
await fs4.writeFile(filePath, block.code, "utf-8");
|
|
627
|
+
files.push(block.filename);
|
|
628
|
+
}
|
|
629
|
+
loader.stop(chalk9.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
|
|
630
|
+
} else {
|
|
631
|
+
const implDir = path5.join(workingDir, "src");
|
|
632
|
+
await fs4.mkdir(implDir, { recursive: true });
|
|
633
|
+
const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
|
|
634
|
+
const filePath = path5.join(implDir, fileName);
|
|
635
|
+
await fs4.writeFile(filePath, `// TODO: ${item.title}
|
|
636
|
+
// ${item.description}
|
|
637
|
+
`, "utf-8");
|
|
638
|
+
files.push(`src/${fileName}`);
|
|
639
|
+
loader.stop(chalk9.yellow(`${prefix} \u26A0 \u751F\u6210\u57FA\u7840\u6A21\u677F: ${item.title.slice(0, 20)}`));
|
|
640
|
+
}
|
|
641
|
+
if (i < session.specItems.length - 1) {
|
|
642
|
+
await new Promise((resolve4) => setTimeout(resolve4, 500));
|
|
592
643
|
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
const response = await ctx.modelService.sendMessage(messages, {
|
|
596
|
-
temperature: 0.3,
|
|
597
|
-
maxTokens: 8e3,
|
|
598
|
-
agent: "frontend-dev",
|
|
599
|
-
timeout: 18e4
|
|
600
|
-
// 3 分钟超时
|
|
601
|
-
});
|
|
602
|
-
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
603
|
-
const codeBlocks = parseCodeBlocks(response.content);
|
|
604
|
-
for (const block of codeBlocks) {
|
|
605
|
-
const filePath = path5.join(workingDir, block.filename);
|
|
606
|
-
const dir = path5.dirname(filePath);
|
|
607
|
-
await fs4.mkdir(dir, { recursive: true });
|
|
608
|
-
await fs4.writeFile(filePath, block.code, "utf-8");
|
|
609
|
-
files.push(block.filename);
|
|
610
|
-
}
|
|
611
|
-
if (files.length === 0) {
|
|
612
|
-
const implDir = path5.join(workingDir, "src", "features");
|
|
613
|
-
await fs4.mkdir(implDir, { recursive: true });
|
|
614
|
-
const featureName = session.specItems[0]?.title || "feature";
|
|
615
|
-
const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
|
|
616
|
-
const filePath = path5.join(implDir, fileName);
|
|
617
|
-
const stubCode = `/**
|
|
618
|
-
* ${session.requirement}
|
|
619
|
-
*
|
|
620
|
-
* TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
|
|
621
|
-
*/
|
|
622
|
-
|
|
623
|
-
export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
624
|
-
// TODO: \u5B9E\u73B0\u529F\u80FD
|
|
625
|
-
console.log('${featureName} - \u5F85\u5B9E\u73B0');
|
|
626
|
-
}
|
|
627
|
-
`;
|
|
628
|
-
await fs4.writeFile(filePath, stubCode, "utf-8");
|
|
629
|
-
files.push(`src/features/${fileName}`);
|
|
644
|
+
} catch (error) {
|
|
645
|
+
loader.stop(chalk9.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
|
|
630
646
|
}
|
|
631
|
-
|
|
647
|
+
}
|
|
648
|
+
if (files.length > 0) {
|
|
632
649
|
return { success: true, files };
|
|
633
|
-
}
|
|
634
|
-
loader.stop();
|
|
650
|
+
} else {
|
|
635
651
|
return {
|
|
636
652
|
success: false,
|
|
637
653
|
files: [],
|
|
638
|
-
error:
|
|
654
|
+
error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
|
|
639
655
|
};
|
|
640
656
|
}
|
|
641
657
|
}
|
|
642
|
-
function
|
|
658
|
+
function buildTaskPrompt(session, item, index) {
|
|
643
659
|
const lines = [];
|
|
644
|
-
lines.push(
|
|
645
|
-
lines.push(
|
|
646
|
-
lines.push(
|
|
647
|
-
lines.push(
|
|
648
|
-
|
|
649
|
-
lines.push(
|
|
650
|
-
for (const
|
|
651
|
-
lines.push(`- ${
|
|
660
|
+
lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
|
|
661
|
+
lines.push(`**\u6807\u9898**: ${item.title}`);
|
|
662
|
+
lines.push(`**\u63CF\u8FF0**: ${item.description}`);
|
|
663
|
+
lines.push(`**\u4F18\u5148\u7EA7**: ${item.priority}`);
|
|
664
|
+
if (item.tests && item.tests.length > 0) {
|
|
665
|
+
lines.push(`**\u9A8C\u6536\u6807\u51C6**:`);
|
|
666
|
+
for (const t of item.tests) {
|
|
667
|
+
lines.push(`- ${t}`);
|
|
652
668
|
}
|
|
653
669
|
}
|
|
654
670
|
lines.push("");
|
|
655
|
-
lines.push(
|
|
656
|
-
|
|
657
|
-
|
|
671
|
+
lines.push(`## \u6574\u4F53\u9700\u6C42\u80CC\u666F`);
|
|
672
|
+
lines.push(session.requirement);
|
|
673
|
+
const relatedScenario = session.bddScenarios.find(
|
|
674
|
+
(s) => item.title.includes(s.feature) || s.feature.includes(item.title)
|
|
675
|
+
);
|
|
676
|
+
if (relatedScenario) {
|
|
677
|
+
lines.push("");
|
|
678
|
+
lines.push(`## \u76F8\u5173 BDD \u573A\u666F`);
|
|
679
|
+
lines.push(`Feature: ${relatedScenario.feature}`);
|
|
680
|
+
for (const s of relatedScenario.scenarios.slice(0, 2)) {
|
|
681
|
+
lines.push(`- ${s.name}`);
|
|
682
|
+
}
|
|
658
683
|
}
|
|
659
684
|
lines.push("");
|
|
660
|
-
lines.push("\u8BF7\
|
|
685
|
+
lines.push("\u8BF7\u751F\u6210\u5B9E\u73B0\u6B64\u4EFB\u52A1\u7684\u4EE3\u7801\u3002\u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u9700\u8981\u7684\u6587\u4EF6\u3002");
|
|
661
686
|
return lines.join("\n");
|
|
662
687
|
}
|
|
663
688
|
function parseCodeBlocks(content) {
|
|
@@ -1124,6 +1149,111 @@ function generateSpecItems(requirement, context, bddScenarios, questions, refere
|
|
|
1124
1149
|
});
|
|
1125
1150
|
return items;
|
|
1126
1151
|
}
|
|
1152
|
+
async function generateSpecItemsWithAI(requirement, context, bddScenarios, questions, references, ctx) {
|
|
1153
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u9879\u76EE\u7ECF\u7406\u548C\u6280\u672F\u67B6\u6784\u5E08\u3002\u8BF7\u5C06\u4EE5\u4E0B\u9700\u6C42\u62C6\u5206\u4E3A\u7CBE\u7EC6\u5316\u7684\u5F00\u53D1\u4EFB\u52A1\u3002
|
|
1154
|
+
|
|
1155
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
1156
|
+
${requirement}
|
|
1157
|
+
|
|
1158
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1159
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
|
|
1160
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
1161
|
+
${context.devStandards ? `
|
|
1162
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
1163
|
+
${context.devStandards.slice(0, 2e3)}
|
|
1164
|
+
` : ""}
|
|
1165
|
+
|
|
1166
|
+
## BDD \u573A\u666F\u53C2\u8003
|
|
1167
|
+
${bddScenarios.map((s) => `- Feature: ${s.feature} (${s.scenarios.length} \u4E2A\u573A\u666F)`).join("\n")}
|
|
1168
|
+
|
|
1169
|
+
## \u6F84\u6E05\u4FE1\u606F
|
|
1170
|
+
${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`).join("\n") || "\u65E0"}
|
|
1171
|
+
|
|
1172
|
+
## \u62C6\u5206\u8981\u6C42
|
|
1173
|
+
|
|
1174
|
+
\u8BF7\u5C06\u9700\u6C42\u62C6\u5206\u4E3A **\u7EC6\u7C92\u5EA6\u7684\u5F00\u53D1\u4EFB\u52A1**\uFF0C\u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\uFF1A
|
|
1175
|
+
1. **\u5355\u4E00\u804C\u8D23** - \u4E00\u4E2A\u4EFB\u52A1\u53EA\u505A\u4E00\u4EF6\u4E8B
|
|
1176
|
+
2. **\u53EF\u72EC\u7ACB\u6D4B\u8BD5** - \u6709\u660E\u786E\u7684\u9A8C\u6536\u6807\u51C6
|
|
1177
|
+
3. **2-4\u5C0F\u65F6\u53EF\u5B8C\u6210** - \u5982\u679C\u4EFB\u52A1\u592A\u5927\uFF0C\u7EE7\u7EED\u62C6\u5206
|
|
1178
|
+
4. **\u6709\u660E\u786E\u7684\u8F93\u5165\u8F93\u51FA** - \u6E05\u695A\u77E5\u9053\u9700\u8981\u4EC0\u4E48\u3001\u4EA7\u51FA\u4EC0\u4E48
|
|
1179
|
+
|
|
1180
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
1181
|
+
\`\`\`json
|
|
1182
|
+
[
|
|
1183
|
+
{
|
|
1184
|
+
"id": "T001",
|
|
1185
|
+
"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
|
|
1186
|
+
"description": "\u8BE6\u7EC6\u63CF\u8FF0\uFF1A\u8981\u505A\u4EC0\u4E48\u3001\u5982\u4F55\u505A\u3001\u9A8C\u6536\u6807\u51C6",
|
|
1187
|
+
"priority": "high",
|
|
1188
|
+
"estimatedHours": 2,
|
|
1189
|
+
"dependencies": [],
|
|
1190
|
+
"acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
|
|
1191
|
+
}
|
|
1192
|
+
]
|
|
1193
|
+
\`\`\`
|
|
1194
|
+
|
|
1195
|
+
## \u62C6\u5206\u5EFA\u8BAE
|
|
1196
|
+
\u5BF9\u4E8E\u590D\u6742\u529F\u80FD\uFF08\u5982\u7B97\u6CD5\u7C7B\uFF09\uFF0C\u5E94\u8BE5\u62C6\u5206\u4E3A\uFF1A
|
|
1197
|
+
- \u6570\u636E\u7ED3\u6784/\u6A21\u578B\u5B9A\u4E49
|
|
1198
|
+
- \u6838\u5FC3\u7B97\u6CD5\u5206\u6B65\u5B9E\u73B0\uFF08\u6BCF\u4E2A\u8BA1\u7B97\u6B65\u9AA4\u4E00\u4E2A\u4EFB\u52A1\uFF09
|
|
1199
|
+
- \u8F93\u5165\u9A8C\u8BC1
|
|
1200
|
+
- \u7ED3\u679C\u683C\u5F0F\u5316
|
|
1201
|
+
- UI \u5C55\u793A\u7EC4\u4EF6
|
|
1202
|
+
- \u96C6\u6210\u6D4B\u8BD5
|
|
1203
|
+
|
|
1204
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
|
|
1205
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
1206
|
+
loader.start();
|
|
1207
|
+
try {
|
|
1208
|
+
const response = await ctx.modelService.sendMessage([
|
|
1209
|
+
{ role: "user", content: prompt2 }
|
|
1210
|
+
], {
|
|
1211
|
+
temperature: 0.3,
|
|
1212
|
+
maxTokens: 4e3,
|
|
1213
|
+
timeout: 18e4
|
|
1214
|
+
// 增加到3分钟
|
|
1215
|
+
});
|
|
1216
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
1217
|
+
if (jsonMatch) {
|
|
1218
|
+
try {
|
|
1219
|
+
const parsed = JSON.parse(jsonMatch[1].trim());
|
|
1220
|
+
loader.stop(chalk9.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
|
|
1221
|
+
return parsed.map((item, index) => ({
|
|
1222
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
1223
|
+
title: item.title,
|
|
1224
|
+
description: item.description,
|
|
1225
|
+
priority: item.priority || "medium",
|
|
1226
|
+
files: [],
|
|
1227
|
+
tests: item.acceptanceCriteria || []
|
|
1228
|
+
}));
|
|
1229
|
+
} catch (parseError) {
|
|
1230
|
+
loader.stop(chalk9.yellow(` \u26A0 JSON \u89E3\u6790\u5931\u8D25: ${parseError.message.slice(0, 50)}`));
|
|
1231
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
try {
|
|
1235
|
+
const parsed = JSON.parse(response.content);
|
|
1236
|
+
if (Array.isArray(parsed)) {
|
|
1237
|
+
loader.stop(chalk9.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
|
|
1238
|
+
return parsed.map((item, index) => ({
|
|
1239
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
1240
|
+
title: item.title,
|
|
1241
|
+
description: item.description,
|
|
1242
|
+
priority: item.priority || "medium",
|
|
1243
|
+
files: [],
|
|
1244
|
+
tests: item.acceptanceCriteria || []
|
|
1245
|
+
}));
|
|
1246
|
+
}
|
|
1247
|
+
} catch {
|
|
1248
|
+
}
|
|
1249
|
+
loader.stop(chalk9.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
|
|
1250
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
const errMsg = error.message.slice(0, 80);
|
|
1253
|
+
loader.stop(chalk9.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
|
|
1254
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1127
1257
|
async function saveSpecFile(workingDir, session) {
|
|
1128
1258
|
const specDir = path5.join(workingDir, "openspec", "changes");
|
|
1129
1259
|
await fs4.mkdir(specDir, { recursive: true });
|