@nick848/sf-cli 1.0.17 → 1.0.19
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 +351 -79
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +351 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +351 -79
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -180,7 +180,7 @@ async function executeWorkflow(ctx) {
|
|
|
180
180
|
for (const url of urls) {
|
|
181
181
|
lines.push(chalk9__default.default.gray(` \u{1F4CE} ${url}`));
|
|
182
182
|
try {
|
|
183
|
-
const resource = await fetchAndAnalyzeReference(url, ctx);
|
|
183
|
+
const resource = await fetchAndAnalyzeReference(url, ctx, activeSession.context || void 0);
|
|
184
184
|
activeSession.referenceResources.push(resource);
|
|
185
185
|
lines.push(chalk9__default.default.green(` \u2713 \u5DF2\u5206\u6790`));
|
|
186
186
|
activeSession.refinedRequirement += `
|
|
@@ -219,12 +219,26 @@ ${resource.analysis}`;
|
|
|
219
219
|
lines.push("");
|
|
220
220
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
221
221
|
lines.push("");
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
activeSession.
|
|
226
|
-
|
|
227
|
-
|
|
222
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
223
|
+
loader.start();
|
|
224
|
+
try {
|
|
225
|
+
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
226
|
+
activeSession.refinedRequirement,
|
|
227
|
+
activeSession.context,
|
|
228
|
+
activeSession.clarificationQuestions,
|
|
229
|
+
activeSession.referenceResources,
|
|
230
|
+
ctx
|
|
231
|
+
);
|
|
232
|
+
loader.stop(chalk9__default.default.green(" \u2713 BDD \u573A\u666F\u5DF2\u751F\u6210"));
|
|
233
|
+
} catch {
|
|
234
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840 BDD \u751F\u6210"));
|
|
235
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
236
|
+
activeSession.refinedRequirement,
|
|
237
|
+
activeSession.context,
|
|
238
|
+
activeSession.clarificationQuestions,
|
|
239
|
+
activeSession.referenceResources
|
|
240
|
+
);
|
|
241
|
+
}
|
|
228
242
|
for (const scenario of activeSession.bddScenarios) {
|
|
229
243
|
lines.push(chalk9__default.default.white(` Feature: ${scenario.feature}`));
|
|
230
244
|
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
@@ -271,7 +285,7 @@ ${resource.analysis}`;
|
|
|
271
285
|
lines.push("");
|
|
272
286
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
273
287
|
lines.push("");
|
|
274
|
-
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
288
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession, ctx);
|
|
275
289
|
lines.push(chalk9__default.default.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
276
290
|
for (const file of activeSession.testFiles) {
|
|
277
291
|
lines.push(chalk9__default.default.gray(` - ${file}`));
|
|
@@ -568,6 +582,8 @@ function getCategoryLabel(category) {
|
|
|
568
582
|
async function executeDevelopment(ctx, session) {
|
|
569
583
|
const workingDir = ctx.options.workingDirectory;
|
|
570
584
|
const files = [];
|
|
585
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
|
|
586
|
+
loader.start();
|
|
571
587
|
try {
|
|
572
588
|
const systemPrompt = buildDevelopmentPrompt(session);
|
|
573
589
|
const messages = [
|
|
@@ -575,7 +591,12 @@ async function executeDevelopment(ctx, session) {
|
|
|
575
591
|
role: "system",
|
|
576
592
|
content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
|
|
577
593
|
|
|
578
|
-
\u8981\
|
|
594
|
+
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
595
|
+
1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
596
|
+
2. \u53C2\u8003\u8D44\u6E90\uFF08\u5982\u679C\u6709\uFF09\u4EC5\u7528\u4E8E\u7406\u89E3\u4E1A\u52A1\u529F\u80FD\uFF0C\u4E0D\u8981\u590D\u5236\u5176\u6280\u672F\u5B9E\u73B0
|
|
597
|
+
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
|
|
598
|
+
|
|
599
|
+
\u4EE3\u7801\u8981\u6C42\uFF1A
|
|
579
600
|
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
580
601
|
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
581
602
|
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
@@ -587,19 +608,23 @@ async function executeDevelopment(ctx, session) {
|
|
|
587
608
|
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
588
609
|
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
589
610
|
|
|
590
|
-
${session.context?.devStandards ? `\u5F00\u53D1\u89C4\u8303\
|
|
591
|
-
${session.context.devStandards.slice(0,
|
|
611
|
+
${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
|
|
612
|
+
${session.context.devStandards.slice(0, 2500)}` : ""}`
|
|
592
613
|
},
|
|
593
614
|
{
|
|
594
615
|
role: "user",
|
|
595
616
|
content: systemPrompt
|
|
596
617
|
}
|
|
597
618
|
];
|
|
619
|
+
loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
|
|
598
620
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
599
621
|
temperature: 0.3,
|
|
600
622
|
maxTokens: 8e3,
|
|
601
|
-
agent: "frontend-dev"
|
|
623
|
+
agent: "frontend-dev",
|
|
624
|
+
timeout: 18e4
|
|
625
|
+
// 3 分钟超时
|
|
602
626
|
});
|
|
627
|
+
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
603
628
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
604
629
|
for (const block of codeBlocks) {
|
|
605
630
|
const filePath = path4__namespace.join(workingDir, block.filename);
|
|
@@ -628,8 +653,10 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
628
653
|
await fs4__namespace.writeFile(filePath, stubCode, "utf-8");
|
|
629
654
|
files.push(`src/features/${fileName}`);
|
|
630
655
|
}
|
|
656
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
|
|
631
657
|
return { success: true, files };
|
|
632
658
|
} catch (error) {
|
|
659
|
+
loader.stop();
|
|
633
660
|
return {
|
|
634
661
|
success: false,
|
|
635
662
|
files: [],
|
|
@@ -913,36 +940,130 @@ function generateBDDScenarios(requirement, context, questions, references = [])
|
|
|
913
940
|
}
|
|
914
941
|
return scenarios;
|
|
915
942
|
}
|
|
943
|
+
async function generateBDDScenariosWithAI(requirement, context, questions, references, ctx) {
|
|
944
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\u548C\u4E1A\u52A1\u5206\u6790\u5E08\u3002\u8BF7\u6839\u636E\u4EE5\u4E0B\u9700\u6C42\u751F\u6210\u8BE6\u7EC6\u7684 BDD (Behavior Driven Development) \u573A\u666F\u3002
|
|
945
|
+
|
|
946
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
947
|
+
${requirement}
|
|
948
|
+
|
|
949
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
950
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "\u672A\u6307\u5B9A"}
|
|
951
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
952
|
+
${context.devStandards ? `
|
|
953
|
+
## \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
954
|
+
${context.devStandards.slice(0, 2e3)}
|
|
955
|
+
` : ""}
|
|
956
|
+
|
|
957
|
+
## \u6F84\u6E05\u95EE\u7B54
|
|
958
|
+
${questions.filter((q) => q.answered).map((q) => `- Q: ${q.question}
|
|
959
|
+
A: ${q.answer}`).join("\n")}
|
|
960
|
+
|
|
961
|
+
## \u53C2\u8003\u8D44\u6E90\u5206\u6790\uFF08\u4EC5\u4F5C\u4E1A\u52A1\u53C2\u8003\uFF0C\u6280\u672F\u5B9E\u73B0\u9075\u5FAA\u9879\u76EE\u89C4\u8303\uFF09
|
|
962
|
+
${references.length > 0 ? references.map((r) => `### ${r.url}
|
|
963
|
+
${r.analysis}`).join("\n\n") : "\u65E0"}
|
|
964
|
+
|
|
965
|
+
## \u8981\u6C42
|
|
966
|
+
1. \u6BCF\u4E2A\u529F\u80FD\u6A21\u5757\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684 Feature
|
|
967
|
+
2. \u6BCF\u4E2A Feature \u5305\u542B\u591A\u4E2A\u5177\u4F53\u7684 Scenario
|
|
968
|
+
3. \u4F7F\u7528 Given-When-Then \u683C\u5F0F
|
|
969
|
+
4. \u573A\u666F\u8981\u8986\u76D6: \u6B63\u5E38\u6D41\u7A0B\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u5F02\u5E38\u5904\u7406
|
|
970
|
+
5. \u573A\u666F\u8981\u5177\u4F53\u53EF\u6D4B\u8BD5\uFF0C\u805A\u7126\u4E1A\u52A1\u903B\u8F91
|
|
971
|
+
6. \u26A0\uFE0F \u53C2\u8003\u8D44\u6E90\u4EC5\u7528\u4E8E\u7406\u89E3\u4E1A\u52A1\u529F\u80FD\uFF0C\u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u9075\u5FAA\u9879\u76EE\u89C4\u8303
|
|
972
|
+
|
|
973
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
974
|
+
\`\`\`json
|
|
975
|
+
[
|
|
976
|
+
{
|
|
977
|
+
"feature": "\u529F\u80FD\u540D\u79F0",
|
|
978
|
+
"description": "\u529F\u80FD\u63CF\u8FF0",
|
|
979
|
+
"scenarios": [
|
|
980
|
+
{
|
|
981
|
+
"name": "\u573A\u666F\u540D\u79F0",
|
|
982
|
+
"given": ["\u524D\u7F6E\u6761\u4EF61", "\u524D\u7F6E\u6761\u4EF62"],
|
|
983
|
+
"when": ["\u64CD\u4F5C1", "\u64CD\u4F5C2"],
|
|
984
|
+
"then": ["\u9884\u671F\u7ED3\u679C1", "\u9884\u671F\u7ED3\u679C2"]
|
|
985
|
+
}
|
|
986
|
+
]
|
|
987
|
+
}
|
|
988
|
+
]
|
|
989
|
+
\`\`\`
|
|
990
|
+
|
|
991
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9\u3002`;
|
|
992
|
+
const response = await ctx.modelService.sendMessage([
|
|
993
|
+
{ role: "user", content: prompt2 }
|
|
994
|
+
], {
|
|
995
|
+
temperature: 0.3,
|
|
996
|
+
maxTokens: 4e3,
|
|
997
|
+
timeout: 12e4
|
|
998
|
+
});
|
|
999
|
+
try {
|
|
1000
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
1001
|
+
if (jsonMatch) {
|
|
1002
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
1003
|
+
}
|
|
1004
|
+
return JSON.parse(response.content);
|
|
1005
|
+
} catch {
|
|
1006
|
+
return generateBDDScenarios(requirement, context, questions, references);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
916
1009
|
function extractFeaturesFromReference(ref) {
|
|
917
1010
|
const features = [];
|
|
918
|
-
const analysis = ref.analysis
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
930
|
-
hasInput: false
|
|
931
|
-
});
|
|
1011
|
+
const analysis = ref.analysis;
|
|
1012
|
+
const featureSection = analysis.match(/###?\s*4\.\s*功能拆分建议[\s\S]*?(?=###?\s*\d|$)/i);
|
|
1013
|
+
if (featureSection) {
|
|
1014
|
+
const taskMatches = featureSection[0].matchAll(/[-*]\s*\*\*([^*]+)\*\*[::]?\s*([^\n]+)/g);
|
|
1015
|
+
for (const match of taskMatches) {
|
|
1016
|
+
features.push({
|
|
1017
|
+
title: match[1].trim(),
|
|
1018
|
+
description: match[2].trim(),
|
|
1019
|
+
hasInput: match[2].includes("\u8F93\u5165") || match[2].includes("\u8868\u5355") || match[2].includes("\u7528\u6237")
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
932
1022
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1023
|
+
const bizSection = analysis.match(/###?\s*1\.\s*业务功能分析[\s\S]*?(?=###?\s*\d|$)/i);
|
|
1024
|
+
if (bizSection && features.length === 0) {
|
|
1025
|
+
const lines = bizSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
1026
|
+
for (const line of lines.slice(0, 5)) {
|
|
1027
|
+
const content = line.replace(/^[-*]\s*/, "").trim();
|
|
1028
|
+
if (content.length > 5) {
|
|
1029
|
+
features.push({
|
|
1030
|
+
title: content.slice(0, 20),
|
|
1031
|
+
description: content,
|
|
1032
|
+
hasInput: content.includes("\u8F93\u5165") || content.includes("\u586B\u5199")
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
939
1036
|
}
|
|
940
|
-
if (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1037
|
+
if (features.length === 0) {
|
|
1038
|
+
const lowerAnalysis = analysis.toLowerCase();
|
|
1039
|
+
if (lowerAnalysis.includes("\u8F93\u5165") || lowerAnalysis.includes("\u8868\u5355")) {
|
|
1040
|
+
features.push({
|
|
1041
|
+
title: "\u8F93\u5165\u8868\u5355",
|
|
1042
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
|
|
1043
|
+
hasInput: true
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (lowerAnalysis.includes("\u6309\u94AE") || lowerAnalysis.includes("\u64CD\u4F5C")) {
|
|
1047
|
+
features.push({
|
|
1048
|
+
title: "\u4EA4\u4E92\u6309\u94AE",
|
|
1049
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
1050
|
+
hasInput: false
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
if (lowerAnalysis.includes("\u8868\u683C") || lowerAnalysis.includes("\u5217\u8868")) {
|
|
1054
|
+
features.push({
|
|
1055
|
+
title: "\u6570\u636E\u5217\u8868",
|
|
1056
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
|
|
1057
|
+
hasInput: false
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
if (lowerAnalysis.includes("\u56FE\u8868") || lowerAnalysis.includes("\u53EF\u89C6\u5316")) {
|
|
1061
|
+
features.push({
|
|
1062
|
+
title: "\u56FE\u8868\u5C55\u793A",
|
|
1063
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
|
|
1064
|
+
hasInput: false
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
946
1067
|
}
|
|
947
1068
|
if (features.length === 0) {
|
|
948
1069
|
features.push({
|
|
@@ -1106,33 +1227,119 @@ function formatSpecFile(session) {
|
|
|
1106
1227
|
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
1107
1228
|
return lines.join("\n");
|
|
1108
1229
|
}
|
|
1109
|
-
async function generateTests(workingDir, session) {
|
|
1230
|
+
async function generateTests(workingDir, session, ctx) {
|
|
1110
1231
|
const testDir = path4__namespace.join(workingDir, "tests");
|
|
1111
1232
|
await fs4__namespace.mkdir(testDir, { recursive: true });
|
|
1112
1233
|
const testFiles = [];
|
|
1113
|
-
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1234
|
+
if (ctx?.modelService) {
|
|
1235
|
+
for (const scenario of session.bddScenarios) {
|
|
1236
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1237
|
+
const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
|
|
1238
|
+
const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
1239
|
+
loader.start();
|
|
1240
|
+
try {
|
|
1241
|
+
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
1242
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1243
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1244
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
1245
|
+
} catch {
|
|
1246
|
+
const content = generateTestFile(scenario, session);
|
|
1247
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1248
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1249
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1253
|
+
for (const scenario of session.bddScenarios) {
|
|
1254
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1255
|
+
const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
|
|
1256
|
+
const content = generateTestFile(scenario, session);
|
|
1257
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1258
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1259
|
+
}
|
|
1119
1260
|
}
|
|
1120
1261
|
return testFiles;
|
|
1121
1262
|
}
|
|
1122
|
-
function
|
|
1263
|
+
async function generateTestFileWithAI(scenario, session, ctx) {
|
|
1264
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EE5\u4E0B BDD \u573A\u666F\u751F\u6210\u5B8C\u6574\u7684 Vitest \u6D4B\u8BD5\u4EE3\u7801\u3002
|
|
1265
|
+
|
|
1266
|
+
## \u529F\u80FD\u540D\u79F0
|
|
1267
|
+
${scenario.feature}
|
|
1268
|
+
|
|
1269
|
+
## BDD \u573A\u666F
|
|
1270
|
+
${scenario.scenarios.map((s) => `
|
|
1271
|
+
### ${s.name}
|
|
1272
|
+
- Given: ${s.given.join(", ")}
|
|
1273
|
+
- When: ${s.when.join(", ")}
|
|
1274
|
+
- Then: ${s.then.join(", ")}
|
|
1275
|
+
`).join("\n")}
|
|
1276
|
+
|
|
1277
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1278
|
+
- \u6280\u672F\u6808: ${session.context?.techStack?.join(", ") || "TypeScript"}
|
|
1279
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1280
|
+
|
|
1281
|
+
## \u8981\u6C42
|
|
1282
|
+
1. \u4F7F\u7528 vitest \u6D4B\u8BD5\u6846\u67B6 (describe, it, expect, beforeEach \u7B49)
|
|
1283
|
+
2. \u6BCF\u4E2A\u573A\u666F\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
1284
|
+
3. \u6D4B\u8BD5\u4EE3\u7801\u8981\u5B8C\u6574\u53EF\u8FD0\u884C\uFF0C\u5305\u542B\u5FC5\u8981\u7684 mock \u548C setup
|
|
1285
|
+
4. \u4F7F\u7528\u4E2D\u6587\u6CE8\u91CA\u8BF4\u660E\u6D4B\u8BD5\u610F\u56FE
|
|
1286
|
+
5. \u6D4B\u8BD5\u8981\u8986\u76D6\u6B63\u5E38\u6D41\u7A0B\u548C\u8FB9\u754C\u60C5\u51B5
|
|
1287
|
+
|
|
1288
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u9700\u8981\u89E3\u91CA\u3002`;
|
|
1289
|
+
const response = await ctx.modelService.sendMessage([
|
|
1290
|
+
{ role: "user", content: prompt2 }
|
|
1291
|
+
], {
|
|
1292
|
+
temperature: 0.3,
|
|
1293
|
+
maxTokens: 4e3
|
|
1294
|
+
});
|
|
1295
|
+
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
1296
|
+
if (codeMatch) {
|
|
1297
|
+
return codeMatch[1].trim();
|
|
1298
|
+
}
|
|
1299
|
+
return response.content;
|
|
1300
|
+
}
|
|
1301
|
+
function generateTestFile(scenario, session) {
|
|
1123
1302
|
const lines = [];
|
|
1124
|
-
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
1303
|
+
lines.push(`import { describe, it, expect, beforeEach } from 'vitest';`);
|
|
1125
1304
|
lines.push("");
|
|
1305
|
+
lines.push(`/**`);
|
|
1306
|
+
lines.push(` * ${scenario.feature} \u529F\u80FD\u6D4B\u8BD5`);
|
|
1307
|
+
lines.push(` * `);
|
|
1308
|
+
lines.push(` * BDD \u573A\u666F\u6570\u91CF: ${scenario.scenarios.length}`);
|
|
1309
|
+
if (session?.context?.techStack) {
|
|
1310
|
+
lines.push(` * \u6280\u672F\u6808: ${session.context.techStack.join(", ")}`);
|
|
1311
|
+
}
|
|
1312
|
+
lines.push(` */`);
|
|
1126
1313
|
lines.push(`describe('${scenario.feature}', () => {`);
|
|
1127
1314
|
for (const s of scenario.scenarios) {
|
|
1128
|
-
lines.push(` it('${s.name}', () => {`);
|
|
1129
|
-
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
1130
|
-
lines.push(` // When: ${s.when.join(", ")}`);
|
|
1131
|
-
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
1132
|
-
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
1133
|
-
lines.push(` });`);
|
|
1134
1315
|
lines.push("");
|
|
1316
|
+
lines.push(` /**`);
|
|
1317
|
+
lines.push(` * \u573A\u666F: ${s.name}`);
|
|
1318
|
+
lines.push(` * Given: ${s.given.join(", ")}`);
|
|
1319
|
+
lines.push(` * When: ${s.when.join(", ")}`);
|
|
1320
|
+
lines.push(` * Then: ${s.then.join(", ")}`);
|
|
1321
|
+
lines.push(` */`);
|
|
1322
|
+
lines.push(` it('${s.name}', async () => {`);
|
|
1323
|
+
lines.push(` // Arrange (Given)`);
|
|
1324
|
+
for (const g of s.given) {
|
|
1325
|
+
lines.push(` // ${g}`);
|
|
1326
|
+
}
|
|
1327
|
+
lines.push(` const input = {}; // TODO: \u8BBE\u7F6E\u521D\u59CB\u72B6\u6001`);
|
|
1328
|
+
lines.push("");
|
|
1329
|
+
lines.push(` // Act (When)`);
|
|
1330
|
+
for (const w of s.when) {
|
|
1331
|
+
lines.push(` // ${w}`);
|
|
1332
|
+
}
|
|
1333
|
+
lines.push(` const result = {}; // TODO: \u6267\u884C\u64CD\u4F5C`);
|
|
1334
|
+
lines.push("");
|
|
1335
|
+
lines.push(` // Assert (Then)`);
|
|
1336
|
+
for (const t of s.then) {
|
|
1337
|
+
lines.push(` // ${t}`);
|
|
1338
|
+
}
|
|
1339
|
+
lines.push(` expect(result).toBeDefined(); // TODO: \u5B8C\u5584\u65AD\u8A00`);
|
|
1340
|
+
lines.push(` });`);
|
|
1135
1341
|
}
|
|
1342
|
+
lines.push("");
|
|
1136
1343
|
lines.push(`});`);
|
|
1137
1344
|
return lines.join("\n");
|
|
1138
1345
|
}
|
|
@@ -1178,7 +1385,7 @@ function extractUrls(text) {
|
|
|
1178
1385
|
const matches = text.match(urlRegex);
|
|
1179
1386
|
return matches ? [...new Set(matches)] : [];
|
|
1180
1387
|
}
|
|
1181
|
-
async function fetchAndAnalyzeReference(url, ctx) {
|
|
1388
|
+
async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
1182
1389
|
const type = detectResourceType(url);
|
|
1183
1390
|
let content = "";
|
|
1184
1391
|
let analysis = "";
|
|
@@ -1193,7 +1400,7 @@ async function fetchAndAnalyzeReference(url, ctx) {
|
|
|
1193
1400
|
}
|
|
1194
1401
|
content = await response.text();
|
|
1195
1402
|
if (ctx.modelService.getCurrentModel()) {
|
|
1196
|
-
analysis = await analyzeReferenceContent(url, content, type, ctx);
|
|
1403
|
+
analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
|
|
1197
1404
|
} else {
|
|
1198
1405
|
analysis = extractBasicInfo(content, type);
|
|
1199
1406
|
}
|
|
@@ -1214,40 +1421,72 @@ function detectResourceType(url) {
|
|
|
1214
1421
|
}
|
|
1215
1422
|
return "webpage";
|
|
1216
1423
|
}
|
|
1217
|
-
async function analyzeReferenceContent(url, content, type, ctx) {
|
|
1218
|
-
const typePrompts = {
|
|
1219
|
-
webpage: "\u5206\u6790\u8FD9\u4E2A\u7F51\u9875\u7684\u529F\u80FD\u3001UI\u7EC4\u4EF6\u548C\u4EA4\u4E92\u65B9\u5F0F",
|
|
1220
|
-
design: "\u5206\u6790\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F\u7684\u5E03\u5C40\u3001\u7EC4\u4EF6\u548C\u6837\u5F0F",
|
|
1221
|
-
image: "\u63CF\u8FF0\u8FD9\u4E2A\u56FE\u7247\u7684\u5185\u5BB9\u548C\u8BBE\u8BA1\u5143\u7D20",
|
|
1222
|
-
api: "\u5206\u6790\u8FD9\u4E2AAPI\u7684\u7ED3\u6784\u548C\u53C2\u6570"
|
|
1223
|
-
};
|
|
1424
|
+
async function analyzeReferenceContent(url, content, type, ctx, projectContext) {
|
|
1224
1425
|
const prompt2 = `
|
|
1225
|
-
\u8BF7\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\
|
|
1426
|
+
\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4EA7\u54C1\u7ECF\u7406\u3002\u8BF7\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\uFF0C\u63D0\u53D6\u4E1A\u52A1\u529F\u80FD\u548C\u754C\u9762\u7ED3\u6784\u3002
|
|
1427
|
+
|
|
1428
|
+
## \u91CD\u8981\u8BF4\u660E
|
|
1429
|
+
\u26A0\uFE0F \u6B64\u5206\u6790\u4EC5\u7528\u4E8E\u7406\u89E3\u4E1A\u52A1\u529F\u80FD\uFF0C**\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u5FC5\u987B\u9075\u5FAA\u5F53\u524D\u9879\u76EE\u7684\u5F00\u53D1\u89C4\u8303**\u3002
|
|
1430
|
+
\u53C2\u8003\u8D44\u6E90\u4E2D\u7684\u6280\u672F\u6808\u3001\u6846\u67B6\u3001\u4EE3\u7801\u98CE\u683C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u5B9E\u9645\u5F00\u53D1\u5E94\u4F7F\u7528\u9879\u76EE\u73B0\u6709\u89C4\u8303\u3002
|
|
1431
|
+
|
|
1432
|
+
${projectContext ? `## \u5F53\u524D\u9879\u76EE\u89C4\u8303
|
|
1433
|
+
- \u9879\u76EE\u540D\u79F0: ${projectContext.name}
|
|
1434
|
+
- \u6280\u672F\u6808: ${projectContext.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
1435
|
+
- \u6846\u67B6: ${projectContext.framework || "\u672A\u6307\u5B9A"}
|
|
1436
|
+
- \u5F00\u53D1\u89C4\u8303\u6458\u8981: ${projectContext.devStandards?.slice(0, 1500) || "\u672A\u6307\u5B9A"}
|
|
1437
|
+
` : ""}
|
|
1438
|
+
|
|
1439
|
+
## \u53C2\u8003\u8D44\u6E90\u4FE1\u606F
|
|
1440
|
+
- URL: ${url}
|
|
1441
|
+
- \u7C7B\u578B: ${type}
|
|
1442
|
+
|
|
1443
|
+
## \u7F51\u9875\u5185\u5BB9
|
|
1444
|
+
\`\`\`html
|
|
1445
|
+
${content.slice(0, 8e3)}
|
|
1446
|
+
\`\`\`
|
|
1447
|
+
|
|
1448
|
+
## \u5206\u6790\u8981\u6C42
|
|
1449
|
+
|
|
1450
|
+
\u8BF7\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u8FDB\u884C\u5206\u6790\uFF1A
|
|
1226
1451
|
|
|
1227
|
-
|
|
1228
|
-
\
|
|
1452
|
+
### 1. \u4E1A\u52A1\u529F\u80FD\u5206\u6790\uFF08\u91CD\u70B9\uFF09
|
|
1453
|
+
- \u6838\u5FC3\u4E1A\u52A1\u529F\u80FD\u662F\u4EC0\u4E48\uFF1F\uFF08\u8BE6\u7EC6\u63CF\u8FF0\u7528\u6237\u80FD\u505A\u4EC0\u4E48\uFF09
|
|
1454
|
+
- \u4E1A\u52A1\u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F\uFF08\u7528\u6237\u64CD\u4F5C\u6B65\u9AA4\uFF09
|
|
1455
|
+
- \u4E1A\u52A1\u89C4\u5219\u662F\u4EC0\u4E48\uFF1F\uFF08\u8F93\u5165\u9650\u5236\u3001\u8BA1\u7B97\u89C4\u5219\u7B49\uFF09
|
|
1229
1456
|
|
|
1230
|
-
\
|
|
1231
|
-
|
|
1457
|
+
### 2. UI/UX \u7ED3\u6784\u5206\u6790
|
|
1458
|
+
- \u9875\u9762\u5E03\u5C40\u7ED3\u6784\uFF08\u4E0D\u6D89\u53CA\u5177\u4F53\u6280\u672F\u5B9E\u73B0\uFF09
|
|
1459
|
+
- \u4E3B\u8981\u7EC4\u4EF6/\u6A21\u5757\u6709\u54EA\u4E9B\uFF1F
|
|
1460
|
+
- \u4EA4\u4E92\u65B9\u5F0F\uFF08\u70B9\u51FB\u3001\u8F93\u5165\u3001\u62D6\u62FD\u7B49\uFF09
|
|
1232
1461
|
|
|
1233
|
-
|
|
1462
|
+
### 3. \u6570\u636E\u6A21\u578B\u5206\u6790
|
|
1463
|
+
- \u9700\u8981\u54EA\u4E9B\u6570\u636E\u5B57\u6BB5\uFF1F
|
|
1464
|
+
- \u6570\u636E\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1465
|
+
- \u6570\u636E\u6765\u6E90\uFF08\u7528\u6237\u8F93\u5165/\u8BA1\u7B97/API\uFF09
|
|
1234
1466
|
|
|
1235
|
-
\
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
|
|
1467
|
+
### 4. \u529F\u80FD\u62C6\u5206\u5EFA\u8BAE
|
|
1468
|
+
\u5C06\u529F\u80FD\u62C6\u5206\u4E3A\u53EF\u72EC\u7ACB\u5F00\u53D1\u7684\u4EFB\u52A1\uFF1A
|
|
1469
|
+
- \u4EFB\u52A1\u540D\u79F0\uFF08\u7B80\u77ED\u660E\u786E\uFF09
|
|
1470
|
+
- \u4EFB\u52A1\u63CF\u8FF0\uFF08\u4E1A\u52A1\u89D2\u5EA6\uFF09
|
|
1471
|
+
- \u9A8C\u6536\u6807\u51C6
|
|
1241
1472
|
|
|
1242
|
-
\
|
|
1473
|
+
\u6CE8\u610F\uFF1A\u8BF7\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\uFF0C\u91CD\u70B9\u7A81\u51FA**\u4E1A\u52A1\u903B\u8F91**\u548C**\u529F\u80FD\u7279\u6027**\u3002
|
|
1474
|
+
\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
|
|
1243
1475
|
`;
|
|
1476
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
1477
|
+
loader.start();
|
|
1244
1478
|
try {
|
|
1245
1479
|
const response = await ctx.modelService.sendMessage([
|
|
1246
1480
|
{ role: "user", content: prompt2 }
|
|
1247
|
-
], {
|
|
1481
|
+
], {
|
|
1482
|
+
temperature: 0.3,
|
|
1483
|
+
maxTokens: 4e3
|
|
1484
|
+
});
|
|
1485
|
+
loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
1248
1486
|
return response.content;
|
|
1249
|
-
} catch {
|
|
1250
|
-
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
loader.stop();
|
|
1489
|
+
throw error;
|
|
1251
1490
|
}
|
|
1252
1491
|
}
|
|
1253
1492
|
function extractBasicInfo(content, type) {
|
|
@@ -1288,10 +1527,43 @@ function getActiveSession() {
|
|
|
1288
1527
|
function clearActiveSession() {
|
|
1289
1528
|
activeSession = null;
|
|
1290
1529
|
}
|
|
1291
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1530
|
+
var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1292
1531
|
var init_new = __esm({
|
|
1293
1532
|
"src/commands/new.ts"() {
|
|
1294
1533
|
init_cjs_shims();
|
|
1534
|
+
LoadingIndicator = class {
|
|
1535
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1536
|
+
frameIndex = 0;
|
|
1537
|
+
interval = null;
|
|
1538
|
+
message;
|
|
1539
|
+
constructor(message) {
|
|
1540
|
+
this.message = message;
|
|
1541
|
+
}
|
|
1542
|
+
start() {
|
|
1543
|
+
process.stdout.write("\x1B[?25l");
|
|
1544
|
+
this.interval = setInterval(() => {
|
|
1545
|
+
const frame = this.frames[this.frameIndex];
|
|
1546
|
+
process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
|
|
1547
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1548
|
+
}, 80);
|
|
1549
|
+
}
|
|
1550
|
+
update(message) {
|
|
1551
|
+
this.message = message;
|
|
1552
|
+
}
|
|
1553
|
+
stop(finalMessage) {
|
|
1554
|
+
if (this.interval) {
|
|
1555
|
+
clearInterval(this.interval);
|
|
1556
|
+
this.interval = null;
|
|
1557
|
+
}
|
|
1558
|
+
process.stdout.write("\x1B[?25h");
|
|
1559
|
+
if (finalMessage) {
|
|
1560
|
+
process.stdout.write(`\r${finalMessage}
|
|
1561
|
+
`);
|
|
1562
|
+
} else {
|
|
1563
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1295
1567
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
1296
1568
|
COMPLEXITY_THRESHOLD = 6;
|
|
1297
1569
|
CLARITY_THRESHOLD = 0.6;
|