@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/index.js CHANGED
@@ -599,107 +599,115 @@ function getCategoryLabel(category) {
599
599
  async function executeDevelopment(ctx, session) {
600
600
  const workingDir = ctx.options.workingDirectory;
601
601
  const files = [];
602
- const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
603
- loader.start();
604
- try {
605
- const systemPrompt = buildDevelopmentPrompt(session);
606
- const messages = [
607
- {
608
- role: "system",
609
- 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
602
+ console.log("");
603
+ for (let i = 0; i < session.specItems.length; i++) {
604
+ const item = session.specItems[i];
605
+ const prefix = `[${i + 1}/${session.specItems.length}]`;
606
+ if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
607
+ continue;
608
+ }
609
+ const loader = new LoadingIndicator(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
610
+ loader.start();
611
+ try {
612
+ const taskPrompt = buildTaskPrompt(session, item, i);
613
+ const messages = [
614
+ {
615
+ role: "system",
616
+ 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
610
617
 
611
618
  \u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
612
- 1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
613
- 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
614
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
615
-
616
- \u4EE3\u7801\u8981\u6C42\uFF1A
617
- 1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
618
- 2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
619
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
620
- 4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
621
- 5. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
619
+ 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
620
+ 2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
621
+ 3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
622
+ 4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
622
623
 
623
624
  \u9879\u76EE\u4FE1\u606F\uFF1A
624
625
  - \u540D\u79F0: ${session.context?.name}
625
626
  - \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
626
627
  - \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
627
628
 
628
- ${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
629
- ${session.context.devStandards.slice(0, 2500)}` : ""}`
630
- },
631
- {
632
- role: "user",
633
- content: systemPrompt
629
+ ${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
630
+ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
631
+ },
632
+ {
633
+ role: "user",
634
+ content: taskPrompt
635
+ }
636
+ ];
637
+ const response = await ctx.modelService.sendMessage(messages, {
638
+ temperature: 0.3,
639
+ maxTokens: 4e3,
640
+ // 单个任务减少 token
641
+ agent: "frontend-dev",
642
+ timeout: 12e4
643
+ // 2分钟超时
644
+ });
645
+ const codeBlocks = parseCodeBlocks(response.content);
646
+ if (codeBlocks.length > 0) {
647
+ for (const block of codeBlocks) {
648
+ const filePath = path4__namespace.join(workingDir, block.filename);
649
+ const dir = path4__namespace.dirname(filePath);
650
+ await fs4__namespace.mkdir(dir, { recursive: true });
651
+ await fs4__namespace.writeFile(filePath, block.code, "utf-8");
652
+ files.push(block.filename);
653
+ }
654
+ loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
655
+ } else {
656
+ const implDir = path4__namespace.join(workingDir, "src");
657
+ await fs4__namespace.mkdir(implDir, { recursive: true });
658
+ const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
659
+ const filePath = path4__namespace.join(implDir, fileName);
660
+ await fs4__namespace.writeFile(filePath, `// TODO: ${item.title}
661
+ // ${item.description}
662
+ `, "utf-8");
663
+ files.push(`src/${fileName}`);
664
+ loader.stop(chalk9__default.default.yellow(`${prefix} \u26A0 \u751F\u6210\u57FA\u7840\u6A21\u677F: ${item.title.slice(0, 20)}`));
665
+ }
666
+ if (i < session.specItems.length - 1) {
667
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
634
668
  }
635
- ];
636
- loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
637
- const response = await ctx.modelService.sendMessage(messages, {
638
- temperature: 0.3,
639
- maxTokens: 8e3,
640
- agent: "frontend-dev",
641
- timeout: 18e4
642
- // 3 分钟超时
643
- });
644
- loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
645
- const codeBlocks = parseCodeBlocks(response.content);
646
- for (const block of codeBlocks) {
647
- const filePath = path4__namespace.join(workingDir, block.filename);
648
- const dir = path4__namespace.dirname(filePath);
649
- await fs4__namespace.mkdir(dir, { recursive: true });
650
- await fs4__namespace.writeFile(filePath, block.code, "utf-8");
651
- files.push(block.filename);
652
- }
653
- if (files.length === 0) {
654
- const implDir = path4__namespace.join(workingDir, "src", "features");
655
- await fs4__namespace.mkdir(implDir, { recursive: true });
656
- const featureName = session.specItems[0]?.title || "feature";
657
- const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
658
- const filePath = path4__namespace.join(implDir, fileName);
659
- const stubCode = `/**
660
- * ${session.requirement}
661
- *
662
- * TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
663
- */
664
-
665
- export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
666
- // TODO: \u5B9E\u73B0\u529F\u80FD
667
- console.log('${featureName} - \u5F85\u5B9E\u73B0');
668
- }
669
- `;
670
- await fs4__namespace.writeFile(filePath, stubCode, "utf-8");
671
- files.push(`src/features/${fileName}`);
669
+ } catch (error) {
670
+ loader.stop(chalk9__default.default.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
672
671
  }
673
- loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
672
+ }
673
+ if (files.length > 0) {
674
674
  return { success: true, files };
675
- } catch (error) {
676
- loader.stop();
675
+ } else {
677
676
  return {
678
677
  success: false,
679
678
  files: [],
680
- error: error.message
679
+ error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
681
680
  };
682
681
  }
683
682
  }
684
- function buildDevelopmentPrompt(session) {
683
+ function buildTaskPrompt(session, item, index) {
685
684
  const lines = [];
686
- lines.push("## \u9700\u6C42\u63CF\u8FF0");
687
- lines.push(session.refinedRequirement);
688
- lines.push("");
689
- lines.push("## BDD \u573A\u666F");
690
- for (const scenario of session.bddScenarios) {
691
- lines.push(`### ${scenario.feature}`);
692
- for (const s of scenario.scenarios) {
693
- lines.push(`- ${s.name}`);
685
+ lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
686
+ lines.push(`**\u6807\u9898**: ${item.title}`);
687
+ lines.push(`**\u63CF\u8FF0**: ${item.description}`);
688
+ lines.push(`**\u4F18\u5148\u7EA7**: ${item.priority}`);
689
+ if (item.tests && item.tests.length > 0) {
690
+ lines.push(`**\u9A8C\u6536\u6807\u51C6**:`);
691
+ for (const t of item.tests) {
692
+ lines.push(`- ${t}`);
694
693
  }
695
694
  }
696
695
  lines.push("");
697
- lines.push("## \u4EFB\u52A1\u5217\u8868");
698
- for (const item of session.specItems) {
699
- lines.push(`- [${item.id}] ${item.title}: ${item.description}`);
696
+ lines.push(`## \u6574\u4F53\u9700\u6C42\u80CC\u666F`);
697
+ lines.push(session.requirement);
698
+ const relatedScenario = session.bddScenarios.find(
699
+ (s) => item.title.includes(s.feature) || s.feature.includes(item.title)
700
+ );
701
+ if (relatedScenario) {
702
+ lines.push("");
703
+ lines.push(`## \u76F8\u5173 BDD \u573A\u666F`);
704
+ lines.push(`Feature: ${relatedScenario.feature}`);
705
+ for (const s of relatedScenario.scenarios.slice(0, 2)) {
706
+ lines.push(`- ${s.name}`);
707
+ }
700
708
  }
701
709
  lines.push("");
702
- lines.push("\u8BF7\u6839\u636E\u4EE5\u4E0A\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002");
710
+ 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");
703
711
  return lines.join("\n");
704
712
  }
705
713
  function parseCodeBlocks(content) {
@@ -1227,25 +1235,47 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1227
1235
  ], {
1228
1236
  temperature: 0.3,
1229
1237
  maxTokens: 4e3,
1230
- timeout: 12e4
1238
+ timeout: 18e4
1239
+ // 增加到3分钟
1231
1240
  });
1232
1241
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1233
1242
  if (jsonMatch) {
1234
- const parsed = JSON.parse(jsonMatch[1].trim());
1235
- loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1236
- return parsed.map((item) => ({
1237
- id: item.id || `T${String(parsed.indexOf(item) + 1).padStart(3, "0")}`,
1238
- title: item.title,
1239
- description: item.description,
1240
- priority: item.priority || "medium",
1241
- files: [],
1242
- tests: item.acceptanceCriteria || []
1243
- }));
1243
+ try {
1244
+ const parsed = JSON.parse(jsonMatch[1].trim());
1245
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1246
+ return parsed.map((item, index) => ({
1247
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1248
+ title: item.title,
1249
+ description: item.description,
1250
+ priority: item.priority || "medium",
1251
+ files: [],
1252
+ tests: item.acceptanceCriteria || []
1253
+ }));
1254
+ } catch (parseError) {
1255
+ loader.stop(chalk9__default.default.yellow(` \u26A0 JSON \u89E3\u6790\u5931\u8D25: ${parseError.message.slice(0, 50)}`));
1256
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1257
+ }
1258
+ }
1259
+ try {
1260
+ const parsed = JSON.parse(response.content);
1261
+ if (Array.isArray(parsed)) {
1262
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1263
+ return parsed.map((item, index) => ({
1264
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1265
+ title: item.title,
1266
+ description: item.description,
1267
+ priority: item.priority || "medium",
1268
+ files: [],
1269
+ tests: item.acceptanceCriteria || []
1270
+ }));
1271
+ }
1272
+ } catch {
1244
1273
  }
1245
- loader.stop(chalk9__default.default.yellow(" \u26A0 \u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1274
+ loader.stop(chalk9__default.default.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1246
1275
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1247
1276
  } catch (error) {
1248
- loader.stop(chalk9__default.default.yellow(" \u26A0 AI \u62C6\u5206\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1277
+ const errMsg = error.message.slice(0, 80);
1278
+ loader.stop(chalk9__default.default.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
1249
1279
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1250
1280
  }
1251
1281
  }