@nick848/sf-cli 1.0.20 → 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 +122 -92
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +121 -91
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +121 -91
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -574,107 +574,115 @@ function getCategoryLabel(category) {
|
|
|
574
574
|
async function executeDevelopment(ctx, session) {
|
|
575
575
|
const workingDir = ctx.options.workingDirectory;
|
|
576
576
|
const files = [];
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
const
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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
|
|
585
592
|
|
|
586
593
|
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
587
|
-
1. \
|
|
588
|
-
2. \
|
|
589
|
-
3. \
|
|
590
|
-
|
|
591
|
-
\u4EE3\u7801\u8981\u6C42\uFF1A
|
|
592
|
-
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
593
|
-
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
594
|
-
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
595
|
-
4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
|
|
596
|
-
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
|
|
597
598
|
|
|
598
599
|
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
599
600
|
- \u540D\u79F0: ${session.context?.name}
|
|
600
601
|
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
601
602
|
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
602
603
|
|
|
603
|
-
${session.context?.devStandards ? `\u3010\
|
|
604
|
-
${session.context.devStandards.slice(0,
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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));
|
|
609
643
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
const response = await ctx.modelService.sendMessage(messages, {
|
|
613
|
-
temperature: 0.3,
|
|
614
|
-
maxTokens: 8e3,
|
|
615
|
-
agent: "frontend-dev",
|
|
616
|
-
timeout: 18e4
|
|
617
|
-
// 3 分钟超时
|
|
618
|
-
});
|
|
619
|
-
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
620
|
-
const codeBlocks = parseCodeBlocks(response.content);
|
|
621
|
-
for (const block of codeBlocks) {
|
|
622
|
-
const filePath = path5.join(workingDir, block.filename);
|
|
623
|
-
const dir = path5.dirname(filePath);
|
|
624
|
-
await fs4.mkdir(dir, { recursive: true });
|
|
625
|
-
await fs4.writeFile(filePath, block.code, "utf-8");
|
|
626
|
-
files.push(block.filename);
|
|
627
|
-
}
|
|
628
|
-
if (files.length === 0) {
|
|
629
|
-
const implDir = path5.join(workingDir, "src", "features");
|
|
630
|
-
await fs4.mkdir(implDir, { recursive: true });
|
|
631
|
-
const featureName = session.specItems[0]?.title || "feature";
|
|
632
|
-
const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
|
|
633
|
-
const filePath = path5.join(implDir, fileName);
|
|
634
|
-
const stubCode = `/**
|
|
635
|
-
* ${session.requirement}
|
|
636
|
-
*
|
|
637
|
-
* TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
|
|
638
|
-
*/
|
|
639
|
-
|
|
640
|
-
export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
641
|
-
// TODO: \u5B9E\u73B0\u529F\u80FD
|
|
642
|
-
console.log('${featureName} - \u5F85\u5B9E\u73B0');
|
|
643
|
-
}
|
|
644
|
-
`;
|
|
645
|
-
await fs4.writeFile(filePath, stubCode, "utf-8");
|
|
646
|
-
files.push(`src/features/${fileName}`);
|
|
644
|
+
} catch (error) {
|
|
645
|
+
loader.stop(chalk9.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
|
|
647
646
|
}
|
|
648
|
-
|
|
647
|
+
}
|
|
648
|
+
if (files.length > 0) {
|
|
649
649
|
return { success: true, files };
|
|
650
|
-
}
|
|
651
|
-
loader.stop();
|
|
650
|
+
} else {
|
|
652
651
|
return {
|
|
653
652
|
success: false,
|
|
654
653
|
files: [],
|
|
655
|
-
error:
|
|
654
|
+
error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
|
|
656
655
|
};
|
|
657
656
|
}
|
|
658
657
|
}
|
|
659
|
-
function
|
|
658
|
+
function buildTaskPrompt(session, item, index) {
|
|
660
659
|
const lines = [];
|
|
661
|
-
lines.push(
|
|
662
|
-
lines.push(
|
|
663
|
-
lines.push(
|
|
664
|
-
lines.push(
|
|
665
|
-
|
|
666
|
-
lines.push(
|
|
667
|
-
for (const
|
|
668
|
-
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}`);
|
|
669
668
|
}
|
|
670
669
|
}
|
|
671
670
|
lines.push("");
|
|
672
|
-
lines.push(
|
|
673
|
-
|
|
674
|
-
|
|
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
|
+
}
|
|
675
683
|
}
|
|
676
684
|
lines.push("");
|
|
677
|
-
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");
|
|
678
686
|
return lines.join("\n");
|
|
679
687
|
}
|
|
680
688
|
function parseCodeBlocks(content) {
|
|
@@ -1202,25 +1210,47 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
1202
1210
|
], {
|
|
1203
1211
|
temperature: 0.3,
|
|
1204
1212
|
maxTokens: 4e3,
|
|
1205
|
-
timeout:
|
|
1213
|
+
timeout: 18e4
|
|
1214
|
+
// 增加到3分钟
|
|
1206
1215
|
});
|
|
1207
1216
|
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
1208
1217
|
if (jsonMatch) {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
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 {
|
|
1219
1248
|
}
|
|
1220
|
-
loader.stop(chalk9.yellow(" \u26A0 \u89E3\u6790\
|
|
1249
|
+
loader.stop(chalk9.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
|
|
1221
1250
|
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1222
1251
|
} catch (error) {
|
|
1223
|
-
|
|
1252
|
+
const errMsg = error.message.slice(0, 80);
|
|
1253
|
+
loader.stop(chalk9.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
|
|
1224
1254
|
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1225
1255
|
}
|
|
1226
1256
|
}
|