@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.mjs
CHANGED
|
@@ -155,7 +155,7 @@ async function executeWorkflow(ctx) {
|
|
|
155
155
|
for (const url of urls) {
|
|
156
156
|
lines.push(chalk9.gray(` \u{1F4CE} ${url}`));
|
|
157
157
|
try {
|
|
158
|
-
const resource = await fetchAndAnalyzeReference(url, ctx);
|
|
158
|
+
const resource = await fetchAndAnalyzeReference(url, ctx, activeSession.context || void 0);
|
|
159
159
|
activeSession.referenceResources.push(resource);
|
|
160
160
|
lines.push(chalk9.green(` \u2713 \u5DF2\u5206\u6790`));
|
|
161
161
|
activeSession.refinedRequirement += `
|
|
@@ -194,12 +194,26 @@ ${resource.analysis}`;
|
|
|
194
194
|
lines.push("");
|
|
195
195
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
196
196
|
lines.push("");
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
activeSession.
|
|
201
|
-
|
|
202
|
-
|
|
197
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
198
|
+
loader.start();
|
|
199
|
+
try {
|
|
200
|
+
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
201
|
+
activeSession.refinedRequirement,
|
|
202
|
+
activeSession.context,
|
|
203
|
+
activeSession.clarificationQuestions,
|
|
204
|
+
activeSession.referenceResources,
|
|
205
|
+
ctx
|
|
206
|
+
);
|
|
207
|
+
loader.stop(chalk9.green(" \u2713 BDD \u573A\u666F\u5DF2\u751F\u6210"));
|
|
208
|
+
} catch {
|
|
209
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840 BDD \u751F\u6210"));
|
|
210
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
211
|
+
activeSession.refinedRequirement,
|
|
212
|
+
activeSession.context,
|
|
213
|
+
activeSession.clarificationQuestions,
|
|
214
|
+
activeSession.referenceResources
|
|
215
|
+
);
|
|
216
|
+
}
|
|
203
217
|
for (const scenario of activeSession.bddScenarios) {
|
|
204
218
|
lines.push(chalk9.white(` Feature: ${scenario.feature}`));
|
|
205
219
|
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
@@ -246,7 +260,7 @@ ${resource.analysis}`;
|
|
|
246
260
|
lines.push("");
|
|
247
261
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
248
262
|
lines.push("");
|
|
249
|
-
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
263
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession, ctx);
|
|
250
264
|
lines.push(chalk9.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
251
265
|
for (const file of activeSession.testFiles) {
|
|
252
266
|
lines.push(chalk9.gray(` - ${file}`));
|
|
@@ -543,6 +557,8 @@ function getCategoryLabel(category) {
|
|
|
543
557
|
async function executeDevelopment(ctx, session) {
|
|
544
558
|
const workingDir = ctx.options.workingDirectory;
|
|
545
559
|
const files = [];
|
|
560
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
|
|
561
|
+
loader.start();
|
|
546
562
|
try {
|
|
547
563
|
const systemPrompt = buildDevelopmentPrompt(session);
|
|
548
564
|
const messages = [
|
|
@@ -550,7 +566,12 @@ async function executeDevelopment(ctx, session) {
|
|
|
550
566
|
role: "system",
|
|
551
567
|
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
|
|
552
568
|
|
|
553
|
-
\u8981\
|
|
569
|
+
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
570
|
+
1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
571
|
+
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
|
|
572
|
+
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
|
|
573
|
+
|
|
574
|
+
\u4EE3\u7801\u8981\u6C42\uFF1A
|
|
554
575
|
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
555
576
|
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
556
577
|
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
@@ -562,19 +583,23 @@ async function executeDevelopment(ctx, session) {
|
|
|
562
583
|
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
563
584
|
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
564
585
|
|
|
565
|
-
${session.context?.devStandards ? `\u5F00\u53D1\u89C4\u8303\
|
|
566
|
-
${session.context.devStandards.slice(0,
|
|
586
|
+
${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
|
|
587
|
+
${session.context.devStandards.slice(0, 2500)}` : ""}`
|
|
567
588
|
},
|
|
568
589
|
{
|
|
569
590
|
role: "user",
|
|
570
591
|
content: systemPrompt
|
|
571
592
|
}
|
|
572
593
|
];
|
|
594
|
+
loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
|
|
573
595
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
574
596
|
temperature: 0.3,
|
|
575
597
|
maxTokens: 8e3,
|
|
576
|
-
agent: "frontend-dev"
|
|
598
|
+
agent: "frontend-dev",
|
|
599
|
+
timeout: 18e4
|
|
600
|
+
// 3 分钟超时
|
|
577
601
|
});
|
|
602
|
+
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
578
603
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
579
604
|
for (const block of codeBlocks) {
|
|
580
605
|
const filePath = path5.join(workingDir, block.filename);
|
|
@@ -603,8 +628,10 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
603
628
|
await fs4.writeFile(filePath, stubCode, "utf-8");
|
|
604
629
|
files.push(`src/features/${fileName}`);
|
|
605
630
|
}
|
|
631
|
+
loader.stop(chalk9.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
|
|
606
632
|
return { success: true, files };
|
|
607
633
|
} catch (error) {
|
|
634
|
+
loader.stop();
|
|
608
635
|
return {
|
|
609
636
|
success: false,
|
|
610
637
|
files: [],
|
|
@@ -888,36 +915,130 @@ function generateBDDScenarios(requirement, context, questions, references = [])
|
|
|
888
915
|
}
|
|
889
916
|
return scenarios;
|
|
890
917
|
}
|
|
918
|
+
async function generateBDDScenariosWithAI(requirement, context, questions, references, ctx) {
|
|
919
|
+
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
|
|
920
|
+
|
|
921
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
922
|
+
${requirement}
|
|
923
|
+
|
|
924
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
925
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "\u672A\u6307\u5B9A"}
|
|
926
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
927
|
+
${context.devStandards ? `
|
|
928
|
+
## \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
929
|
+
${context.devStandards.slice(0, 2e3)}
|
|
930
|
+
` : ""}
|
|
931
|
+
|
|
932
|
+
## \u6F84\u6E05\u95EE\u7B54
|
|
933
|
+
${questions.filter((q) => q.answered).map((q) => `- Q: ${q.question}
|
|
934
|
+
A: ${q.answer}`).join("\n")}
|
|
935
|
+
|
|
936
|
+
## \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
|
|
937
|
+
${references.length > 0 ? references.map((r) => `### ${r.url}
|
|
938
|
+
${r.analysis}`).join("\n\n") : "\u65E0"}
|
|
939
|
+
|
|
940
|
+
## \u8981\u6C42
|
|
941
|
+
1. \u6BCF\u4E2A\u529F\u80FD\u6A21\u5757\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684 Feature
|
|
942
|
+
2. \u6BCF\u4E2A Feature \u5305\u542B\u591A\u4E2A\u5177\u4F53\u7684 Scenario
|
|
943
|
+
3. \u4F7F\u7528 Given-When-Then \u683C\u5F0F
|
|
944
|
+
4. \u573A\u666F\u8981\u8986\u76D6: \u6B63\u5E38\u6D41\u7A0B\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u5F02\u5E38\u5904\u7406
|
|
945
|
+
5. \u573A\u666F\u8981\u5177\u4F53\u53EF\u6D4B\u8BD5\uFF0C\u805A\u7126\u4E1A\u52A1\u903B\u8F91
|
|
946
|
+
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
|
|
947
|
+
|
|
948
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
949
|
+
\`\`\`json
|
|
950
|
+
[
|
|
951
|
+
{
|
|
952
|
+
"feature": "\u529F\u80FD\u540D\u79F0",
|
|
953
|
+
"description": "\u529F\u80FD\u63CF\u8FF0",
|
|
954
|
+
"scenarios": [
|
|
955
|
+
{
|
|
956
|
+
"name": "\u573A\u666F\u540D\u79F0",
|
|
957
|
+
"given": ["\u524D\u7F6E\u6761\u4EF61", "\u524D\u7F6E\u6761\u4EF62"],
|
|
958
|
+
"when": ["\u64CD\u4F5C1", "\u64CD\u4F5C2"],
|
|
959
|
+
"then": ["\u9884\u671F\u7ED3\u679C1", "\u9884\u671F\u7ED3\u679C2"]
|
|
960
|
+
}
|
|
961
|
+
]
|
|
962
|
+
}
|
|
963
|
+
]
|
|
964
|
+
\`\`\`
|
|
965
|
+
|
|
966
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9\u3002`;
|
|
967
|
+
const response = await ctx.modelService.sendMessage([
|
|
968
|
+
{ role: "user", content: prompt2 }
|
|
969
|
+
], {
|
|
970
|
+
temperature: 0.3,
|
|
971
|
+
maxTokens: 4e3,
|
|
972
|
+
timeout: 12e4
|
|
973
|
+
});
|
|
974
|
+
try {
|
|
975
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
976
|
+
if (jsonMatch) {
|
|
977
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
978
|
+
}
|
|
979
|
+
return JSON.parse(response.content);
|
|
980
|
+
} catch {
|
|
981
|
+
return generateBDDScenarios(requirement, context, questions, references);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
891
984
|
function extractFeaturesFromReference(ref) {
|
|
892
985
|
const features = [];
|
|
893
|
-
const analysis = ref.analysis
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
905
|
-
hasInput: false
|
|
906
|
-
});
|
|
986
|
+
const analysis = ref.analysis;
|
|
987
|
+
const featureSection = analysis.match(/###?\s*4\.\s*功能拆分建议[\s\S]*?(?=###?\s*\d|$)/i);
|
|
988
|
+
if (featureSection) {
|
|
989
|
+
const taskMatches = featureSection[0].matchAll(/[-*]\s*\*\*([^*]+)\*\*[::]?\s*([^\n]+)/g);
|
|
990
|
+
for (const match of taskMatches) {
|
|
991
|
+
features.push({
|
|
992
|
+
title: match[1].trim(),
|
|
993
|
+
description: match[2].trim(),
|
|
994
|
+
hasInput: match[2].includes("\u8F93\u5165") || match[2].includes("\u8868\u5355") || match[2].includes("\u7528\u6237")
|
|
995
|
+
});
|
|
996
|
+
}
|
|
907
997
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
998
|
+
const bizSection = analysis.match(/###?\s*1\.\s*业务功能分析[\s\S]*?(?=###?\s*\d|$)/i);
|
|
999
|
+
if (bizSection && features.length === 0) {
|
|
1000
|
+
const lines = bizSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
1001
|
+
for (const line of lines.slice(0, 5)) {
|
|
1002
|
+
const content = line.replace(/^[-*]\s*/, "").trim();
|
|
1003
|
+
if (content.length > 5) {
|
|
1004
|
+
features.push({
|
|
1005
|
+
title: content.slice(0, 20),
|
|
1006
|
+
description: content,
|
|
1007
|
+
hasInput: content.includes("\u8F93\u5165") || content.includes("\u586B\u5199")
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
914
1011
|
}
|
|
915
|
-
if (
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1012
|
+
if (features.length === 0) {
|
|
1013
|
+
const lowerAnalysis = analysis.toLowerCase();
|
|
1014
|
+
if (lowerAnalysis.includes("\u8F93\u5165") || lowerAnalysis.includes("\u8868\u5355")) {
|
|
1015
|
+
features.push({
|
|
1016
|
+
title: "\u8F93\u5165\u8868\u5355",
|
|
1017
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
|
|
1018
|
+
hasInput: true
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
if (lowerAnalysis.includes("\u6309\u94AE") || lowerAnalysis.includes("\u64CD\u4F5C")) {
|
|
1022
|
+
features.push({
|
|
1023
|
+
title: "\u4EA4\u4E92\u6309\u94AE",
|
|
1024
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
1025
|
+
hasInput: false
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
if (lowerAnalysis.includes("\u8868\u683C") || lowerAnalysis.includes("\u5217\u8868")) {
|
|
1029
|
+
features.push({
|
|
1030
|
+
title: "\u6570\u636E\u5217\u8868",
|
|
1031
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
|
|
1032
|
+
hasInput: false
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
if (lowerAnalysis.includes("\u56FE\u8868") || lowerAnalysis.includes("\u53EF\u89C6\u5316")) {
|
|
1036
|
+
features.push({
|
|
1037
|
+
title: "\u56FE\u8868\u5C55\u793A",
|
|
1038
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
|
|
1039
|
+
hasInput: false
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
921
1042
|
}
|
|
922
1043
|
if (features.length === 0) {
|
|
923
1044
|
features.push({
|
|
@@ -1081,33 +1202,119 @@ function formatSpecFile(session) {
|
|
|
1081
1202
|
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
1082
1203
|
return lines.join("\n");
|
|
1083
1204
|
}
|
|
1084
|
-
async function generateTests(workingDir, session) {
|
|
1205
|
+
async function generateTests(workingDir, session, ctx) {
|
|
1085
1206
|
const testDir = path5.join(workingDir, "tests");
|
|
1086
1207
|
await fs4.mkdir(testDir, { recursive: true });
|
|
1087
1208
|
const testFiles = [];
|
|
1088
|
-
|
|
1089
|
-
const
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1209
|
+
if (ctx?.modelService) {
|
|
1210
|
+
for (const scenario of session.bddScenarios) {
|
|
1211
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1212
|
+
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1213
|
+
const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
1214
|
+
loader.start();
|
|
1215
|
+
try {
|
|
1216
|
+
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
1217
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1218
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1219
|
+
loader.stop(chalk9.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
1220
|
+
} catch {
|
|
1221
|
+
const content = generateTestFile(scenario, session);
|
|
1222
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1223
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1224
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
} else {
|
|
1228
|
+
for (const scenario of session.bddScenarios) {
|
|
1229
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1230
|
+
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1231
|
+
const content = generateTestFile(scenario, session);
|
|
1232
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1233
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1234
|
+
}
|
|
1094
1235
|
}
|
|
1095
1236
|
return testFiles;
|
|
1096
1237
|
}
|
|
1097
|
-
function
|
|
1238
|
+
async function generateTestFileWithAI(scenario, session, ctx) {
|
|
1239
|
+
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
|
|
1240
|
+
|
|
1241
|
+
## \u529F\u80FD\u540D\u79F0
|
|
1242
|
+
${scenario.feature}
|
|
1243
|
+
|
|
1244
|
+
## BDD \u573A\u666F
|
|
1245
|
+
${scenario.scenarios.map((s) => `
|
|
1246
|
+
### ${s.name}
|
|
1247
|
+
- Given: ${s.given.join(", ")}
|
|
1248
|
+
- When: ${s.when.join(", ")}
|
|
1249
|
+
- Then: ${s.then.join(", ")}
|
|
1250
|
+
`).join("\n")}
|
|
1251
|
+
|
|
1252
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1253
|
+
- \u6280\u672F\u6808: ${session.context?.techStack?.join(", ") || "TypeScript"}
|
|
1254
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1255
|
+
|
|
1256
|
+
## \u8981\u6C42
|
|
1257
|
+
1. \u4F7F\u7528 vitest \u6D4B\u8BD5\u6846\u67B6 (describe, it, expect, beforeEach \u7B49)
|
|
1258
|
+
2. \u6BCF\u4E2A\u573A\u666F\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
1259
|
+
3. \u6D4B\u8BD5\u4EE3\u7801\u8981\u5B8C\u6574\u53EF\u8FD0\u884C\uFF0C\u5305\u542B\u5FC5\u8981\u7684 mock \u548C setup
|
|
1260
|
+
4. \u4F7F\u7528\u4E2D\u6587\u6CE8\u91CA\u8BF4\u660E\u6D4B\u8BD5\u610F\u56FE
|
|
1261
|
+
5. \u6D4B\u8BD5\u8981\u8986\u76D6\u6B63\u5E38\u6D41\u7A0B\u548C\u8FB9\u754C\u60C5\u51B5
|
|
1262
|
+
|
|
1263
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u9700\u8981\u89E3\u91CA\u3002`;
|
|
1264
|
+
const response = await ctx.modelService.sendMessage([
|
|
1265
|
+
{ role: "user", content: prompt2 }
|
|
1266
|
+
], {
|
|
1267
|
+
temperature: 0.3,
|
|
1268
|
+
maxTokens: 4e3
|
|
1269
|
+
});
|
|
1270
|
+
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
1271
|
+
if (codeMatch) {
|
|
1272
|
+
return codeMatch[1].trim();
|
|
1273
|
+
}
|
|
1274
|
+
return response.content;
|
|
1275
|
+
}
|
|
1276
|
+
function generateTestFile(scenario, session) {
|
|
1098
1277
|
const lines = [];
|
|
1099
|
-
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
1278
|
+
lines.push(`import { describe, it, expect, beforeEach } from 'vitest';`);
|
|
1100
1279
|
lines.push("");
|
|
1280
|
+
lines.push(`/**`);
|
|
1281
|
+
lines.push(` * ${scenario.feature} \u529F\u80FD\u6D4B\u8BD5`);
|
|
1282
|
+
lines.push(` * `);
|
|
1283
|
+
lines.push(` * BDD \u573A\u666F\u6570\u91CF: ${scenario.scenarios.length}`);
|
|
1284
|
+
if (session?.context?.techStack) {
|
|
1285
|
+
lines.push(` * \u6280\u672F\u6808: ${session.context.techStack.join(", ")}`);
|
|
1286
|
+
}
|
|
1287
|
+
lines.push(` */`);
|
|
1101
1288
|
lines.push(`describe('${scenario.feature}', () => {`);
|
|
1102
1289
|
for (const s of scenario.scenarios) {
|
|
1103
|
-
lines.push(` it('${s.name}', () => {`);
|
|
1104
|
-
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
1105
|
-
lines.push(` // When: ${s.when.join(", ")}`);
|
|
1106
|
-
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
1107
|
-
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
1108
|
-
lines.push(` });`);
|
|
1109
1290
|
lines.push("");
|
|
1291
|
+
lines.push(` /**`);
|
|
1292
|
+
lines.push(` * \u573A\u666F: ${s.name}`);
|
|
1293
|
+
lines.push(` * Given: ${s.given.join(", ")}`);
|
|
1294
|
+
lines.push(` * When: ${s.when.join(", ")}`);
|
|
1295
|
+
lines.push(` * Then: ${s.then.join(", ")}`);
|
|
1296
|
+
lines.push(` */`);
|
|
1297
|
+
lines.push(` it('${s.name}', async () => {`);
|
|
1298
|
+
lines.push(` // Arrange (Given)`);
|
|
1299
|
+
for (const g of s.given) {
|
|
1300
|
+
lines.push(` // ${g}`);
|
|
1301
|
+
}
|
|
1302
|
+
lines.push(` const input = {}; // TODO: \u8BBE\u7F6E\u521D\u59CB\u72B6\u6001`);
|
|
1303
|
+
lines.push("");
|
|
1304
|
+
lines.push(` // Act (When)`);
|
|
1305
|
+
for (const w of s.when) {
|
|
1306
|
+
lines.push(` // ${w}`);
|
|
1307
|
+
}
|
|
1308
|
+
lines.push(` const result = {}; // TODO: \u6267\u884C\u64CD\u4F5C`);
|
|
1309
|
+
lines.push("");
|
|
1310
|
+
lines.push(` // Assert (Then)`);
|
|
1311
|
+
for (const t of s.then) {
|
|
1312
|
+
lines.push(` // ${t}`);
|
|
1313
|
+
}
|
|
1314
|
+
lines.push(` expect(result).toBeDefined(); // TODO: \u5B8C\u5584\u65AD\u8A00`);
|
|
1315
|
+
lines.push(` });`);
|
|
1110
1316
|
}
|
|
1317
|
+
lines.push("");
|
|
1111
1318
|
lines.push(`});`);
|
|
1112
1319
|
return lines.join("\n");
|
|
1113
1320
|
}
|
|
@@ -1153,7 +1360,7 @@ function extractUrls(text) {
|
|
|
1153
1360
|
const matches = text.match(urlRegex);
|
|
1154
1361
|
return matches ? [...new Set(matches)] : [];
|
|
1155
1362
|
}
|
|
1156
|
-
async function fetchAndAnalyzeReference(url, ctx) {
|
|
1363
|
+
async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
1157
1364
|
const type = detectResourceType(url);
|
|
1158
1365
|
let content = "";
|
|
1159
1366
|
let analysis = "";
|
|
@@ -1168,7 +1375,7 @@ async function fetchAndAnalyzeReference(url, ctx) {
|
|
|
1168
1375
|
}
|
|
1169
1376
|
content = await response.text();
|
|
1170
1377
|
if (ctx.modelService.getCurrentModel()) {
|
|
1171
|
-
analysis = await analyzeReferenceContent(url, content, type, ctx);
|
|
1378
|
+
analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
|
|
1172
1379
|
} else {
|
|
1173
1380
|
analysis = extractBasicInfo(content, type);
|
|
1174
1381
|
}
|
|
@@ -1189,40 +1396,72 @@ function detectResourceType(url) {
|
|
|
1189
1396
|
}
|
|
1190
1397
|
return "webpage";
|
|
1191
1398
|
}
|
|
1192
|
-
async function analyzeReferenceContent(url, content, type, ctx) {
|
|
1193
|
-
const typePrompts = {
|
|
1194
|
-
webpage: "\u5206\u6790\u8FD9\u4E2A\u7F51\u9875\u7684\u529F\u80FD\u3001UI\u7EC4\u4EF6\u548C\u4EA4\u4E92\u65B9\u5F0F",
|
|
1195
|
-
design: "\u5206\u6790\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F\u7684\u5E03\u5C40\u3001\u7EC4\u4EF6\u548C\u6837\u5F0F",
|
|
1196
|
-
image: "\u63CF\u8FF0\u8FD9\u4E2A\u56FE\u7247\u7684\u5185\u5BB9\u548C\u8BBE\u8BA1\u5143\u7D20",
|
|
1197
|
-
api: "\u5206\u6790\u8FD9\u4E2AAPI\u7684\u7ED3\u6784\u548C\u53C2\u6570"
|
|
1198
|
-
};
|
|
1399
|
+
async function analyzeReferenceContent(url, content, type, ctx, projectContext) {
|
|
1199
1400
|
const prompt2 = `
|
|
1200
|
-
\u8BF7\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\
|
|
1401
|
+
\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
|
|
1402
|
+
|
|
1403
|
+
## \u91CD\u8981\u8BF4\u660E
|
|
1404
|
+
\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
|
|
1405
|
+
\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
|
|
1406
|
+
|
|
1407
|
+
${projectContext ? `## \u5F53\u524D\u9879\u76EE\u89C4\u8303
|
|
1408
|
+
- \u9879\u76EE\u540D\u79F0: ${projectContext.name}
|
|
1409
|
+
- \u6280\u672F\u6808: ${projectContext.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
1410
|
+
- \u6846\u67B6: ${projectContext.framework || "\u672A\u6307\u5B9A"}
|
|
1411
|
+
- \u5F00\u53D1\u89C4\u8303\u6458\u8981: ${projectContext.devStandards?.slice(0, 1500) || "\u672A\u6307\u5B9A"}
|
|
1412
|
+
` : ""}
|
|
1413
|
+
|
|
1414
|
+
## \u53C2\u8003\u8D44\u6E90\u4FE1\u606F
|
|
1415
|
+
- URL: ${url}
|
|
1416
|
+
- \u7C7B\u578B: ${type}
|
|
1417
|
+
|
|
1418
|
+
## \u7F51\u9875\u5185\u5BB9
|
|
1419
|
+
\`\`\`html
|
|
1420
|
+
${content.slice(0, 8e3)}
|
|
1421
|
+
\`\`\`
|
|
1422
|
+
|
|
1423
|
+
## \u5206\u6790\u8981\u6C42
|
|
1424
|
+
|
|
1425
|
+
\u8BF7\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u8FDB\u884C\u5206\u6790\uFF1A
|
|
1201
1426
|
|
|
1202
|
-
|
|
1203
|
-
\
|
|
1427
|
+
### 1. \u4E1A\u52A1\u529F\u80FD\u5206\u6790\uFF08\u91CD\u70B9\uFF09
|
|
1428
|
+
- \u6838\u5FC3\u4E1A\u52A1\u529F\u80FD\u662F\u4EC0\u4E48\uFF1F\uFF08\u8BE6\u7EC6\u63CF\u8FF0\u7528\u6237\u80FD\u505A\u4EC0\u4E48\uFF09
|
|
1429
|
+
- \u4E1A\u52A1\u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F\uFF08\u7528\u6237\u64CD\u4F5C\u6B65\u9AA4\uFF09
|
|
1430
|
+
- \u4E1A\u52A1\u89C4\u5219\u662F\u4EC0\u4E48\uFF1F\uFF08\u8F93\u5165\u9650\u5236\u3001\u8BA1\u7B97\u89C4\u5219\u7B49\uFF09
|
|
1204
1431
|
|
|
1205
|
-
\
|
|
1206
|
-
|
|
1432
|
+
### 2. UI/UX \u7ED3\u6784\u5206\u6790
|
|
1433
|
+
- \u9875\u9762\u5E03\u5C40\u7ED3\u6784\uFF08\u4E0D\u6D89\u53CA\u5177\u4F53\u6280\u672F\u5B9E\u73B0\uFF09
|
|
1434
|
+
- \u4E3B\u8981\u7EC4\u4EF6/\u6A21\u5757\u6709\u54EA\u4E9B\uFF1F
|
|
1435
|
+
- \u4EA4\u4E92\u65B9\u5F0F\uFF08\u70B9\u51FB\u3001\u8F93\u5165\u3001\u62D6\u62FD\u7B49\uFF09
|
|
1207
1436
|
|
|
1208
|
-
|
|
1437
|
+
### 3. \u6570\u636E\u6A21\u578B\u5206\u6790
|
|
1438
|
+
- \u9700\u8981\u54EA\u4E9B\u6570\u636E\u5B57\u6BB5\uFF1F
|
|
1439
|
+
- \u6570\u636E\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1440
|
+
- \u6570\u636E\u6765\u6E90\uFF08\u7528\u6237\u8F93\u5165/\u8BA1\u7B97/API\uFF09
|
|
1209
1441
|
|
|
1210
|
-
\
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
|
|
1442
|
+
### 4. \u529F\u80FD\u62C6\u5206\u5EFA\u8BAE
|
|
1443
|
+
\u5C06\u529F\u80FD\u62C6\u5206\u4E3A\u53EF\u72EC\u7ACB\u5F00\u53D1\u7684\u4EFB\u52A1\uFF1A
|
|
1444
|
+
- \u4EFB\u52A1\u540D\u79F0\uFF08\u7B80\u77ED\u660E\u786E\uFF09
|
|
1445
|
+
- \u4EFB\u52A1\u63CF\u8FF0\uFF08\u4E1A\u52A1\u89D2\u5EA6\uFF09
|
|
1446
|
+
- \u9A8C\u6536\u6807\u51C6
|
|
1216
1447
|
|
|
1217
|
-
\
|
|
1448
|
+
\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
|
|
1449
|
+
\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
|
|
1218
1450
|
`;
|
|
1451
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
1452
|
+
loader.start();
|
|
1219
1453
|
try {
|
|
1220
1454
|
const response = await ctx.modelService.sendMessage([
|
|
1221
1455
|
{ role: "user", content: prompt2 }
|
|
1222
|
-
], {
|
|
1456
|
+
], {
|
|
1457
|
+
temperature: 0.3,
|
|
1458
|
+
maxTokens: 4e3
|
|
1459
|
+
});
|
|
1460
|
+
loader.stop(chalk9.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
1223
1461
|
return response.content;
|
|
1224
|
-
} catch {
|
|
1225
|
-
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
loader.stop();
|
|
1464
|
+
throw error;
|
|
1226
1465
|
}
|
|
1227
1466
|
}
|
|
1228
1467
|
function extractBasicInfo(content, type) {
|
|
@@ -1263,10 +1502,43 @@ function getActiveSession() {
|
|
|
1263
1502
|
function clearActiveSession() {
|
|
1264
1503
|
activeSession = null;
|
|
1265
1504
|
}
|
|
1266
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1505
|
+
var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1267
1506
|
var init_new = __esm({
|
|
1268
1507
|
"src/commands/new.ts"() {
|
|
1269
1508
|
init_esm_shims();
|
|
1509
|
+
LoadingIndicator = class {
|
|
1510
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1511
|
+
frameIndex = 0;
|
|
1512
|
+
interval = null;
|
|
1513
|
+
message;
|
|
1514
|
+
constructor(message) {
|
|
1515
|
+
this.message = message;
|
|
1516
|
+
}
|
|
1517
|
+
start() {
|
|
1518
|
+
process.stdout.write("\x1B[?25l");
|
|
1519
|
+
this.interval = setInterval(() => {
|
|
1520
|
+
const frame = this.frames[this.frameIndex];
|
|
1521
|
+
process.stdout.write(`\r${chalk9.cyan(frame)} ${this.message}...`);
|
|
1522
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1523
|
+
}, 80);
|
|
1524
|
+
}
|
|
1525
|
+
update(message) {
|
|
1526
|
+
this.message = message;
|
|
1527
|
+
}
|
|
1528
|
+
stop(finalMessage) {
|
|
1529
|
+
if (this.interval) {
|
|
1530
|
+
clearInterval(this.interval);
|
|
1531
|
+
this.interval = null;
|
|
1532
|
+
}
|
|
1533
|
+
process.stdout.write("\x1B[?25h");
|
|
1534
|
+
if (finalMessage) {
|
|
1535
|
+
process.stdout.write(`\r${finalMessage}
|
|
1536
|
+
`);
|
|
1537
|
+
} else {
|
|
1538
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1270
1542
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
1271
1543
|
COMPLEXITY_THRESHOLD = 6;
|
|
1272
1544
|
CLARITY_THRESHOLD = 0.6;
|