@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/cli/index.js
CHANGED
|
@@ -1510,7 +1510,7 @@ async function executeWorkflow(ctx) {
|
|
|
1510
1510
|
for (const url of urls) {
|
|
1511
1511
|
lines.push(chalk9__default.default.gray(` \u{1F4CE} ${url}`));
|
|
1512
1512
|
try {
|
|
1513
|
-
const resource = await fetchAndAnalyzeReference(url, ctx);
|
|
1513
|
+
const resource = await fetchAndAnalyzeReference(url, ctx, activeSession.context || void 0);
|
|
1514
1514
|
activeSession.referenceResources.push(resource);
|
|
1515
1515
|
lines.push(chalk9__default.default.green(` \u2713 \u5DF2\u5206\u6790`));
|
|
1516
1516
|
activeSession.refinedRequirement += `
|
|
@@ -1549,12 +1549,26 @@ ${resource.analysis}`;
|
|
|
1549
1549
|
lines.push("");
|
|
1550
1550
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
1551
1551
|
lines.push("");
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
activeSession.
|
|
1556
|
-
|
|
1557
|
-
|
|
1552
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
1553
|
+
loader.start();
|
|
1554
|
+
try {
|
|
1555
|
+
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
1556
|
+
activeSession.refinedRequirement,
|
|
1557
|
+
activeSession.context,
|
|
1558
|
+
activeSession.clarificationQuestions,
|
|
1559
|
+
activeSession.referenceResources,
|
|
1560
|
+
ctx
|
|
1561
|
+
);
|
|
1562
|
+
loader.stop(chalk9__default.default.green(" \u2713 BDD \u573A\u666F\u5DF2\u751F\u6210"));
|
|
1563
|
+
} catch {
|
|
1564
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840 BDD \u751F\u6210"));
|
|
1565
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
1566
|
+
activeSession.refinedRequirement,
|
|
1567
|
+
activeSession.context,
|
|
1568
|
+
activeSession.clarificationQuestions,
|
|
1569
|
+
activeSession.referenceResources
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1558
1572
|
for (const scenario of activeSession.bddScenarios) {
|
|
1559
1573
|
lines.push(chalk9__default.default.white(` Feature: ${scenario.feature}`));
|
|
1560
1574
|
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
@@ -1601,7 +1615,7 @@ ${resource.analysis}`;
|
|
|
1601
1615
|
lines.push("");
|
|
1602
1616
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
1603
1617
|
lines.push("");
|
|
1604
|
-
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
1618
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession, ctx);
|
|
1605
1619
|
lines.push(chalk9__default.default.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
1606
1620
|
for (const file of activeSession.testFiles) {
|
|
1607
1621
|
lines.push(chalk9__default.default.gray(` - ${file}`));
|
|
@@ -1898,6 +1912,8 @@ function getCategoryLabel(category) {
|
|
|
1898
1912
|
async function executeDevelopment(ctx, session) {
|
|
1899
1913
|
const workingDir = ctx.options.workingDirectory;
|
|
1900
1914
|
const files = [];
|
|
1915
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
|
|
1916
|
+
loader.start();
|
|
1901
1917
|
try {
|
|
1902
1918
|
const systemPrompt = buildDevelopmentPrompt(session);
|
|
1903
1919
|
const messages = [
|
|
@@ -1905,7 +1921,12 @@ async function executeDevelopment(ctx, session) {
|
|
|
1905
1921
|
role: "system",
|
|
1906
1922
|
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
|
|
1907
1923
|
|
|
1908
|
-
\u8981\
|
|
1924
|
+
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
1925
|
+
1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
1926
|
+
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
|
|
1927
|
+
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
|
|
1928
|
+
|
|
1929
|
+
\u4EE3\u7801\u8981\u6C42\uFF1A
|
|
1909
1930
|
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
1910
1931
|
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
1911
1932
|
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
@@ -1917,19 +1938,23 @@ async function executeDevelopment(ctx, session) {
|
|
|
1917
1938
|
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1918
1939
|
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
1919
1940
|
|
|
1920
|
-
${session.context?.devStandards ? `\u5F00\u53D1\u89C4\u8303\
|
|
1921
|
-
${session.context.devStandards.slice(0,
|
|
1941
|
+
${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
|
|
1942
|
+
${session.context.devStandards.slice(0, 2500)}` : ""}`
|
|
1922
1943
|
},
|
|
1923
1944
|
{
|
|
1924
1945
|
role: "user",
|
|
1925
1946
|
content: systemPrompt
|
|
1926
1947
|
}
|
|
1927
1948
|
];
|
|
1949
|
+
loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
|
|
1928
1950
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
1929
1951
|
temperature: 0.3,
|
|
1930
1952
|
maxTokens: 8e3,
|
|
1931
|
-
agent: "frontend-dev"
|
|
1953
|
+
agent: "frontend-dev",
|
|
1954
|
+
timeout: 18e4
|
|
1955
|
+
// 3 分钟超时
|
|
1932
1956
|
});
|
|
1957
|
+
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
1933
1958
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
1934
1959
|
for (const block of codeBlocks) {
|
|
1935
1960
|
const filePath = path5__namespace.join(workingDir, block.filename);
|
|
@@ -1958,8 +1983,10 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
1958
1983
|
await fs5__namespace.writeFile(filePath, stubCode, "utf-8");
|
|
1959
1984
|
files.push(`src/features/${fileName}`);
|
|
1960
1985
|
}
|
|
1986
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
|
|
1961
1987
|
return { success: true, files };
|
|
1962
1988
|
} catch (error) {
|
|
1989
|
+
loader.stop();
|
|
1963
1990
|
return {
|
|
1964
1991
|
success: false,
|
|
1965
1992
|
files: [],
|
|
@@ -2243,36 +2270,130 @@ function generateBDDScenarios(requirement, context, questions, references = [])
|
|
|
2243
2270
|
}
|
|
2244
2271
|
return scenarios;
|
|
2245
2272
|
}
|
|
2273
|
+
async function generateBDDScenariosWithAI(requirement, context, questions, references, ctx) {
|
|
2274
|
+
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
|
|
2275
|
+
|
|
2276
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
2277
|
+
${requirement}
|
|
2278
|
+
|
|
2279
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
2280
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "\u672A\u6307\u5B9A"}
|
|
2281
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
2282
|
+
${context.devStandards ? `
|
|
2283
|
+
## \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
2284
|
+
${context.devStandards.slice(0, 2e3)}
|
|
2285
|
+
` : ""}
|
|
2286
|
+
|
|
2287
|
+
## \u6F84\u6E05\u95EE\u7B54
|
|
2288
|
+
${questions.filter((q) => q.answered).map((q) => `- Q: ${q.question}
|
|
2289
|
+
A: ${q.answer}`).join("\n")}
|
|
2290
|
+
|
|
2291
|
+
## \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
|
|
2292
|
+
${references.length > 0 ? references.map((r) => `### ${r.url}
|
|
2293
|
+
${r.analysis}`).join("\n\n") : "\u65E0"}
|
|
2294
|
+
|
|
2295
|
+
## \u8981\u6C42
|
|
2296
|
+
1. \u6BCF\u4E2A\u529F\u80FD\u6A21\u5757\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684 Feature
|
|
2297
|
+
2. \u6BCF\u4E2A Feature \u5305\u542B\u591A\u4E2A\u5177\u4F53\u7684 Scenario
|
|
2298
|
+
3. \u4F7F\u7528 Given-When-Then \u683C\u5F0F
|
|
2299
|
+
4. \u573A\u666F\u8981\u8986\u76D6: \u6B63\u5E38\u6D41\u7A0B\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u5F02\u5E38\u5904\u7406
|
|
2300
|
+
5. \u573A\u666F\u8981\u5177\u4F53\u53EF\u6D4B\u8BD5\uFF0C\u805A\u7126\u4E1A\u52A1\u903B\u8F91
|
|
2301
|
+
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
|
|
2302
|
+
|
|
2303
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
2304
|
+
\`\`\`json
|
|
2305
|
+
[
|
|
2306
|
+
{
|
|
2307
|
+
"feature": "\u529F\u80FD\u540D\u79F0",
|
|
2308
|
+
"description": "\u529F\u80FD\u63CF\u8FF0",
|
|
2309
|
+
"scenarios": [
|
|
2310
|
+
{
|
|
2311
|
+
"name": "\u573A\u666F\u540D\u79F0",
|
|
2312
|
+
"given": ["\u524D\u7F6E\u6761\u4EF61", "\u524D\u7F6E\u6761\u4EF62"],
|
|
2313
|
+
"when": ["\u64CD\u4F5C1", "\u64CD\u4F5C2"],
|
|
2314
|
+
"then": ["\u9884\u671F\u7ED3\u679C1", "\u9884\u671F\u7ED3\u679C2"]
|
|
2315
|
+
}
|
|
2316
|
+
]
|
|
2317
|
+
}
|
|
2318
|
+
]
|
|
2319
|
+
\`\`\`
|
|
2320
|
+
|
|
2321
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9\u3002`;
|
|
2322
|
+
const response = await ctx.modelService.sendMessage([
|
|
2323
|
+
{ role: "user", content: prompt2 }
|
|
2324
|
+
], {
|
|
2325
|
+
temperature: 0.3,
|
|
2326
|
+
maxTokens: 4e3,
|
|
2327
|
+
timeout: 12e4
|
|
2328
|
+
});
|
|
2329
|
+
try {
|
|
2330
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
2331
|
+
if (jsonMatch) {
|
|
2332
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
2333
|
+
}
|
|
2334
|
+
return JSON.parse(response.content);
|
|
2335
|
+
} catch {
|
|
2336
|
+
return generateBDDScenarios(requirement, context, questions, references);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2246
2339
|
function extractFeaturesFromReference(ref) {
|
|
2247
2340
|
const features = [];
|
|
2248
|
-
const analysis = ref.analysis
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
2260
|
-
hasInput: false
|
|
2261
|
-
});
|
|
2341
|
+
const analysis = ref.analysis;
|
|
2342
|
+
const featureSection = analysis.match(/###?\s*4\.\s*功能拆分建议[\s\S]*?(?=###?\s*\d|$)/i);
|
|
2343
|
+
if (featureSection) {
|
|
2344
|
+
const taskMatches = featureSection[0].matchAll(/[-*]\s*\*\*([^*]+)\*\*[::]?\s*([^\n]+)/g);
|
|
2345
|
+
for (const match of taskMatches) {
|
|
2346
|
+
features.push({
|
|
2347
|
+
title: match[1].trim(),
|
|
2348
|
+
description: match[2].trim(),
|
|
2349
|
+
hasInput: match[2].includes("\u8F93\u5165") || match[2].includes("\u8868\u5355") || match[2].includes("\u7528\u6237")
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2262
2352
|
}
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2353
|
+
const bizSection = analysis.match(/###?\s*1\.\s*业务功能分析[\s\S]*?(?=###?\s*\d|$)/i);
|
|
2354
|
+
if (bizSection && features.length === 0) {
|
|
2355
|
+
const lines = bizSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
2356
|
+
for (const line of lines.slice(0, 5)) {
|
|
2357
|
+
const content = line.replace(/^[-*]\s*/, "").trim();
|
|
2358
|
+
if (content.length > 5) {
|
|
2359
|
+
features.push({
|
|
2360
|
+
title: content.slice(0, 20),
|
|
2361
|
+
description: content,
|
|
2362
|
+
hasInput: content.includes("\u8F93\u5165") || content.includes("\u586B\u5199")
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2269
2366
|
}
|
|
2270
|
-
if (
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2367
|
+
if (features.length === 0) {
|
|
2368
|
+
const lowerAnalysis = analysis.toLowerCase();
|
|
2369
|
+
if (lowerAnalysis.includes("\u8F93\u5165") || lowerAnalysis.includes("\u8868\u5355")) {
|
|
2370
|
+
features.push({
|
|
2371
|
+
title: "\u8F93\u5165\u8868\u5355",
|
|
2372
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
|
|
2373
|
+
hasInput: true
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
if (lowerAnalysis.includes("\u6309\u94AE") || lowerAnalysis.includes("\u64CD\u4F5C")) {
|
|
2377
|
+
features.push({
|
|
2378
|
+
title: "\u4EA4\u4E92\u6309\u94AE",
|
|
2379
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
2380
|
+
hasInput: false
|
|
2381
|
+
});
|
|
2382
|
+
}
|
|
2383
|
+
if (lowerAnalysis.includes("\u8868\u683C") || lowerAnalysis.includes("\u5217\u8868")) {
|
|
2384
|
+
features.push({
|
|
2385
|
+
title: "\u6570\u636E\u5217\u8868",
|
|
2386
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
|
|
2387
|
+
hasInput: false
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
if (lowerAnalysis.includes("\u56FE\u8868") || lowerAnalysis.includes("\u53EF\u89C6\u5316")) {
|
|
2391
|
+
features.push({
|
|
2392
|
+
title: "\u56FE\u8868\u5C55\u793A",
|
|
2393
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
|
|
2394
|
+
hasInput: false
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2276
2397
|
}
|
|
2277
2398
|
if (features.length === 0) {
|
|
2278
2399
|
features.push({
|
|
@@ -2436,33 +2557,119 @@ function formatSpecFile(session) {
|
|
|
2436
2557
|
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
2437
2558
|
return lines.join("\n");
|
|
2438
2559
|
}
|
|
2439
|
-
async function generateTests(workingDir, session) {
|
|
2560
|
+
async function generateTests(workingDir, session, ctx) {
|
|
2440
2561
|
const testDir = path5__namespace.join(workingDir, "tests");
|
|
2441
2562
|
await fs5__namespace.mkdir(testDir, { recursive: true });
|
|
2442
2563
|
const testFiles = [];
|
|
2443
|
-
|
|
2444
|
-
const
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2564
|
+
if (ctx?.modelService) {
|
|
2565
|
+
for (const scenario of session.bddScenarios) {
|
|
2566
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2567
|
+
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2568
|
+
const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
2569
|
+
loader.start();
|
|
2570
|
+
try {
|
|
2571
|
+
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
2572
|
+
await fs5__namespace.writeFile(testPath, content, "utf-8");
|
|
2573
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
2574
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
2575
|
+
} catch {
|
|
2576
|
+
const content = generateTestFile(scenario, session);
|
|
2577
|
+
await fs5__namespace.writeFile(testPath, content, "utf-8");
|
|
2578
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
2579
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
} else {
|
|
2583
|
+
for (const scenario of session.bddScenarios) {
|
|
2584
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2585
|
+
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2586
|
+
const content = generateTestFile(scenario, session);
|
|
2587
|
+
await fs5__namespace.writeFile(testPath, content, "utf-8");
|
|
2588
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
2589
|
+
}
|
|
2449
2590
|
}
|
|
2450
2591
|
return testFiles;
|
|
2451
2592
|
}
|
|
2452
|
-
function
|
|
2593
|
+
async function generateTestFileWithAI(scenario, session, ctx) {
|
|
2594
|
+
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
|
|
2595
|
+
|
|
2596
|
+
## \u529F\u80FD\u540D\u79F0
|
|
2597
|
+
${scenario.feature}
|
|
2598
|
+
|
|
2599
|
+
## BDD \u573A\u666F
|
|
2600
|
+
${scenario.scenarios.map((s) => `
|
|
2601
|
+
### ${s.name}
|
|
2602
|
+
- Given: ${s.given.join(", ")}
|
|
2603
|
+
- When: ${s.when.join(", ")}
|
|
2604
|
+
- Then: ${s.then.join(", ")}
|
|
2605
|
+
`).join("\n")}
|
|
2606
|
+
|
|
2607
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
2608
|
+
- \u6280\u672F\u6808: ${session.context?.techStack?.join(", ") || "TypeScript"}
|
|
2609
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
2610
|
+
|
|
2611
|
+
## \u8981\u6C42
|
|
2612
|
+
1. \u4F7F\u7528 vitest \u6D4B\u8BD5\u6846\u67B6 (describe, it, expect, beforeEach \u7B49)
|
|
2613
|
+
2. \u6BCF\u4E2A\u573A\u666F\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
2614
|
+
3. \u6D4B\u8BD5\u4EE3\u7801\u8981\u5B8C\u6574\u53EF\u8FD0\u884C\uFF0C\u5305\u542B\u5FC5\u8981\u7684 mock \u548C setup
|
|
2615
|
+
4. \u4F7F\u7528\u4E2D\u6587\u6CE8\u91CA\u8BF4\u660E\u6D4B\u8BD5\u610F\u56FE
|
|
2616
|
+
5. \u6D4B\u8BD5\u8981\u8986\u76D6\u6B63\u5E38\u6D41\u7A0B\u548C\u8FB9\u754C\u60C5\u51B5
|
|
2617
|
+
|
|
2618
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u9700\u8981\u89E3\u91CA\u3002`;
|
|
2619
|
+
const response = await ctx.modelService.sendMessage([
|
|
2620
|
+
{ role: "user", content: prompt2 }
|
|
2621
|
+
], {
|
|
2622
|
+
temperature: 0.3,
|
|
2623
|
+
maxTokens: 4e3
|
|
2624
|
+
});
|
|
2625
|
+
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
2626
|
+
if (codeMatch) {
|
|
2627
|
+
return codeMatch[1].trim();
|
|
2628
|
+
}
|
|
2629
|
+
return response.content;
|
|
2630
|
+
}
|
|
2631
|
+
function generateTestFile(scenario, session) {
|
|
2453
2632
|
const lines = [];
|
|
2454
|
-
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
2633
|
+
lines.push(`import { describe, it, expect, beforeEach } from 'vitest';`);
|
|
2455
2634
|
lines.push("");
|
|
2635
|
+
lines.push(`/**`);
|
|
2636
|
+
lines.push(` * ${scenario.feature} \u529F\u80FD\u6D4B\u8BD5`);
|
|
2637
|
+
lines.push(` * `);
|
|
2638
|
+
lines.push(` * BDD \u573A\u666F\u6570\u91CF: ${scenario.scenarios.length}`);
|
|
2639
|
+
if (session?.context?.techStack) {
|
|
2640
|
+
lines.push(` * \u6280\u672F\u6808: ${session.context.techStack.join(", ")}`);
|
|
2641
|
+
}
|
|
2642
|
+
lines.push(` */`);
|
|
2456
2643
|
lines.push(`describe('${scenario.feature}', () => {`);
|
|
2457
2644
|
for (const s of scenario.scenarios) {
|
|
2458
|
-
lines.push(` it('${s.name}', () => {`);
|
|
2459
|
-
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
2460
|
-
lines.push(` // When: ${s.when.join(", ")}`);
|
|
2461
|
-
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
2462
|
-
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
2463
|
-
lines.push(` });`);
|
|
2464
2645
|
lines.push("");
|
|
2646
|
+
lines.push(` /**`);
|
|
2647
|
+
lines.push(` * \u573A\u666F: ${s.name}`);
|
|
2648
|
+
lines.push(` * Given: ${s.given.join(", ")}`);
|
|
2649
|
+
lines.push(` * When: ${s.when.join(", ")}`);
|
|
2650
|
+
lines.push(` * Then: ${s.then.join(", ")}`);
|
|
2651
|
+
lines.push(` */`);
|
|
2652
|
+
lines.push(` it('${s.name}', async () => {`);
|
|
2653
|
+
lines.push(` // Arrange (Given)`);
|
|
2654
|
+
for (const g of s.given) {
|
|
2655
|
+
lines.push(` // ${g}`);
|
|
2656
|
+
}
|
|
2657
|
+
lines.push(` const input = {}; // TODO: \u8BBE\u7F6E\u521D\u59CB\u72B6\u6001`);
|
|
2658
|
+
lines.push("");
|
|
2659
|
+
lines.push(` // Act (When)`);
|
|
2660
|
+
for (const w of s.when) {
|
|
2661
|
+
lines.push(` // ${w}`);
|
|
2662
|
+
}
|
|
2663
|
+
lines.push(` const result = {}; // TODO: \u6267\u884C\u64CD\u4F5C`);
|
|
2664
|
+
lines.push("");
|
|
2665
|
+
lines.push(` // Assert (Then)`);
|
|
2666
|
+
for (const t of s.then) {
|
|
2667
|
+
lines.push(` // ${t}`);
|
|
2668
|
+
}
|
|
2669
|
+
lines.push(` expect(result).toBeDefined(); // TODO: \u5B8C\u5584\u65AD\u8A00`);
|
|
2670
|
+
lines.push(` });`);
|
|
2465
2671
|
}
|
|
2672
|
+
lines.push("");
|
|
2466
2673
|
lines.push(`});`);
|
|
2467
2674
|
return lines.join("\n");
|
|
2468
2675
|
}
|
|
@@ -2508,7 +2715,7 @@ function extractUrls(text) {
|
|
|
2508
2715
|
const matches = text.match(urlRegex);
|
|
2509
2716
|
return matches ? [...new Set(matches)] : [];
|
|
2510
2717
|
}
|
|
2511
|
-
async function fetchAndAnalyzeReference(url, ctx) {
|
|
2718
|
+
async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
2512
2719
|
const type = detectResourceType(url);
|
|
2513
2720
|
let content = "";
|
|
2514
2721
|
let analysis = "";
|
|
@@ -2523,7 +2730,7 @@ async function fetchAndAnalyzeReference(url, ctx) {
|
|
|
2523
2730
|
}
|
|
2524
2731
|
content = await response.text();
|
|
2525
2732
|
if (ctx.modelService.getCurrentModel()) {
|
|
2526
|
-
analysis = await analyzeReferenceContent(url, content, type, ctx);
|
|
2733
|
+
analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
|
|
2527
2734
|
} else {
|
|
2528
2735
|
analysis = extractBasicInfo(content, type);
|
|
2529
2736
|
}
|
|
@@ -2544,40 +2751,72 @@ function detectResourceType(url) {
|
|
|
2544
2751
|
}
|
|
2545
2752
|
return "webpage";
|
|
2546
2753
|
}
|
|
2547
|
-
async function analyzeReferenceContent(url, content, type, ctx) {
|
|
2548
|
-
const typePrompts = {
|
|
2549
|
-
webpage: "\u5206\u6790\u8FD9\u4E2A\u7F51\u9875\u7684\u529F\u80FD\u3001UI\u7EC4\u4EF6\u548C\u4EA4\u4E92\u65B9\u5F0F",
|
|
2550
|
-
design: "\u5206\u6790\u8FD9\u4E2A\u8BBE\u8BA1\u7A3F\u7684\u5E03\u5C40\u3001\u7EC4\u4EF6\u548C\u6837\u5F0F",
|
|
2551
|
-
image: "\u63CF\u8FF0\u8FD9\u4E2A\u56FE\u7247\u7684\u5185\u5BB9\u548C\u8BBE\u8BA1\u5143\u7D20",
|
|
2552
|
-
api: "\u5206\u6790\u8FD9\u4E2AAPI\u7684\u7ED3\u6784\u548C\u53C2\u6570"
|
|
2553
|
-
};
|
|
2754
|
+
async function analyzeReferenceContent(url, content, type, ctx, projectContext) {
|
|
2554
2755
|
const prompt2 = `
|
|
2555
|
-
\u8BF7\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\
|
|
2756
|
+
\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
|
|
2757
|
+
|
|
2758
|
+
## \u91CD\u8981\u8BF4\u660E
|
|
2759
|
+
\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
|
|
2760
|
+
\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
|
|
2761
|
+
|
|
2762
|
+
${projectContext ? `## \u5F53\u524D\u9879\u76EE\u89C4\u8303
|
|
2763
|
+
- \u9879\u76EE\u540D\u79F0: ${projectContext.name}
|
|
2764
|
+
- \u6280\u672F\u6808: ${projectContext.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
2765
|
+
- \u6846\u67B6: ${projectContext.framework || "\u672A\u6307\u5B9A"}
|
|
2766
|
+
- \u5F00\u53D1\u89C4\u8303\u6458\u8981: ${projectContext.devStandards?.slice(0, 1500) || "\u672A\u6307\u5B9A"}
|
|
2767
|
+
` : ""}
|
|
2768
|
+
|
|
2769
|
+
## \u53C2\u8003\u8D44\u6E90\u4FE1\u606F
|
|
2770
|
+
- URL: ${url}
|
|
2771
|
+
- \u7C7B\u578B: ${type}
|
|
2772
|
+
|
|
2773
|
+
## \u7F51\u9875\u5185\u5BB9
|
|
2774
|
+
\`\`\`html
|
|
2775
|
+
${content.slice(0, 8e3)}
|
|
2776
|
+
\`\`\`
|
|
2556
2777
|
|
|
2557
|
-
|
|
2558
|
-
\u7C7B\u578B: ${type}
|
|
2778
|
+
## \u5206\u6790\u8981\u6C42
|
|
2559
2779
|
|
|
2560
|
-
\
|
|
2561
|
-
${content.slice(0, 5e3)}
|
|
2780
|
+
\u8BF7\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u8FDB\u884C\u5206\u6790\uFF1A
|
|
2562
2781
|
|
|
2563
|
-
|
|
2782
|
+
### 1. \u4E1A\u52A1\u529F\u80FD\u5206\u6790\uFF08\u91CD\u70B9\uFF09
|
|
2783
|
+
- \u6838\u5FC3\u4E1A\u52A1\u529F\u80FD\u662F\u4EC0\u4E48\uFF1F\uFF08\u8BE6\u7EC6\u63CF\u8FF0\u7528\u6237\u80FD\u505A\u4EC0\u4E48\uFF09
|
|
2784
|
+
- \u4E1A\u52A1\u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F\uFF08\u7528\u6237\u64CD\u4F5C\u6B65\u9AA4\uFF09
|
|
2785
|
+
- \u4E1A\u52A1\u89C4\u5219\u662F\u4EC0\u4E48\uFF1F\uFF08\u8F93\u5165\u9650\u5236\u3001\u8BA1\u7B97\u89C4\u5219\u7B49\uFF09
|
|
2564
2786
|
|
|
2565
|
-
\
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
4. \u6570\u636E\u7ED3\u6784\uFF08\u5982\u679C\u6709\uFF09
|
|
2570
|
-
5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
|
|
2787
|
+
### 2. UI/UX \u7ED3\u6784\u5206\u6790
|
|
2788
|
+
- \u9875\u9762\u5E03\u5C40\u7ED3\u6784\uFF08\u4E0D\u6D89\u53CA\u5177\u4F53\u6280\u672F\u5B9E\u73B0\uFF09
|
|
2789
|
+
- \u4E3B\u8981\u7EC4\u4EF6/\u6A21\u5757\u6709\u54EA\u4E9B\uFF1F
|
|
2790
|
+
- \u4EA4\u4E92\u65B9\u5F0F\uFF08\u70B9\u51FB\u3001\u8F93\u5165\u3001\u62D6\u62FD\u7B49\uFF09
|
|
2571
2791
|
|
|
2572
|
-
\
|
|
2792
|
+
### 3. \u6570\u636E\u6A21\u578B\u5206\u6790
|
|
2793
|
+
- \u9700\u8981\u54EA\u4E9B\u6570\u636E\u5B57\u6BB5\uFF1F
|
|
2794
|
+
- \u6570\u636E\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
2795
|
+
- \u6570\u636E\u6765\u6E90\uFF08\u7528\u6237\u8F93\u5165/\u8BA1\u7B97/API\uFF09
|
|
2796
|
+
|
|
2797
|
+
### 4. \u529F\u80FD\u62C6\u5206\u5EFA\u8BAE
|
|
2798
|
+
\u5C06\u529F\u80FD\u62C6\u5206\u4E3A\u53EF\u72EC\u7ACB\u5F00\u53D1\u7684\u4EFB\u52A1\uFF1A
|
|
2799
|
+
- \u4EFB\u52A1\u540D\u79F0\uFF08\u7B80\u77ED\u660E\u786E\uFF09
|
|
2800
|
+
- \u4EFB\u52A1\u63CF\u8FF0\uFF08\u4E1A\u52A1\u89D2\u5EA6\uFF09
|
|
2801
|
+
- \u9A8C\u6536\u6807\u51C6
|
|
2802
|
+
|
|
2803
|
+
\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
|
|
2804
|
+
\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
|
|
2573
2805
|
`;
|
|
2806
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
2807
|
+
loader.start();
|
|
2574
2808
|
try {
|
|
2575
2809
|
const response = await ctx.modelService.sendMessage([
|
|
2576
2810
|
{ role: "user", content: prompt2 }
|
|
2577
|
-
], {
|
|
2811
|
+
], {
|
|
2812
|
+
temperature: 0.3,
|
|
2813
|
+
maxTokens: 4e3
|
|
2814
|
+
});
|
|
2815
|
+
loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
2578
2816
|
return response.content;
|
|
2579
|
-
} catch {
|
|
2580
|
-
|
|
2817
|
+
} catch (error) {
|
|
2818
|
+
loader.stop();
|
|
2819
|
+
throw error;
|
|
2581
2820
|
}
|
|
2582
2821
|
}
|
|
2583
2822
|
function extractBasicInfo(content, type) {
|
|
@@ -2618,10 +2857,43 @@ function getActiveSession() {
|
|
|
2618
2857
|
function clearActiveSession() {
|
|
2619
2858
|
activeSession = null;
|
|
2620
2859
|
}
|
|
2621
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
2860
|
+
var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
2622
2861
|
var init_new = __esm({
|
|
2623
2862
|
"src/commands/new.ts"() {
|
|
2624
2863
|
init_cjs_shims();
|
|
2864
|
+
LoadingIndicator = class {
|
|
2865
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2866
|
+
frameIndex = 0;
|
|
2867
|
+
interval = null;
|
|
2868
|
+
message;
|
|
2869
|
+
constructor(message) {
|
|
2870
|
+
this.message = message;
|
|
2871
|
+
}
|
|
2872
|
+
start() {
|
|
2873
|
+
process.stdout.write("\x1B[?25l");
|
|
2874
|
+
this.interval = setInterval(() => {
|
|
2875
|
+
const frame = this.frames[this.frameIndex];
|
|
2876
|
+
process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
|
|
2877
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
2878
|
+
}, 80);
|
|
2879
|
+
}
|
|
2880
|
+
update(message) {
|
|
2881
|
+
this.message = message;
|
|
2882
|
+
}
|
|
2883
|
+
stop(finalMessage) {
|
|
2884
|
+
if (this.interval) {
|
|
2885
|
+
clearInterval(this.interval);
|
|
2886
|
+
this.interval = null;
|
|
2887
|
+
}
|
|
2888
|
+
process.stdout.write("\x1B[?25h");
|
|
2889
|
+
if (finalMessage) {
|
|
2890
|
+
process.stdout.write(`\r${finalMessage}
|
|
2891
|
+
`);
|
|
2892
|
+
} else {
|
|
2893
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
};
|
|
2625
2897
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
2626
2898
|
COMPLEXITY_THRESHOLD = 6;
|
|
2627
2899
|
CLARITY_THRESHOLD = 0.6;
|