@nick848/sf-cli 1.0.20 → 1.0.22

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
@@ -129,7 +129,10 @@ async function executeWorkflow(ctx) {
129
129
  if (activeSession.phase === "context") {
130
130
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
131
131
  lines.push("");
132
+ const loader = new LoadingIndicator2("\u8BFB\u53D6\u9879\u76EE\u914D\u7F6E");
133
+ loader.start();
132
134
  activeSession.context = await readProjectContext(ctx.options.workingDirectory);
135
+ loader.stop();
133
136
  lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
134
137
  lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
135
138
  lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
@@ -219,7 +222,7 @@ ${resource.analysis}`;
219
222
  lines.push("");
220
223
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
221
224
  lines.push("");
222
- const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
225
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
223
226
  loader.start();
224
227
  try {
225
228
  activeSession.bddScenarios = await generateBDDScenariosWithAI(
@@ -254,7 +257,7 @@ ${resource.analysis}`;
254
257
  lines.push("");
255
258
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
256
259
  lines.push("");
257
- const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
260
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
258
261
  loader.start();
259
262
  try {
260
263
  activeSession.specItems = await generateSpecItemsWithAI(
@@ -423,31 +426,85 @@ async function handleWorkflowInput(input, ctx) {
423
426
  activeSession.phase = "analysis";
424
427
  return executeWorkflow(ctx);
425
428
  }
426
- if (activeSession.phase === "spec") {
429
+ if (activeSession.phase === "spec" || activeSession.specFeedbackState === "waiting_confirm") {
427
430
  if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
431
+ activeSession.specFeedbackState = void 0;
428
432
  activeSession.phase = "tdd";
429
433
  return executeWorkflow(ctx);
430
434
  }
431
- if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
432
- activeSession.bddScenarios = generateBDDScenarios(
433
- activeSession.refinedRequirement,
434
- activeSession.context,
435
- activeSession.clarificationQuestions,
436
- activeSession.referenceResources
437
- );
438
- activeSession.specItems = generateSpecItems(
439
- activeSession.refinedRequirement,
440
- activeSession.context,
441
- activeSession.bddScenarios,
442
- activeSession.clarificationQuestions,
443
- activeSession.referenceResources
444
- );
435
+ if (trimmed === "n" || trimmed === "no" || trimmed === "\u4E0D\u6EE1\u610F" || trimmed === "\u91CD\u65B0") {
436
+ activeSession.specFeedbackState = "collecting_feedback";
437
+ if (!activeSession.specFeedbacks) {
438
+ activeSession.specFeedbacks = [];
439
+ }
440
+ return {
441
+ output: chalk9__default.default.yellow("\n\u{1F4DD} \u8BF7\u8F93\u5165\u60A8\u5BF9\u89C4\u683C\u4E0D\u6EE1\u610F\u7684\u5730\u65B9:") + chalk9__default.default.gray("\n - \u9057\u6F0F\u7684\u529F\u80FD\u70B9") + chalk9__default.default.gray("\n - \u4E0D\u5408\u7406\u7684\u62C6\u5206") + chalk9__default.default.gray("\n - \u9700\u8981\u8865\u5145\u7684\u7EC6\u8282") + chalk9__default.default.gray("\n - \u5176\u4ED6\u6539\u8FDB\u5EFA\u8BAE") + chalk9__default.default.cyan('\n\n\u8F93\u5165\u5B8C\u6210\u540E\uFF0C\u8F93\u5165\u7A7A\u884C\u6216 "done" \u7ED3\u675F\u8F93\u5165')
442
+ };
443
+ }
444
+ }
445
+ if (activeSession.specFeedbackState === "collecting_feedback") {
446
+ if (trimmed === "" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
447
+ if (!activeSession.specFeedbacks || activeSession.specFeedbacks.length === 0) {
448
+ return {
449
+ output: chalk9__default.default.yellow('\u26A0\uFE0F \u8BF7\u81F3\u5C11\u8F93\u5165\u4E00\u6761\u53CD\u9988\u610F\u89C1\uFF0C\u6216\u8F93\u5165 "cancel" \u53D6\u6D88')
450
+ };
451
+ }
452
+ activeSession.specFeedbackState = "feedback_received";
453
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210\u89C4\u683C");
454
+ loader.start();
455
+ try {
456
+ activeSession.specItems = await generateSpecItemsWithFeedback(
457
+ activeSession.refinedRequirement,
458
+ activeSession.context,
459
+ activeSession.bddScenarios,
460
+ activeSession.clarificationQuestions,
461
+ activeSession.referenceResources,
462
+ activeSession.specFeedbacks,
463
+ ctx
464
+ );
465
+ loader.stop(chalk9__default.default.green(" \u2713 \u89C4\u683C\u5DF2\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210"));
466
+ } catch (error) {
467
+ loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u65B9\u6CD5\u91CD\u65B0\u751F\u6210"));
468
+ activeSession.specItems = generateSpecItems(
469
+ activeSession.refinedRequirement,
470
+ activeSession.context,
471
+ activeSession.bddScenarios,
472
+ activeSession.clarificationQuestions,
473
+ activeSession.referenceResources
474
+ );
475
+ }
445
476
  const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
477
+ activeSession.specFeedbackState = "waiting_confirm";
478
+ const lines = [];
479
+ lines.push(chalk9__default.default.cyan("\n\u2501\u2501\u2501 \u66F4\u65B0\u540E\u7684\u4EFB\u52A1\u6982\u89C8 \u2501\u2501\u2501"));
480
+ for (const item of activeSession.specItems.slice(0, 8)) {
481
+ const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
482
+ lines.push(chalk9__default.default.gray(` ${icon} [${item.id}] ${item.title}`));
483
+ }
484
+ if (activeSession.specItems.length > 8) {
485
+ lines.push(chalk9__default.default.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
486
+ }
446
487
  return {
447
- output: chalk9__default.default.cyan("\u{1F504} \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210") + chalk9__default.default.gray(`
448
- \u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u518D\u6B21\u91CD\u65B0\u751F\u6210")
488
+ output: lines.join("\n") + chalk9__default.default.green(`
489
+
490
+ \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u66F4\u65B0`) + chalk9__default.default.gray(`
491
+ \u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u4ECD\u6709\u95EE\u9898\uFF0C\u7EE7\u7EED\u53CD\u9988")
449
492
  };
450
493
  }
494
+ if (trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
495
+ activeSession.specFeedbackState = "waiting_confirm";
496
+ activeSession.specFeedbacks = [];
497
+ return {
498
+ output: chalk9__default.default.gray("\u5DF2\u53D6\u6D88\u53CD\u9988\uFF0C\u8FD4\u56DE\u89C4\u683C\u786E\u8BA4") + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210")
499
+ };
500
+ }
501
+ if (!activeSession.specFeedbacks) {
502
+ activeSession.specFeedbacks = [];
503
+ }
504
+ activeSession.specFeedbacks.push(trimmed);
505
+ return {
506
+ output: chalk9__default.default.gray(`\u5DF2\u8BB0\u5F55\u53CD\u9988 #${activeSession.specFeedbacks.length}: `) + chalk9__default.default.white(trimmed.slice(0, 50)) + chalk9__default.default.gray('\n\u7EE7\u7EED\u8F93\u5165\uFF0C\u6216\u8F93\u5165 "done" \u5B8C\u6210\u53CD\u9988')
507
+ };
451
508
  }
452
509
  if (activeSession.phase === "develop") {
453
510
  if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
@@ -599,107 +656,115 @@ function getCategoryLabel(category) {
599
656
  async function executeDevelopment(ctx, session) {
600
657
  const workingDir = ctx.options.workingDirectory;
601
658
  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
659
+ console.log("");
660
+ for (let i = 0; i < session.specItems.length; i++) {
661
+ const item = session.specItems[i];
662
+ const prefix = `[${i + 1}/${session.specItems.length}]`;
663
+ if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
664
+ continue;
665
+ }
666
+ const loader = new LoadingIndicator2(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
667
+ loader.start();
668
+ try {
669
+ const taskPrompt = buildTaskPrompt(session, item, i);
670
+ const messages = [
671
+ {
672
+ role: "system",
673
+ 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
674
 
611
675
  \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
676
+ 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
677
+ 2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
678
+ 3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
679
+ 4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
622
680
 
623
681
  \u9879\u76EE\u4FE1\u606F\uFF1A
624
682
  - \u540D\u79F0: ${session.context?.name}
625
683
  - \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
626
684
  - \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
627
685
 
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
686
+ ${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
687
+ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
688
+ },
689
+ {
690
+ role: "user",
691
+ content: taskPrompt
692
+ }
693
+ ];
694
+ const response = await ctx.modelService.sendMessage(messages, {
695
+ temperature: 0.3,
696
+ maxTokens: 4e3,
697
+ // 单个任务减少 token
698
+ agent: "frontend-dev",
699
+ timeout: 3e5
700
+ // 5分钟超时,确保复杂代码有足够时间
701
+ });
702
+ const codeBlocks = parseCodeBlocks(response.content);
703
+ if (codeBlocks.length > 0) {
704
+ for (const block of codeBlocks) {
705
+ const filePath = path4__namespace.join(workingDir, block.filename);
706
+ const dir = path4__namespace.dirname(filePath);
707
+ await fs4__namespace.mkdir(dir, { recursive: true });
708
+ await fs4__namespace.writeFile(filePath, block.code, "utf-8");
709
+ files.push(block.filename);
710
+ }
711
+ loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
712
+ } else {
713
+ const implDir = path4__namespace.join(workingDir, "src");
714
+ await fs4__namespace.mkdir(implDir, { recursive: true });
715
+ const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
716
+ const filePath = path4__namespace.join(implDir, fileName);
717
+ await fs4__namespace.writeFile(filePath, `// TODO: ${item.title}
718
+ // ${item.description}
719
+ `, "utf-8");
720
+ files.push(`src/${fileName}`);
721
+ loader.stop(chalk9__default.default.yellow(`${prefix} \u26A0 \u751F\u6210\u57FA\u7840\u6A21\u677F: ${item.title.slice(0, 20)}`));
722
+ }
723
+ if (i < session.specItems.length - 1) {
724
+ await new Promise((resolve4) => setTimeout(resolve4, 500));
634
725
  }
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}`);
726
+ } catch (error) {
727
+ loader.stop(chalk9__default.default.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
672
728
  }
673
- loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
729
+ }
730
+ if (files.length > 0) {
674
731
  return { success: true, files };
675
- } catch (error) {
676
- loader.stop();
732
+ } else {
677
733
  return {
678
734
  success: false,
679
735
  files: [],
680
- error: error.message
736
+ error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
681
737
  };
682
738
  }
683
739
  }
684
- function buildDevelopmentPrompt(session) {
740
+ function buildTaskPrompt(session, item, index) {
685
741
  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}`);
742
+ lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
743
+ lines.push(`**\u6807\u9898**: ${item.title}`);
744
+ lines.push(`**\u63CF\u8FF0**: ${item.description}`);
745
+ lines.push(`**\u4F18\u5148\u7EA7**: ${item.priority}`);
746
+ if (item.tests && item.tests.length > 0) {
747
+ lines.push(`**\u9A8C\u6536\u6807\u51C6**:`);
748
+ for (const t of item.tests) {
749
+ lines.push(`- ${t}`);
694
750
  }
695
751
  }
696
752
  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}`);
753
+ lines.push(`## \u6574\u4F53\u9700\u6C42\u80CC\u666F`);
754
+ lines.push(session.requirement);
755
+ const relatedScenario = session.bddScenarios.find(
756
+ (s) => item.title.includes(s.feature) || s.feature.includes(item.title)
757
+ );
758
+ if (relatedScenario) {
759
+ lines.push("");
760
+ lines.push(`## \u76F8\u5173 BDD \u573A\u666F`);
761
+ lines.push(`Feature: ${relatedScenario.feature}`);
762
+ for (const s of relatedScenario.scenarios.slice(0, 2)) {
763
+ lines.push(`- ${s.name}`);
764
+ }
700
765
  }
701
766
  lines.push("");
702
- lines.push("\u8BF7\u6839\u636E\u4EE5\u4E0A\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002");
767
+ 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
768
  return lines.join("\n");
704
769
  }
705
770
  function parseCodeBlocks(content) {
@@ -720,6 +785,8 @@ async function executeReview(ctx, session) {
720
785
  const workingDir = ctx.options.workingDirectory;
721
786
  const issues = [];
722
787
  const suggestions = [];
788
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5BA1\u6838\u4EE3\u7801");
789
+ loader.start();
723
790
  try {
724
791
  const codeContents = [];
725
792
  for (const file of session.implFiles) {
@@ -782,7 +849,9 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
782
849
  const response = await ctx.modelService.sendMessage(messages, {
783
850
  temperature: 0.2,
784
851
  maxTokens: 2e3,
785
- agent: "code-reviewer"
852
+ agent: "code-reviewer",
853
+ timeout: 18e4
854
+ // 3分钟超时
786
855
  });
787
856
  const result = response.content;
788
857
  const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
@@ -804,8 +873,10 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
804
873
  const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
805
874
  suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
806
875
  }
876
+ loader.stop(passed ? chalk9__default.default.green(" \u2713 \u5BA1\u6838\u901A\u8FC7") : chalk9__default.default.yellow(" \u26A0 \u53D1\u73B0\u95EE\u9898"));
807
877
  return { passed, issues, suggestions };
808
878
  } catch (error) {
879
+ loader.stop(chalk9__default.default.red(" \u2717 \u5BA1\u6838\u51FA\u9519"));
809
880
  suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
810
881
  return { passed: true, issues, suggestions };
811
882
  }
@@ -1011,7 +1082,8 @@ ${r.analysis}`).join("\n\n") : "\u65E0"}
1011
1082
  ], {
1012
1083
  temperature: 0.3,
1013
1084
  maxTokens: 4e3,
1014
- timeout: 12e4
1085
+ timeout: 3e5
1086
+ // 5分钟超时
1015
1087
  });
1016
1088
  try {
1017
1089
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
@@ -1219,7 +1291,7 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1219
1291
  - \u96C6\u6210\u6D4B\u8BD5
1220
1292
 
1221
1293
  \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
1222
- const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1294
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1223
1295
  loader.start();
1224
1296
  try {
1225
1297
  const response = await ctx.modelService.sendMessage([
@@ -1227,25 +1299,131 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1227
1299
  ], {
1228
1300
  temperature: 0.3,
1229
1301
  maxTokens: 4e3,
1230
- timeout: 12e4
1302
+ timeout: 3e5
1303
+ // 5分钟超时
1231
1304
  });
1232
1305
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1233
1306
  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
- }));
1307
+ try {
1308
+ const parsed = JSON.parse(jsonMatch[1].trim());
1309
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1310
+ return parsed.map((item, index) => ({
1311
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1312
+ title: item.title,
1313
+ description: item.description,
1314
+ priority: item.priority || "medium",
1315
+ files: [],
1316
+ tests: item.acceptanceCriteria || []
1317
+ }));
1318
+ } catch (parseError) {
1319
+ loader.stop(chalk9__default.default.yellow(` \u26A0 JSON \u89E3\u6790\u5931\u8D25: ${parseError.message.slice(0, 50)}`));
1320
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1321
+ }
1244
1322
  }
1245
- loader.stop(chalk9__default.default.yellow(" \u26A0 \u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1323
+ try {
1324
+ const parsed = JSON.parse(response.content);
1325
+ if (Array.isArray(parsed)) {
1326
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
1327
+ return parsed.map((item, index) => ({
1328
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1329
+ title: item.title,
1330
+ description: item.description,
1331
+ priority: item.priority || "medium",
1332
+ files: [],
1333
+ tests: item.acceptanceCriteria || []
1334
+ }));
1335
+ }
1336
+ } catch {
1337
+ }
1338
+ loader.stop(chalk9__default.default.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1246
1339
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1247
1340
  } catch (error) {
1248
- loader.stop(chalk9__default.default.yellow(" \u26A0 AI \u62C6\u5206\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
1341
+ const errMsg = error.message.slice(0, 80);
1342
+ loader.stop(chalk9__default.default.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
1343
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1344
+ }
1345
+ }
1346
+ async function generateSpecItemsWithFeedback(requirement, context, bddScenarios, questions, references, feedbacks, ctx) {
1347
+ const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u9879\u76EE\u7ECF\u7406\u548C\u6280\u672F\u67B6\u6784\u5E08\u3002\u8BF7\u6839\u636E\u7528\u6237\u53CD\u9988\uFF0C\u91CD\u65B0\u62C6\u5206\u9700\u6C42\u4EFB\u52A1\u3002
1348
+
1349
+ ## \u9700\u6C42\u63CF\u8FF0
1350
+ ${requirement}
1351
+
1352
+ ## \u9879\u76EE\u4E0A\u4E0B\u6587
1353
+ - \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
1354
+ - \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
1355
+ ${context.devStandards ? `
1356
+ ## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
1357
+ ${context.devStandards.slice(0, 2e3)}
1358
+ ` : ""}
1359
+
1360
+ ## \u4E4B\u524D\u7684 BDD \u573A\u666F
1361
+ ${bddScenarios.map((s) => `- Feature: ${s.feature}`).join("\n")}
1362
+
1363
+ ## \u7528\u6237\u53CD\u9988\uFF08\u5FC5\u987B\u89E3\u51B3\u8FD9\u4E9B\u95EE\u9898\uFF09
1364
+ ${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
1365
+
1366
+ ## \u8981\u6C42
1367
+ 1. **\u5FC5\u987B\u89E3\u51B3\u7528\u6237\u53CD\u9988\u4E2D\u7684\u6240\u6709\u95EE\u9898**
1368
+ 2. \u5982\u679C\u7528\u6237\u6307\u51FA\u9057\u6F0F\u7684\u529F\u80FD\u70B9\uFF0C\u8BF7\u8865\u5145\u76F8\u5173\u4EFB\u52A1
1369
+ 3. \u5982\u679C\u7528\u6237\u6307\u51FA\u62C6\u5206\u4E0D\u5408\u7406\uFF0C\u8BF7\u91CD\u65B0\u8C03\u6574\u7C92\u5EA6
1370
+ 4. \u5982\u679C\u7528\u6237\u9700\u8981\u66F4\u591A\u7EC6\u8282\uFF0C\u8BF7\u62C6\u5206\u5F97\u66F4\u7EC6\u81F4
1371
+ 5. \u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\u5355\u4E00\u804C\u8D23\u30012-4\u5C0F\u65F6\u53EF\u5B8C\u6210
1372
+
1373
+ ## \u8F93\u51FA\u683C\u5F0F (JSON)
1374
+ \`\`\`json
1375
+ [
1376
+ {
1377
+ "id": "T001",
1378
+ "title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
1379
+ "description": "\u8BE6\u7EC6\u63CF\u8FF0",
1380
+ "priority": "high",
1381
+ "acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
1382
+ }
1383
+ ]
1384
+ \`\`\`
1385
+
1386
+ \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u786E\u4FDD\u89E3\u51B3\u4E86\u7528\u6237\u7684\u6240\u6709\u53CD\u9988\u3002`;
1387
+ try {
1388
+ const response = await ctx.modelService.sendMessage([
1389
+ { role: "user", content: prompt2 }
1390
+ ], {
1391
+ temperature: 0.3,
1392
+ maxTokens: 4e3,
1393
+ timeout: 3e5
1394
+ // 5分钟超时
1395
+ });
1396
+ const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1397
+ if (jsonMatch) {
1398
+ try {
1399
+ const parsed = JSON.parse(jsonMatch[1].trim());
1400
+ return parsed.map((item, index) => ({
1401
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1402
+ title: item.title,
1403
+ description: item.description,
1404
+ priority: item.priority || "medium",
1405
+ files: [],
1406
+ tests: item.acceptanceCriteria || []
1407
+ }));
1408
+ } catch {
1409
+ }
1410
+ }
1411
+ try {
1412
+ const parsed = JSON.parse(response.content);
1413
+ if (Array.isArray(parsed)) {
1414
+ return parsed.map((item, index) => ({
1415
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1416
+ title: item.title,
1417
+ description: item.description,
1418
+ priority: item.priority || "medium",
1419
+ files: [],
1420
+ tests: item.acceptanceCriteria || []
1421
+ }));
1422
+ }
1423
+ } catch {
1424
+ }
1425
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1426
+ } catch {
1249
1427
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1250
1428
  }
1251
1429
  }
@@ -1315,6 +1493,16 @@ function formatSpecFile(session) {
1315
1493
  lines.push("");
1316
1494
  }
1317
1495
  }
1496
+ if (session.specFeedbacks && session.specFeedbacks.length > 0) {
1497
+ lines.push("## \u89C4\u683C\u8FED\u4EE3\u53CD\u9988");
1498
+ lines.push("");
1499
+ for (let i = 0; i < session.specFeedbacks.length; i++) {
1500
+ lines.push(`${i + 1}. ${session.specFeedbacks[i]}`);
1501
+ }
1502
+ lines.push("");
1503
+ lines.push("---");
1504
+ lines.push("");
1505
+ }
1318
1506
  lines.push("## \u4EFB\u52A1\u5217\u8868");
1319
1507
  lines.push("");
1320
1508
  for (const item of session.specItems) {
@@ -1335,7 +1523,7 @@ async function generateTests(workingDir, session, ctx) {
1335
1523
  for (const scenario of session.bddScenarios) {
1336
1524
  const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
1337
1525
  const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
1338
- const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
1526
+ const loader = new LoadingIndicator2(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
1339
1527
  loader.start();
1340
1528
  try {
1341
1529
  const content = await generateTestFileWithAI(scenario, session, ctx);
@@ -1390,7 +1578,9 @@ ${scenario.scenarios.map((s) => `
1390
1578
  { role: "user", content: prompt2 }
1391
1579
  ], {
1392
1580
  temperature: 0.3,
1393
- maxTokens: 4e3
1581
+ maxTokens: 4e3,
1582
+ timeout: 18e4
1583
+ // 3分钟超时
1394
1584
  });
1395
1585
  const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
1396
1586
  if (codeMatch) {
@@ -1489,7 +1679,10 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
1489
1679
  const type = detectResourceType(url);
1490
1680
  let content = "";
1491
1681
  let analysis = "";
1682
+ const loader = new LoadingIndicator2(`\u83B7\u53D6 ${url.slice(0, 40)}...`);
1683
+ loader.start();
1492
1684
  try {
1685
+ loader.update("\u6B63\u5728\u83B7\u53D6\u7F51\u9875\u5185\u5BB9");
1493
1686
  const response = await fetch(url, {
1494
1687
  headers: {
1495
1688
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
@@ -1500,11 +1693,14 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
1500
1693
  }
1501
1694
  content = await response.text();
1502
1695
  if (ctx.modelService.getCurrentModel()) {
1696
+ loader.update("\u6B63\u5728\u5206\u6790\u5185\u5BB9");
1503
1697
  analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
1504
1698
  } else {
1505
1699
  analysis = extractBasicInfo(content, type);
1506
1700
  }
1701
+ loader.stop();
1507
1702
  } catch (error) {
1703
+ loader.stop();
1508
1704
  throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
1509
1705
  }
1510
1706
  return { url, type, content: content.slice(0, 1e4), analysis };
@@ -1573,14 +1769,16 @@ ${content.slice(0, 8e3)}
1573
1769
  \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
1574
1770
  \u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
1575
1771
  `;
1576
- const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
1772
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
1577
1773
  loader.start();
1578
1774
  try {
1579
1775
  const response = await ctx.modelService.sendMessage([
1580
1776
  { role: "user", content: prompt2 }
1581
1777
  ], {
1582
1778
  temperature: 0.3,
1583
- maxTokens: 4e3
1779
+ maxTokens: 4e3,
1780
+ timeout: 18e4
1781
+ // 3分钟超时
1584
1782
  });
1585
1783
  loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
1586
1784
  return response.content;
@@ -1627,11 +1825,11 @@ function getActiveSession() {
1627
1825
  function clearActiveSession() {
1628
1826
  activeSession = null;
1629
1827
  }
1630
- var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
1828
+ var LoadingIndicator2, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
1631
1829
  var init_new = __esm({
1632
1830
  "src/commands/new.ts"() {
1633
1831
  init_cjs_shims();
1634
- LoadingIndicator = class {
1832
+ LoadingIndicator2 = class {
1635
1833
  frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1636
1834
  frameIndex = 0;
1637
1835
  interval = null;
@@ -1996,9 +2194,10 @@ var BaseAdapter = class {
1996
2194
  }
1997
2195
  /**
1998
2196
  * 获取超时时间
2197
+ * 默认 5 分钟,对于复杂的代码生成任务足够
1999
2198
  */
2000
2199
  getTimeout() {
2001
- return this.config?.timeout || 6e4;
2200
+ return this.config?.timeout || 3e5;
2002
2201
  }
2003
2202
  /**
2004
2203
  * 获取重试次数
@@ -8188,6 +8387,36 @@ ${chalk9__default.default.yellow("\u793A\u4F8B:")}
8188
8387
 
8189
8388
  // src/commands/model.ts
8190
8389
  init_cjs_shims();
8390
+ var LoadingIndicator = class {
8391
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8392
+ frameIndex = 0;
8393
+ interval = null;
8394
+ message;
8395
+ constructor(message) {
8396
+ this.message = message;
8397
+ }
8398
+ start() {
8399
+ process.stdout.write("\x1B[?25l");
8400
+ this.interval = setInterval(() => {
8401
+ const frame = this.frames[this.frameIndex];
8402
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
8403
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
8404
+ }, 80);
8405
+ }
8406
+ stop(finalMessage) {
8407
+ if (this.interval) {
8408
+ clearInterval(this.interval);
8409
+ this.interval = null;
8410
+ }
8411
+ process.stdout.write("\x1B[?25h");
8412
+ if (finalMessage) {
8413
+ process.stdout.write(`\r${finalMessage}
8414
+ `);
8415
+ } else {
8416
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
8417
+ }
8418
+ }
8419
+ };
8191
8420
  async function handleModel(args, ctx) {
8192
8421
  const subCommand = args[0];
8193
8422
  const configManager = ctx.configManager;
@@ -8287,9 +8516,12 @@ async function verifyCurrentModel(configManager, modelService) {
8287
8516
  output: chalk9__default.default.red(`\u2717 \u672A\u77E5\u6A21\u578B: ${currentModel}`)
8288
8517
  };
8289
8518
  }
8519
+ const loader = new LoadingIndicator(`\u9A8C\u8BC1 ${modelInfo.name} API Key`);
8520
+ loader.start();
8290
8521
  try {
8291
8522
  const adapter = createAdapter(modelInfo.provider);
8292
8523
  const isValid = await adapter.validateApiKey(apiKey);
8524
+ loader.stop();
8293
8525
  if (isValid) {
8294
8526
  return {
8295
8527
  output: chalk9__default.default.green(`\u2713 API Key \u9A8C\u8BC1\u6210\u529F
@@ -8301,6 +8533,7 @@ async function verifyCurrentModel(configManager, modelService) {
8301
8533
  };
8302
8534
  }
8303
8535
  } catch (error) {
8536
+ loader.stop();
8304
8537
  return {
8305
8538
  output: chalk9__default.default.red(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${error.message}`)
8306
8539
  };
@@ -9512,6 +9745,36 @@ async function executeShell(command, ctx) {
9512
9745
  // src/commands/natural.ts
9513
9746
  init_cjs_shims();
9514
9747
  init_new();
9748
+ var LoadingIndicator3 = class {
9749
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
9750
+ frameIndex = 0;
9751
+ interval = null;
9752
+ message;
9753
+ constructor(message) {
9754
+ this.message = message;
9755
+ }
9756
+ start() {
9757
+ process.stdout.write("\x1B[?25l");
9758
+ this.interval = setInterval(() => {
9759
+ const frame = this.frames[this.frameIndex];
9760
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
9761
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
9762
+ }, 80);
9763
+ }
9764
+ stop(finalMessage) {
9765
+ if (this.interval) {
9766
+ clearInterval(this.interval);
9767
+ this.interval = null;
9768
+ }
9769
+ process.stdout.write("\x1B[?25h");
9770
+ if (finalMessage) {
9771
+ process.stdout.write(`\r${finalMessage}
9772
+ `);
9773
+ } else {
9774
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
9775
+ }
9776
+ }
9777
+ };
9515
9778
  async function handleNaturalLanguage(input, ctx) {
9516
9779
  const trimmedInput = input.trim();
9517
9780
  const session = getActiveSession();
@@ -9531,6 +9794,8 @@ async function handleNaturalLanguage(input, ctx) {
9531
9794
  contextUsed: 0
9532
9795
  };
9533
9796
  }
9797
+ const loader = new LoadingIndicator3("AI \u601D\u8003\u4E2D");
9798
+ loader.start();
9534
9799
  try {
9535
9800
  const response = await ctx.modelService.sendMessage(
9536
9801
  [
@@ -9548,6 +9813,7 @@ async function handleNaturalLanguage(input, ctx) {
9548
9813
  maxTokens: 2e3
9549
9814
  }
9550
9815
  );
9816
+ loader.stop();
9551
9817
  ctx.contextManager.addMessage({
9552
9818
  role: "user",
9553
9819
  content: trimmedInput
@@ -9561,6 +9827,7 @@ async function handleNaturalLanguage(input, ctx) {
9561
9827
  contextUsed: response.usage?.totalTokens || 0
9562
9828
  };
9563
9829
  } catch (error) {
9830
+ loader.stop();
9564
9831
  const errorMessage = error.message;
9565
9832
  if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
9566
9833
  return {