@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.mjs CHANGED
@@ -574,107 +574,115 @@ function getCategoryLabel(category) {
574
574
  async function executeDevelopment(ctx, session) {
575
575
  const workingDir = ctx.options.workingDirectory;
576
576
  const files = [];
577
- const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
578
- loader.start();
579
- try {
580
- const systemPrompt = buildDevelopmentPrompt(session);
581
- const messages = [
582
- {
583
- role: "system",
584
- 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
+ console.log("");
578
+ for (let i = 0; i < session.specItems.length; i++) {
579
+ const item = session.specItems[i];
580
+ const prefix = `[${i + 1}/${session.specItems.length}]`;
581
+ if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
582
+ continue;
583
+ }
584
+ const loader = new LoadingIndicator(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
585
+ loader.start();
586
+ try {
587
+ const taskPrompt = buildTaskPrompt(session, item, i);
588
+ const messages = [
589
+ {
590
+ role: "system",
591
+ content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EFB\u52A1\u63CF\u8FF0\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
585
592
 
586
593
  \u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
587
- 1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
588
- 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
589
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
590
-
591
- \u4EE3\u7801\u8981\u6C42\uFF1A
592
- 1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
593
- 2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
594
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
595
- 4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
596
- 5. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
594
+ 1. \u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
595
+ 2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
596
+ 3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
597
+ 4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
597
598
 
598
599
  \u9879\u76EE\u4FE1\u606F\uFF1A
599
600
  - \u540D\u79F0: ${session.context?.name}
600
601
  - \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
601
602
  - \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
602
603
 
603
- ${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
604
- ${session.context.devStandards.slice(0, 2500)}` : ""}`
605
- },
606
- {
607
- role: "user",
608
- content: systemPrompt
604
+ ${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
605
+ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
606
+ },
607
+ {
608
+ role: "user",
609
+ content: taskPrompt
610
+ }
611
+ ];
612
+ const response = await ctx.modelService.sendMessage(messages, {
613
+ temperature: 0.3,
614
+ maxTokens: 4e3,
615
+ // 单个任务减少 token
616
+ agent: "frontend-dev",
617
+ timeout: 12e4
618
+ // 2分钟超时
619
+ });
620
+ const codeBlocks = parseCodeBlocks(response.content);
621
+ if (codeBlocks.length > 0) {
622
+ for (const block of codeBlocks) {
623
+ const filePath = path5.join(workingDir, block.filename);
624
+ const dir = path5.dirname(filePath);
625
+ await fs4.mkdir(dir, { recursive: true });
626
+ await fs4.writeFile(filePath, block.code, "utf-8");
627
+ files.push(block.filename);
628
+ }
629
+ loader.stop(chalk9.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
630
+ } else {
631
+ const implDir = path5.join(workingDir, "src");
632
+ await fs4.mkdir(implDir, { recursive: true });
633
+ const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
634
+ const filePath = path5.join(implDir, fileName);
635
+ await fs4.writeFile(filePath, `// TODO: ${item.title}
636
+ // ${item.description}
637
+ `, "utf-8");
638
+ files.push(`src/${fileName}`);
639
+ loader.stop(chalk9.yellow(`${prefix} \u26A0 \u751F\u6210\u57FA\u7840\u6A21\u677F: ${item.title.slice(0, 20)}`));
640
+ }
641
+ if (i < session.specItems.length - 1) {
642
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
609
643
  }
610
- ];
611
- loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
612
- const response = await ctx.modelService.sendMessage(messages, {
613
- temperature: 0.3,
614
- maxTokens: 8e3,
615
- agent: "frontend-dev",
616
- timeout: 18e4
617
- // 3 分钟超时
618
- });
619
- loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
620
- const codeBlocks = parseCodeBlocks(response.content);
621
- for (const block of codeBlocks) {
622
- const filePath = path5.join(workingDir, block.filename);
623
- const dir = path5.dirname(filePath);
624
- await fs4.mkdir(dir, { recursive: true });
625
- await fs4.writeFile(filePath, block.code, "utf-8");
626
- files.push(block.filename);
627
- }
628
- if (files.length === 0) {
629
- const implDir = path5.join(workingDir, "src", "features");
630
- await fs4.mkdir(implDir, { recursive: true });
631
- const featureName = session.specItems[0]?.title || "feature";
632
- const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
633
- const filePath = path5.join(implDir, fileName);
634
- const stubCode = `/**
635
- * ${session.requirement}
636
- *
637
- * TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
638
- */
639
-
640
- export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
641
- // TODO: \u5B9E\u73B0\u529F\u80FD
642
- console.log('${featureName} - \u5F85\u5B9E\u73B0');
643
- }
644
- `;
645
- await fs4.writeFile(filePath, stubCode, "utf-8");
646
- files.push(`src/features/${fileName}`);
644
+ } catch (error) {
645
+ loader.stop(chalk9.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
647
646
  }
648
- loader.stop(chalk9.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
647
+ }
648
+ if (files.length > 0) {
649
649
  return { success: true, files };
650
- } catch (error) {
651
- loader.stop();
650
+ } else {
652
651
  return {
653
652
  success: false,
654
653
  files: [],
655
- error: error.message
654
+ error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
656
655
  };
657
656
  }
658
657
  }
659
- function buildDevelopmentPrompt(session) {
658
+ function buildTaskPrompt(session, item, index) {
660
659
  const lines = [];
661
- lines.push("## \u9700\u6C42\u63CF\u8FF0");
662
- lines.push(session.refinedRequirement);
663
- lines.push("");
664
- lines.push("## BDD \u573A\u666F");
665
- for (const scenario of session.bddScenarios) {
666
- lines.push(`### ${scenario.feature}`);
667
- for (const s of scenario.scenarios) {
668
- lines.push(`- ${s.name}`);
660
+ lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
661
+ lines.push(`**\u6807\u9898**: ${item.title}`);
662
+ lines.push(`**\u63CF\u8FF0**: ${item.description}`);
663
+ lines.push(`**\u4F18\u5148\u7EA7**: ${item.priority}`);
664
+ if (item.tests && item.tests.length > 0) {
665
+ lines.push(`**\u9A8C\u6536\u6807\u51C6**:`);
666
+ for (const t of item.tests) {
667
+ lines.push(`- ${t}`);
669
668
  }
670
669
  }
671
670
  lines.push("");
672
- lines.push("## \u4EFB\u52A1\u5217\u8868");
673
- for (const item of session.specItems) {
674
- lines.push(`- [${item.id}] ${item.title}: ${item.description}`);
671
+ lines.push(`## \u6574\u4F53\u9700\u6C42\u80CC\u666F`);
672
+ lines.push(session.requirement);
673
+ const relatedScenario = session.bddScenarios.find(
674
+ (s) => item.title.includes(s.feature) || s.feature.includes(item.title)
675
+ );
676
+ if (relatedScenario) {
677
+ lines.push("");
678
+ lines.push(`## \u76F8\u5173 BDD \u573A\u666F`);
679
+ lines.push(`Feature: ${relatedScenario.feature}`);
680
+ for (const s of relatedScenario.scenarios.slice(0, 2)) {
681
+ lines.push(`- ${s.name}`);
682
+ }
675
683
  }
676
684
  lines.push("");
677
- lines.push("\u8BF7\u6839\u636E\u4EE5\u4E0A\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002");
685
+ lines.push("\u8BF7\u751F\u6210\u5B9E\u73B0\u6B64\u4EFB\u52A1\u7684\u4EE3\u7801\u3002\u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u9700\u8981\u7684\u6587\u4EF6\u3002");
678
686
  return lines.join("\n");
679
687
  }
680
688
  function parseCodeBlocks(content) {
@@ -1202,25 +1210,47 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1202
1210
  ], {
1203
1211
  temperature: 0.3,
1204
1212
  maxTokens: 4e3,
1205
- timeout: 12e4
1213
+ timeout: 18e4
1214
+ // 增加到3分钟
1206
1215
  });
1207
1216
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1208
1217
  if (jsonMatch) {
1209
- const parsed = JSON.parse(jsonMatch[1].trim());
1210
- loader.stop(chalk9.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1211
- return parsed.map((item) => ({
1212
- id: item.id || `T${String(parsed.indexOf(item) + 1).padStart(3, "0")}`,
1213
- title: item.title,
1214
- description: item.description,
1215
- priority: item.priority || "medium",
1216
- files: [],
1217
- tests: item.acceptanceCriteria || []
1218
- }));
1218
+ try {
1219
+ const parsed = JSON.parse(jsonMatch[1].trim());
1220
+ loader.stop(chalk9.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1221
+ return parsed.map((item, index) => ({
1222
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1223
+ title: item.title,
1224
+ description: item.description,
1225
+ priority: item.priority || "medium",
1226
+ files: [],
1227
+ tests: item.acceptanceCriteria || []
1228
+ }));
1229
+ } catch (parseError) {
1230
+ loader.stop(chalk9.yellow(` \u26A0 JSON \u89E3\u6790\u5931\u8D25: ${parseError.message.slice(0, 50)}`));
1231
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1232
+ }
1233
+ }
1234
+ try {
1235
+ const parsed = JSON.parse(response.content);
1236
+ if (Array.isArray(parsed)) {
1237
+ loader.stop(chalk9.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1238
+ return parsed.map((item, index) => ({
1239
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1240
+ title: item.title,
1241
+ description: item.description,
1242
+ priority: item.priority || "medium",
1243
+ files: [],
1244
+ tests: item.acceptanceCriteria || []
1245
+ }));
1246
+ }
1247
+ } catch {
1219
1248
  }
1220
- loader.stop(chalk9.yellow(" \u26A0 \u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1249
+ loader.stop(chalk9.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1221
1250
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1222
1251
  } catch (error) {
1223
- loader.stop(chalk9.yellow(" \u26A0 AI \u62C6\u5206\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1252
+ const errMsg = error.message.slice(0, 80);
1253
+ loader.stop(chalk9.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
1224
1254
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1225
1255
  }
1226
1256
  }