@nick848/sf-cli 1.0.21 → 1.0.23

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
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk9 = require('chalk');
4
- var fs4 = require('fs/promises');
4
+ var fs9 = require('fs/promises');
5
5
  var path4 = require('path');
6
6
  var fs10 = require('fs');
7
7
  var crypto = require('crypto');
@@ -34,7 +34,7 @@ function _interopNamespace(e) {
34
34
  }
35
35
 
36
36
  var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
37
- var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
37
+ var fs9__namespace = /*#__PURE__*/_interopNamespace(fs9);
38
38
  var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
39
39
  var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
40
40
  var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
@@ -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") {
@@ -606,28 +663,17 @@ async function executeDevelopment(ctx, session) {
606
663
  if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
607
664
  continue;
608
665
  }
609
- const loader = new LoadingIndicator(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
666
+ const loader = new LoadingIndicator2(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
610
667
  loader.start();
611
668
  try {
612
669
  const taskPrompt = buildTaskPrompt(session, item, i);
670
+ const structureInfo = session.context?.projectStructure;
671
+ const structureGuide = buildStructureGuide(structureInfo);
672
+ const systemPrompt = buildCodeGenerationSystemPrompt(session, structureGuide);
613
673
  const messages = [
614
674
  {
615
675
  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
617
-
618
- \u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
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
623
-
624
- \u9879\u76EE\u4FE1\u606F\uFF1A
625
- - \u540D\u79F0: ${session.context?.name}
626
- - \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
627
- - \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
628
-
629
- ${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
630
- ${session.context.devStandards.slice(0, 2e3)}` : ""}`
676
+ content: systemPrompt
631
677
  },
632
678
  {
633
679
  role: "user",
@@ -639,25 +685,25 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
639
685
  maxTokens: 4e3,
640
686
  // 单个任务减少 token
641
687
  agent: "frontend-dev",
642
- timeout: 12e4
643
- // 2分钟超时
688
+ timeout: 3e5
689
+ // 5分钟超时,确保复杂代码有足够时间
644
690
  });
645
691
  const codeBlocks = parseCodeBlocks(response.content);
646
692
  if (codeBlocks.length > 0) {
647
693
  for (const block of codeBlocks) {
648
694
  const filePath = path4__namespace.join(workingDir, block.filename);
649
695
  const dir = path4__namespace.dirname(filePath);
650
- await fs4__namespace.mkdir(dir, { recursive: true });
651
- await fs4__namespace.writeFile(filePath, block.code, "utf-8");
696
+ await fs9__namespace.mkdir(dir, { recursive: true });
697
+ await fs9__namespace.writeFile(filePath, block.code, "utf-8");
652
698
  files.push(block.filename);
653
699
  }
654
700
  loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
655
701
  } else {
656
702
  const implDir = path4__namespace.join(workingDir, "src");
657
- await fs4__namespace.mkdir(implDir, { recursive: true });
703
+ await fs9__namespace.mkdir(implDir, { recursive: true });
658
704
  const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
659
705
  const filePath = path4__namespace.join(implDir, fileName);
660
- await fs4__namespace.writeFile(filePath, `// TODO: ${item.title}
706
+ await fs9__namespace.writeFile(filePath, `// TODO: ${item.title}
661
707
  // ${item.description}
662
708
  `, "utf-8");
663
709
  files.push(`src/${fileName}`);
@@ -680,6 +726,91 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
680
726
  };
681
727
  }
682
728
  }
729
+ function buildStructureGuide(structure) {
730
+ if (!structure) {
731
+ return `## \u9879\u76EE\u7ED3\u6784
732
+ - \u7EC4\u4EF6\u653E\u5728 src/components/
733
+ - \u9875\u9762\u653E\u5728 src/pages/
734
+ - \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/`;
735
+ }
736
+ const lines = ["## \u9879\u76EE\u7ED3\u6784\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09"];
737
+ if (structure.hasSrc) {
738
+ lines.push(`- \u6E90\u7801\u76EE\u5F55: ${structure.srcDir}/`);
739
+ }
740
+ if (structure.hasPages) {
741
+ lines.push(`- \u9875\u9762\u76EE\u5F55: ${structure.pagesDir}/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
742
+ } else {
743
+ lines.push(`- \u9875\u9762\u76EE\u5F55: src/pages/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
744
+ }
745
+ if (structure.hasComponents) {
746
+ lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: ${structure.componentsDir}/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
747
+ } else {
748
+ lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: src/components/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
749
+ }
750
+ if (structure.hasApi) {
751
+ lines.push(`- API \u76EE\u5F55: src/api/ \u6216 src/services/`);
752
+ }
753
+ if (structure.hasHooks) {
754
+ lines.push(`- Hooks \u76EE\u5F55: src/hooks/`);
755
+ }
756
+ if (structure.hasStore) {
757
+ lines.push(`- \u72B6\u6001\u7BA1\u7406\u76EE\u5F55: src/store/`);
758
+ }
759
+ if (structure.hasUtils) {
760
+ lines.push(`- \u5DE5\u5177\u51FD\u6570\u76EE\u5F55: src/utils/`);
761
+ }
762
+ lines.push("");
763
+ lines.push("\u26A0\uFE0F \u6587\u4EF6\u5FC5\u987B\u653E\u5728\u4E0A\u8FF0\u5BF9\u5E94\u76EE\u5F55\u4E2D\uFF0C\u4E0D\u8981\u968F\u610F\u521B\u5EFA\u65B0\u76EE\u5F55");
764
+ return lines.join("\n");
765
+ }
766
+ function buildCodeGenerationSystemPrompt(session, structureGuide) {
767
+ const framework = session.context?.framework || "React";
768
+ const techStack = session.context?.techStack?.join(", ") || "TypeScript";
769
+ const devStandards = session.context?.devStandards || "";
770
+ const standardsText = devStandards.slice(0, 6e3);
771
+ return `\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
772
+
773
+ ## \u26A0\uFE0F \u6838\u5FC3\u89C4\u5219\uFF08\u5FC5\u987B\u9075\u5B88\uFF09
774
+
775
+ 1. **\u8BED\u8A00**: \u5FC5\u987B\u4F7F\u7528 TypeScript (.tsx/.ts \u6587\u4EF6)
776
+ 2. **\u6846\u67B6**: ${framework}
777
+ 3. **\u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
778
+ 4. **\u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u6B63\u786E**\uFF1A\u4E25\u683C\u6309\u7167\u9879\u76EE\u7ED3\u6784\u653E\u7F6E\u6587\u4EF6
779
+ 5. **\u5FC5\u987B\u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u5199 TODO \u6216\u5360\u4F4D\u7B26
780
+ 6. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA\u9875\u9762**\uFF0C\u5FC5\u987B\u751F\u6210\u9875\u9762\u7EC4\u4EF6
781
+ 7. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA UI**\uFF0C\u5FC5\u987B\u751F\u6210\u5BF9\u5E94\u7684\u7EC4\u4EF6\u548C\u6837\u5F0F
782
+
783
+ ${structureGuide}
784
+
785
+ ## \u9879\u76EE\u4FE1\u606F
786
+
787
+ - \u540D\u79F0: ${session.context?.name || "\u672A\u547D\u540D\u9879\u76EE"}
788
+ - \u6846\u67B6: ${framework}
789
+ - \u6280\u672F\u6808: ${techStack}
790
+
791
+ ## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
792
+
793
+ ${standardsText || `### \u9ED8\u8BA4\u89C4\u8303
794
+ - \u4F7F\u7528\u51FD\u6570\u5F0F\u7EC4\u4EF6
795
+ - \u4F7F\u7528 TypeScript \u7C7B\u578B\u5B9A\u4E49
796
+ - \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65
797
+ - \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase
798
+ - \u5BFC\u51FA\u4F7F\u7528 export default \u6216 export const`}
799
+
800
+ ## \u8F93\u51FA\u683C\u5F0F
801
+
802
+ \u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u5305\u88F9\uFF0C\u4F8B\u5982\uFF1A
803
+
804
+ \`\`\`src/pages/HomePage.tsx
805
+ // \u4EE3\u7801\u5185\u5BB9
806
+ \`\`\`
807
+
808
+ \`\`\`src/components/Button/Button.tsx
809
+ // \u4EE3\u7801\u5185\u5BB9
810
+ \`\`\`
811
+
812
+ \u73B0\u5728\u8BF7\u6839\u636E\u4EFB\u52A1\u8981\u6C42\u751F\u6210\u4EE3\u7801\u3002`;
813
+ }
683
814
  function buildTaskPrompt(session, item, index) {
684
815
  const lines = [];
685
816
  lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
@@ -728,11 +859,13 @@ async function executeReview(ctx, session) {
728
859
  const workingDir = ctx.options.workingDirectory;
729
860
  const issues = [];
730
861
  const suggestions = [];
862
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5BA1\u6838\u4EE3\u7801");
863
+ loader.start();
731
864
  try {
732
865
  const codeContents = [];
733
866
  for (const file of session.implFiles) {
734
867
  try {
735
- const content = await fs4__namespace.readFile(path4__namespace.join(workingDir, file), "utf-8");
868
+ const content = await fs9__namespace.readFile(path4__namespace.join(workingDir, file), "utf-8");
736
869
  codeContents.push(`// ${file}
737
870
  ${content}`);
738
871
  } catch {
@@ -741,7 +874,7 @@ ${content}`);
741
874
  const testContents = [];
742
875
  for (const file of session.testFiles) {
743
876
  try {
744
- const content = await fs4__namespace.readFile(path4__namespace.join(workingDir, file), "utf-8");
877
+ const content = await fs9__namespace.readFile(path4__namespace.join(workingDir, file), "utf-8");
745
878
  testContents.push(`// ${file}
746
879
  ${content}`);
747
880
  } catch {
@@ -790,7 +923,9 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
790
923
  const response = await ctx.modelService.sendMessage(messages, {
791
924
  temperature: 0.2,
792
925
  maxTokens: 2e3,
793
- agent: "code-reviewer"
926
+ agent: "code-reviewer",
927
+ timeout: 18e4
928
+ // 3分钟超时
794
929
  });
795
930
  const result = response.content;
796
931
  const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
@@ -812,8 +947,10 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
812
947
  const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
813
948
  suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
814
949
  }
950
+ loader.stop(passed ? chalk9__default.default.green(" \u2713 \u5BA1\u6838\u901A\u8FC7") : chalk9__default.default.yellow(" \u26A0 \u53D1\u73B0\u95EE\u9898"));
815
951
  return { passed, issues, suggestions };
816
952
  } catch (error) {
953
+ loader.stop(chalk9__default.default.red(" \u2717 \u5BA1\u6838\u51FA\u9519"));
817
954
  suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
818
955
  return { passed: true, issues, suggestions };
819
956
  }
@@ -831,9 +968,9 @@ async function readProjectContext(workingDir) {
831
968
  };
832
969
  const agentsPath = path4__namespace.join(workingDir, "AGENTS.md");
833
970
  try {
834
- const stats = await fs4__namespace.stat(agentsPath);
971
+ const stats = await fs9__namespace.stat(agentsPath);
835
972
  if (stats.size <= MAX_FILE_SIZE2) {
836
- context.agentsMd = await fs4__namespace.readFile(agentsPath, "utf-8");
973
+ context.agentsMd = await fs9__namespace.readFile(agentsPath, "utf-8");
837
974
  const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
838
975
  if (nameMatch) context.name = nameMatch[1];
839
976
  const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
@@ -847,9 +984,9 @@ async function readProjectContext(workingDir) {
847
984
  }
848
985
  const configPath = path4__namespace.join(workingDir, "openspec", "config.yaml");
849
986
  try {
850
- const stats = await fs4__namespace.stat(configPath);
987
+ const stats = await fs9__namespace.stat(configPath);
851
988
  if (stats.size <= MAX_FILE_SIZE2) {
852
- context.configYaml = await fs4__namespace.readFile(configPath, "utf-8");
989
+ context.configYaml = await fs9__namespace.readFile(configPath, "utf-8");
853
990
  const nameMatch = context.configYaml.match(/name:\s*(.+)/);
854
991
  if (nameMatch) context.name = nameMatch[1].trim();
855
992
  const typeMatch = context.configYaml.match(/type:\s*(.+)/);
@@ -870,14 +1007,173 @@ async function readProjectContext(workingDir) {
870
1007
  }
871
1008
  const devStandardsPath = path4__namespace.join(workingDir, ".sf-cli", "norms", "devstanded.md");
872
1009
  try {
873
- const stats = await fs4__namespace.stat(devStandardsPath);
1010
+ const stats = await fs9__namespace.stat(devStandardsPath);
874
1011
  if (stats.size <= MAX_FILE_SIZE2) {
875
- context.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8");
1012
+ context.devStandards = await fs9__namespace.readFile(devStandardsPath, "utf-8");
876
1013
  }
877
1014
  } catch {
878
1015
  }
1016
+ if (!context.framework) {
1017
+ const detectedFramework = await detectFramework2(workingDir);
1018
+ if (detectedFramework) {
1019
+ context.framework = detectedFramework;
1020
+ context.techStack = [detectedFramework, ...context.techStack.filter((t) => t !== detectedFramework)];
1021
+ }
1022
+ }
1023
+ if (!context.devStandards) {
1024
+ context.devStandards = await analyzeProjectForStandards(workingDir, context);
1025
+ }
1026
+ context.projectStructure = await analyzeProjectStructure(workingDir);
879
1027
  return context;
880
1028
  }
1029
+ async function detectFramework2(workingDir) {
1030
+ try {
1031
+ const pkgPath = path4__namespace.join(workingDir, "package.json");
1032
+ const pkgContent = await fs9__namespace.readFile(pkgPath, "utf-8");
1033
+ const pkg = JSON.parse(pkgContent);
1034
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
1035
+ if (deps["react"] || deps["next"]) return "React";
1036
+ if (deps["vue"] || deps["nuxt"]) return "Vue";
1037
+ if (deps["angular"] || deps["@angular/core"]) return "Angular";
1038
+ if (deps["svelte"]) return "Svelte";
1039
+ if (deps["solid-js"]) return "Solid";
1040
+ if (deps["vite"] || deps["webpack"]) return "JavaScript";
1041
+ if (deps["typescript"]) return "TypeScript";
1042
+ return null;
1043
+ } catch {
1044
+ return null;
1045
+ }
1046
+ }
1047
+ async function analyzeProjectForStandards(workingDir, context) {
1048
+ const standards = [];
1049
+ standards.push(`# \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u81EA\u52A8\u751F\u6210\uFF09
1050
+ > \u9879\u76EE\u540D\u79F0: ${context.name}
1051
+ > \u6846\u67B6: ${context.framework || "\u672A\u68C0\u6D4B\u5230"}
1052
+ > \u6280\u672F\u6808: ${context.techStack.join(", ") || "\u672A\u68C0\u6D4B\u5230"}
1053
+ `);
1054
+ try {
1055
+ const tsConfigPath = path4__namespace.join(workingDir, "tsconfig.json");
1056
+ await fs9__namespace.access(tsConfigPath);
1057
+ standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 TypeScript");
1058
+ } catch {
1059
+ standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 JavaScript");
1060
+ }
1061
+ try {
1062
+ const srcDir = path4__namespace.join(workingDir, "src");
1063
+ const files = await listFilesDeep(srcDir);
1064
+ const hasCss = files.some((f) => f.endsWith(".css"));
1065
+ const hasScss = files.some((f) => f.endsWith(".scss") || f.endsWith(".sass"));
1066
+ const hasLess = files.some((f) => f.endsWith(".less"));
1067
+ const hasStyled = files.some((f) => f.includes(".styled.") || f.includes("styled-components"));
1068
+ if (hasScss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 SCSS/Sass");
1069
+ else if (hasLess) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 Less");
1070
+ else if (hasStyled) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 styled-components");
1071
+ else if (hasCss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 CSS");
1072
+ } catch {
1073
+ }
1074
+ try {
1075
+ const srcDir = path4__namespace.join(workingDir, "src");
1076
+ const entries = await fs9__namespace.readdir(srcDir, { withFileTypes: true });
1077
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
1078
+ if (dirs.includes("components")) standards.push("\n## \u76EE\u5F55\u7ED3\u6784\n- \u7EC4\u4EF6\u653E\u5728 src/components/");
1079
+ if (dirs.includes("pages") || dirs.includes("views")) standards.push("- \u9875\u9762\u653E\u5728 src/pages/ \u6216 src/views/");
1080
+ if (dirs.includes("hooks")) standards.push("- Hooks \u653E\u5728 src/hooks/");
1081
+ if (dirs.includes("utils")) standards.push("- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/");
1082
+ if (dirs.includes("api") || dirs.includes("services")) standards.push("- API \u8C03\u7528\u653E\u5728 src/api/ \u6216 src/services/");
1083
+ if (dirs.includes("store") || dirs.includes("stores")) standards.push("- \u72B6\u6001\u7BA1\u7406\u653E\u5728 src/store/");
1084
+ } catch {
1085
+ }
1086
+ standards.push("\n## \u4EE3\u7801\u89C4\u8303");
1087
+ standards.push("- \u4F7F\u7528 ES6+ \u8BED\u6CD5");
1088
+ standards.push("- \u7EC4\u4EF6\u4F7F\u7528\u51FD\u6570\u5F0F\u5199\u6CD5");
1089
+ standards.push("- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65");
1090
+ standards.push("- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase");
1091
+ standards.push("- \u5E38\u91CF\u4F7F\u7528 UPPER_SNAKE_CASE");
1092
+ return standards.join("\n");
1093
+ }
1094
+ async function listFilesDeep(dir, maxDepth = 3) {
1095
+ const files = [];
1096
+ async function scan(currentDir, depth) {
1097
+ if (depth > maxDepth) return;
1098
+ try {
1099
+ const entries = await fs9__namespace.readdir(currentDir, { withFileTypes: true });
1100
+ for (const entry of entries) {
1101
+ if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
1102
+ const fullPath = path4__namespace.join(currentDir, entry.name);
1103
+ if (entry.isDirectory()) {
1104
+ await scan(fullPath, depth + 1);
1105
+ } else {
1106
+ files.push(fullPath);
1107
+ }
1108
+ }
1109
+ } catch {
1110
+ }
1111
+ }
1112
+ await scan(dir, 0);
1113
+ return files;
1114
+ }
1115
+ async function analyzeProjectStructure(workingDir) {
1116
+ const structure = {
1117
+ hasSrc: false,
1118
+ hasPages: false,
1119
+ hasComponents: false,
1120
+ hasApi: false,
1121
+ hasHooks: false,
1122
+ hasStore: false,
1123
+ hasUtils: false,
1124
+ srcDir: "src",
1125
+ pagesDir: "",
1126
+ componentsDir: ""
1127
+ };
1128
+ try {
1129
+ const srcDir = path4__namespace.join(workingDir, "src");
1130
+ try {
1131
+ const srcStat = await fs9__namespace.stat(srcDir);
1132
+ if (srcStat.isDirectory()) {
1133
+ structure.hasSrc = true;
1134
+ structure.srcDir = "src";
1135
+ const srcEntries = await fs9__namespace.readdir(srcDir, { withFileTypes: true });
1136
+ const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
1137
+ if (srcDirs.includes("pages")) {
1138
+ structure.hasPages = true;
1139
+ structure.pagesDir = "src/pages";
1140
+ } else if (srcDirs.includes("views")) {
1141
+ structure.hasPages = true;
1142
+ structure.pagesDir = "src/views";
1143
+ }
1144
+ if (srcDirs.includes("components")) {
1145
+ structure.hasComponents = true;
1146
+ structure.componentsDir = "src/components";
1147
+ }
1148
+ if (srcDirs.includes("api") || srcDirs.includes("services")) {
1149
+ structure.hasApi = true;
1150
+ }
1151
+ if (srcDirs.includes("hooks")) {
1152
+ structure.hasHooks = true;
1153
+ }
1154
+ if (srcDirs.includes("store") || srcDirs.includes("stores")) {
1155
+ structure.hasStore = true;
1156
+ }
1157
+ if (srcDirs.includes("utils")) {
1158
+ structure.hasUtils = true;
1159
+ }
1160
+ }
1161
+ } catch {
1162
+ const rootEntries = await fs9__namespace.readdir(workingDir, { withFileTypes: true });
1163
+ const rootDirs = rootEntries.filter((e) => e.isDirectory()).map((e) => e.name);
1164
+ if (rootDirs.includes("pages")) {
1165
+ structure.hasPages = true;
1166
+ structure.pagesDir = "pages";
1167
+ }
1168
+ if (rootDirs.includes("components")) {
1169
+ structure.hasComponents = true;
1170
+ structure.componentsDir = "components";
1171
+ }
1172
+ }
1173
+ } catch {
1174
+ }
1175
+ return structure;
1176
+ }
881
1177
  function analyzeComplexity(requirement, context) {
882
1178
  let score = 3;
883
1179
  if (requirement.length > 100) score += 1;
@@ -1019,7 +1315,8 @@ ${r.analysis}`).join("\n\n") : "\u65E0"}
1019
1315
  ], {
1020
1316
  temperature: 0.3,
1021
1317
  maxTokens: 4e3,
1022
- timeout: 12e4
1318
+ timeout: 3e5
1319
+ // 5分钟超时
1023
1320
  });
1024
1321
  try {
1025
1322
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
@@ -1227,7 +1524,7 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1227
1524
  - \u96C6\u6210\u6D4B\u8BD5
1228
1525
 
1229
1526
  \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
1230
- const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1527
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1231
1528
  loader.start();
1232
1529
  try {
1233
1530
  const response = await ctx.modelService.sendMessage([
@@ -1235,8 +1532,8 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1235
1532
  ], {
1236
1533
  temperature: 0.3,
1237
1534
  maxTokens: 4e3,
1238
- timeout: 18e4
1239
- // 增加到3分钟
1535
+ timeout: 3e5
1536
+ // 5分钟超时
1240
1537
  });
1241
1538
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1242
1539
  if (jsonMatch) {
@@ -1279,12 +1576,96 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
1279
1576
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
1280
1577
  }
1281
1578
  }
1579
+ async function generateSpecItemsWithFeedback(requirement, context, bddScenarios, questions, references, feedbacks, ctx) {
1580
+ 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
1581
+
1582
+ ## \u9700\u6C42\u63CF\u8FF0
1583
+ ${requirement}
1584
+
1585
+ ## \u9879\u76EE\u4E0A\u4E0B\u6587
1586
+ - \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
1587
+ - \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
1588
+ ${context.devStandards ? `
1589
+ ## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
1590
+ ${context.devStandards.slice(0, 2e3)}
1591
+ ` : ""}
1592
+
1593
+ ## \u4E4B\u524D\u7684 BDD \u573A\u666F
1594
+ ${bddScenarios.map((s) => `- Feature: ${s.feature}`).join("\n")}
1595
+
1596
+ ## \u7528\u6237\u53CD\u9988\uFF08\u5FC5\u987B\u89E3\u51B3\u8FD9\u4E9B\u95EE\u9898\uFF09
1597
+ ${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
1598
+
1599
+ ## \u8981\u6C42
1600
+ 1. **\u5FC5\u987B\u89E3\u51B3\u7528\u6237\u53CD\u9988\u4E2D\u7684\u6240\u6709\u95EE\u9898**
1601
+ 2. \u5982\u679C\u7528\u6237\u6307\u51FA\u9057\u6F0F\u7684\u529F\u80FD\u70B9\uFF0C\u8BF7\u8865\u5145\u76F8\u5173\u4EFB\u52A1
1602
+ 3. \u5982\u679C\u7528\u6237\u6307\u51FA\u62C6\u5206\u4E0D\u5408\u7406\uFF0C\u8BF7\u91CD\u65B0\u8C03\u6574\u7C92\u5EA6
1603
+ 4. \u5982\u679C\u7528\u6237\u9700\u8981\u66F4\u591A\u7EC6\u8282\uFF0C\u8BF7\u62C6\u5206\u5F97\u66F4\u7EC6\u81F4
1604
+ 5. \u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\u5355\u4E00\u804C\u8D23\u30012-4\u5C0F\u65F6\u53EF\u5B8C\u6210
1605
+
1606
+ ## \u8F93\u51FA\u683C\u5F0F (JSON)
1607
+ \`\`\`json
1608
+ [
1609
+ {
1610
+ "id": "T001",
1611
+ "title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
1612
+ "description": "\u8BE6\u7EC6\u63CF\u8FF0",
1613
+ "priority": "high",
1614
+ "acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
1615
+ }
1616
+ ]
1617
+ \`\`\`
1618
+
1619
+ \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u786E\u4FDD\u89E3\u51B3\u4E86\u7528\u6237\u7684\u6240\u6709\u53CD\u9988\u3002`;
1620
+ try {
1621
+ const response = await ctx.modelService.sendMessage([
1622
+ { role: "user", content: prompt2 }
1623
+ ], {
1624
+ temperature: 0.3,
1625
+ maxTokens: 4e3,
1626
+ timeout: 3e5
1627
+ // 5分钟超时
1628
+ });
1629
+ const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
1630
+ if (jsonMatch) {
1631
+ try {
1632
+ const parsed = JSON.parse(jsonMatch[1].trim());
1633
+ return parsed.map((item, index) => ({
1634
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1635
+ title: item.title,
1636
+ description: item.description,
1637
+ priority: item.priority || "medium",
1638
+ files: [],
1639
+ tests: item.acceptanceCriteria || []
1640
+ }));
1641
+ } catch {
1642
+ }
1643
+ }
1644
+ try {
1645
+ const parsed = JSON.parse(response.content);
1646
+ if (Array.isArray(parsed)) {
1647
+ return parsed.map((item, index) => ({
1648
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
1649
+ title: item.title,
1650
+ description: item.description,
1651
+ priority: item.priority || "medium",
1652
+ files: [],
1653
+ tests: item.acceptanceCriteria || []
1654
+ }));
1655
+ }
1656
+ } catch {
1657
+ }
1658
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1659
+ } catch {
1660
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
1661
+ }
1662
+ }
1282
1663
  async function saveSpecFile(workingDir, session) {
1283
1664
  const specDir = path4__namespace.join(workingDir, "openspec", "changes");
1284
- await fs4__namespace.mkdir(specDir, { recursive: true });
1665
+ await fs9__namespace.mkdir(specDir, { recursive: true });
1285
1666
  const specPath = path4__namespace.join(specDir, `${session.id}-spec.md`);
1286
1667
  const content = formatSpecFile(session);
1287
- await fs4__namespace.writeFile(specPath, content, "utf-8");
1668
+ await fs9__namespace.writeFile(specPath, content, "utf-8");
1288
1669
  return specPath;
1289
1670
  }
1290
1671
  function formatSpecFile(session) {
@@ -1345,6 +1726,16 @@ function formatSpecFile(session) {
1345
1726
  lines.push("");
1346
1727
  }
1347
1728
  }
1729
+ if (session.specFeedbacks && session.specFeedbacks.length > 0) {
1730
+ lines.push("## \u89C4\u683C\u8FED\u4EE3\u53CD\u9988");
1731
+ lines.push("");
1732
+ for (let i = 0; i < session.specFeedbacks.length; i++) {
1733
+ lines.push(`${i + 1}. ${session.specFeedbacks[i]}`);
1734
+ }
1735
+ lines.push("");
1736
+ lines.push("---");
1737
+ lines.push("");
1738
+ }
1348
1739
  lines.push("## \u4EFB\u52A1\u5217\u8868");
1349
1740
  lines.push("");
1350
1741
  for (const item of session.specItems) {
@@ -1359,22 +1750,22 @@ function formatSpecFile(session) {
1359
1750
  }
1360
1751
  async function generateTests(workingDir, session, ctx) {
1361
1752
  const testDir = path4__namespace.join(workingDir, "tests");
1362
- await fs4__namespace.mkdir(testDir, { recursive: true });
1753
+ await fs9__namespace.mkdir(testDir, { recursive: true });
1363
1754
  const testFiles = [];
1364
1755
  if (ctx?.modelService) {
1365
1756
  for (const scenario of session.bddScenarios) {
1366
1757
  const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
1367
1758
  const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
1368
- const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
1759
+ const loader = new LoadingIndicator2(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
1369
1760
  loader.start();
1370
1761
  try {
1371
1762
  const content = await generateTestFileWithAI(scenario, session, ctx);
1372
- await fs4__namespace.writeFile(testPath, content, "utf-8");
1763
+ await fs9__namespace.writeFile(testPath, content, "utf-8");
1373
1764
  testFiles.push(`tests/${testName}.test.ts`);
1374
1765
  loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
1375
1766
  } catch {
1376
1767
  const content = generateTestFile(scenario, session);
1377
- await fs4__namespace.writeFile(testPath, content, "utf-8");
1768
+ await fs9__namespace.writeFile(testPath, content, "utf-8");
1378
1769
  testFiles.push(`tests/${testName}.test.ts`);
1379
1770
  loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
1380
1771
  }
@@ -1384,7 +1775,7 @@ async function generateTests(workingDir, session, ctx) {
1384
1775
  const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
1385
1776
  const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
1386
1777
  const content = generateTestFile(scenario, session);
1387
- await fs4__namespace.writeFile(testPath, content, "utf-8");
1778
+ await fs9__namespace.writeFile(testPath, content, "utf-8");
1388
1779
  testFiles.push(`tests/${testName}.test.ts`);
1389
1780
  }
1390
1781
  }
@@ -1420,7 +1811,9 @@ ${scenario.scenarios.map((s) => `
1420
1811
  { role: "user", content: prompt2 }
1421
1812
  ], {
1422
1813
  temperature: 0.3,
1423
- maxTokens: 4e3
1814
+ maxTokens: 4e3,
1815
+ timeout: 18e4
1816
+ // 3分钟超时
1424
1817
  });
1425
1818
  const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
1426
1819
  if (codeMatch) {
@@ -1476,7 +1869,7 @@ function generateTestFile(scenario, session) {
1476
1869
  async function archiveWorkflow(workingDir) {
1477
1870
  if (!activeSession) return;
1478
1871
  const archiveDir = path4__namespace.join(workingDir, "openspec", "spec");
1479
- await fs4__namespace.mkdir(archiveDir, { recursive: true });
1872
+ await fs9__namespace.mkdir(archiveDir, { recursive: true });
1480
1873
  const archivePath = path4__namespace.join(archiveDir, `${activeSession.id}.md`);
1481
1874
  const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
1482
1875
 
@@ -1503,7 +1896,7 @@ ${activeSession.refinedRequirement}
1503
1896
 
1504
1897
  ${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
1505
1898
  `;
1506
- await fs4__namespace.writeFile(archivePath, content, "utf-8");
1899
+ await fs9__namespace.writeFile(archivePath, content, "utf-8");
1507
1900
  }
1508
1901
  function generateSessionId() {
1509
1902
  const timestamp = Date.now().toString(36);
@@ -1519,7 +1912,10 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
1519
1912
  const type = detectResourceType(url);
1520
1913
  let content = "";
1521
1914
  let analysis = "";
1915
+ const loader = new LoadingIndicator2(`\u83B7\u53D6 ${url.slice(0, 40)}...`);
1916
+ loader.start();
1522
1917
  try {
1918
+ loader.update("\u6B63\u5728\u83B7\u53D6\u7F51\u9875\u5185\u5BB9");
1523
1919
  const response = await fetch(url, {
1524
1920
  headers: {
1525
1921
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
@@ -1530,11 +1926,14 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
1530
1926
  }
1531
1927
  content = await response.text();
1532
1928
  if (ctx.modelService.getCurrentModel()) {
1929
+ loader.update("\u6B63\u5728\u5206\u6790\u5185\u5BB9");
1533
1930
  analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
1534
1931
  } else {
1535
1932
  analysis = extractBasicInfo(content, type);
1536
1933
  }
1934
+ loader.stop();
1537
1935
  } catch (error) {
1936
+ loader.stop();
1538
1937
  throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
1539
1938
  }
1540
1939
  return { url, type, content: content.slice(0, 1e4), analysis };
@@ -1603,14 +2002,16 @@ ${content.slice(0, 8e3)}
1603
2002
  \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
1604
2003
  \u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
1605
2004
  `;
1606
- const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
2005
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
1607
2006
  loader.start();
1608
2007
  try {
1609
2008
  const response = await ctx.modelService.sendMessage([
1610
2009
  { role: "user", content: prompt2 }
1611
2010
  ], {
1612
2011
  temperature: 0.3,
1613
- maxTokens: 4e3
2012
+ maxTokens: 4e3,
2013
+ timeout: 18e4
2014
+ // 3分钟超时
1614
2015
  });
1615
2016
  loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
1616
2017
  return response.content;
@@ -1657,11 +2058,11 @@ function getActiveSession() {
1657
2058
  function clearActiveSession() {
1658
2059
  activeSession = null;
1659
2060
  }
1660
- var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
2061
+ var LoadingIndicator2, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
1661
2062
  var init_new = __esm({
1662
2063
  "src/commands/new.ts"() {
1663
2064
  init_cjs_shims();
1664
- LoadingIndicator = class {
2065
+ LoadingIndicator2 = class {
1665
2066
  frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1666
2067
  frameIndex = 0;
1667
2068
  interval = null;
@@ -1771,7 +2172,7 @@ var ConfigManager = class {
1771
2172
  this.projectPath = projectPath;
1772
2173
  this.configPath = path4__namespace.join(projectPath, ".sf-cli", "config.json");
1773
2174
  try {
1774
- const content = await fs4__namespace.readFile(this.configPath, "utf-8");
2175
+ const content = await fs9__namespace.readFile(this.configPath, "utf-8");
1775
2176
  const loaded = JSON.parse(content);
1776
2177
  if (loaded.apiKey && loaded.apiKeyEncrypted) {
1777
2178
  loaded.apiKey = this.decrypt(loaded.apiKey);
@@ -1790,8 +2191,8 @@ var ConfigManager = class {
1790
2191
  configToSave.apiKey = encrypted;
1791
2192
  configToSave.apiKeyEncrypted = true;
1792
2193
  }
1793
- await fs4__namespace.mkdir(path4__namespace.dirname(this.configPath), { recursive: true });
1794
- await fs4__namespace.writeFile(
2194
+ await fs9__namespace.mkdir(path4__namespace.dirname(this.configPath), { recursive: true });
2195
+ await fs9__namespace.writeFile(
1795
2196
  this.configPath,
1796
2197
  JSON.stringify(configToSave, null, 2),
1797
2198
  "utf-8"
@@ -2026,9 +2427,10 @@ var BaseAdapter = class {
2026
2427
  }
2027
2428
  /**
2028
2429
  * 获取超时时间
2430
+ * 默认 5 分钟,对于复杂的代码生成任务足够
2029
2431
  */
2030
2432
  getTimeout() {
2031
- return this.config?.timeout || 6e4;
2433
+ return this.config?.timeout || 3e5;
2032
2434
  }
2033
2435
  /**
2034
2436
  * 获取重试次数
@@ -2888,8 +3290,8 @@ var ModelService = class {
2888
3290
  const dailyPath = path4__namespace.join(this.statsPath, "daily.json");
2889
3291
  const totalPath = path4__namespace.join(this.statsPath, "total.json");
2890
3292
  const [daily, total] = await Promise.all([
2891
- fs4__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
2892
- fs4__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
3293
+ fs9__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
3294
+ fs9__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
2893
3295
  ]);
2894
3296
  const dailyData = JSON.parse(daily);
2895
3297
  const totalData = JSON.parse(total);
@@ -2902,12 +3304,12 @@ var ModelService = class {
2902
3304
  async saveStats() {
2903
3305
  if (!this.statsPath) return;
2904
3306
  try {
2905
- await fs4__namespace.mkdir(this.statsPath, { recursive: true });
3307
+ await fs9__namespace.mkdir(this.statsPath, { recursive: true });
2906
3308
  const dailyPath = path4__namespace.join(this.statsPath, "daily.json");
2907
3309
  const totalPath = path4__namespace.join(this.statsPath, "total.json");
2908
3310
  await Promise.all([
2909
- fs4__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
2910
- fs4__namespace.writeFile(totalPath, JSON.stringify({
3311
+ fs9__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
3312
+ fs9__namespace.writeFile(totalPath, JSON.stringify({
2911
3313
  totalInput: this.stats.totalInput,
2912
3314
  totalOutput: this.stats.totalOutput
2913
3315
  }))
@@ -4616,15 +5018,15 @@ async function loadProjectContext(workingDirectory) {
4616
5018
  devStandards: ""
4617
5019
  };
4618
5020
  try {
4619
- context.agentsMd = await fs4__namespace.readFile(path4__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
5021
+ context.agentsMd = await fs9__namespace.readFile(path4__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
4620
5022
  } catch {
4621
5023
  }
4622
5024
  try {
4623
- context.configYaml = await fs4__namespace.readFile(path4__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
5025
+ context.configYaml = await fs9__namespace.readFile(path4__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
4624
5026
  } catch {
4625
5027
  }
4626
5028
  try {
4627
- context.devStandards = await fs4__namespace.readFile(path4__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
5029
+ context.devStandards = await fs9__namespace.readFile(path4__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
4628
5030
  } catch {
4629
5031
  }
4630
5032
  return context;
@@ -5587,19 +5989,19 @@ var WorkflowEngine = class {
5587
5989
  async loadProjectContext() {
5588
5990
  const agentsMdPath = path4__namespace.join(this.projectPath, "AGENTS.md");
5589
5991
  try {
5590
- this.projectContext = await fs4__namespace.readFile(agentsMdPath, "utf-8");
5992
+ this.projectContext = await fs9__namespace.readFile(agentsMdPath, "utf-8");
5591
5993
  } catch {
5592
5994
  this.projectContext = "";
5593
5995
  }
5594
5996
  const configPath = path4__namespace.join(this.openspecPath, "config.yaml");
5595
5997
  try {
5596
- this.projectConfig = await fs4__namespace.readFile(configPath, "utf-8");
5998
+ this.projectConfig = await fs9__namespace.readFile(configPath, "utf-8");
5597
5999
  } catch {
5598
6000
  this.projectConfig = "";
5599
6001
  }
5600
6002
  const devstandedPath = path4__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
5601
6003
  try {
5602
- this.devStandards = await fs4__namespace.readFile(devstandedPath, "utf-8");
6004
+ this.devStandards = await fs9__namespace.readFile(devstandedPath, "utf-8");
5603
6005
  } catch {
5604
6006
  this.devStandards = "";
5605
6007
  }
@@ -5628,7 +6030,7 @@ var WorkflowEngine = class {
5628
6030
  const specPath = this.getSpecFilePath();
5629
6031
  if (!specPath) return false;
5630
6032
  try {
5631
- await fs4__namespace.access(specPath);
6033
+ await fs9__namespace.access(specPath);
5632
6034
  return true;
5633
6035
  } catch {
5634
6036
  return false;
@@ -5862,11 +6264,11 @@ var WorkflowEngine = class {
5862
6264
  const workflows = [];
5863
6265
  const changesDir = path4__namespace.join(this.openspecPath, "changes");
5864
6266
  try {
5865
- const files = await fs4__namespace.readdir(changesDir);
6267
+ const files = await fs9__namespace.readdir(changesDir);
5866
6268
  for (const file of files) {
5867
6269
  if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
5868
6270
  const filePath = path4__namespace.join(changesDir, file);
5869
- const content = await fs4__namespace.readFile(filePath, "utf-8");
6271
+ const content = await fs9__namespace.readFile(filePath, "utf-8");
5870
6272
  const state = this.parseChangeRecord(content);
5871
6273
  if (state && state.status === "running") {
5872
6274
  workflows.push(state);
@@ -5917,7 +6319,7 @@ var WorkflowEngine = class {
5917
6319
  }
5918
6320
  const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
5919
6321
  try {
5920
- const content = await fs4__namespace.readFile(statePath, "utf-8");
6322
+ const content = await fs9__namespace.readFile(statePath, "utf-8");
5921
6323
  this.state = JSON.parse(content, (key, value) => {
5922
6324
  if (key.endsWith("At") && typeof value === "string") {
5923
6325
  return new Date(value);
@@ -5930,7 +6332,7 @@ var WorkflowEngine = class {
5930
6332
  const changesDir = path4__namespace.join(this.openspecPath, "changes");
5931
6333
  const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
5932
6334
  try {
5933
- const content = await fs4__namespace.readFile(changeFile, "utf-8");
6335
+ const content = await fs9__namespace.readFile(changeFile, "utf-8");
5934
6336
  const parsed = this.parseChangeRecord(content);
5935
6337
  if (parsed && parsed.status === "running") {
5936
6338
  this.state = parsed;
@@ -5995,8 +6397,8 @@ var WorkflowEngine = class {
5995
6397
  const archiveDir = path4__namespace.join(changesDir, "archive");
5996
6398
  const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
5997
6399
  const archiveFile = path4__namespace.join(archiveDir, `${changeId}.md`);
5998
- await fs4__namespace.mkdir(archiveDir, { recursive: true });
5999
- await fs4__namespace.rename(changeFile, archiveFile).catch(() => {
6400
+ await fs9__namespace.mkdir(archiveDir, { recursive: true });
6401
+ await fs9__namespace.rename(changeFile, archiveFile).catch(() => {
6000
6402
  });
6001
6403
  this.state = null;
6002
6404
  this.snapshots.clear();
@@ -6022,15 +6424,15 @@ var WorkflowEngine = class {
6022
6424
  const archiveDir = path4__namespace.join(changesDir, "archive");
6023
6425
  const specDir = path4__namespace.join(this.openspecPath, "spec");
6024
6426
  const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
6025
- await fs4__namespace.mkdir(archiveDir, { recursive: true });
6026
- await fs4__namespace.mkdir(specDir, { recursive: true });
6027
- await fs4__namespace.mkdir(statesDir, { recursive: true });
6427
+ await fs9__namespace.mkdir(archiveDir, { recursive: true });
6428
+ await fs9__namespace.mkdir(specDir, { recursive: true });
6429
+ await fs9__namespace.mkdir(statesDir, { recursive: true });
6028
6430
  }
6029
6431
  async restoreState() {
6030
6432
  const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
6031
6433
  let activeId = null;
6032
6434
  try {
6033
- const activeContent = await fs4__namespace.readFile(activePath, "utf-8");
6435
+ const activeContent = await fs9__namespace.readFile(activePath, "utf-8");
6034
6436
  const activeData = JSON.parse(activeContent);
6035
6437
  activeId = activeData.activeId;
6036
6438
  } catch {
@@ -6038,7 +6440,7 @@ var WorkflowEngine = class {
6038
6440
  if (activeId) {
6039
6441
  const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
6040
6442
  try {
6041
- const content = await fs4__namespace.readFile(statePath, "utf-8");
6443
+ const content = await fs9__namespace.readFile(statePath, "utf-8");
6042
6444
  this.state = JSON.parse(content, (key, value) => {
6043
6445
  if (key.endsWith("At") && typeof value === "string") {
6044
6446
  return new Date(value);
@@ -6051,7 +6453,7 @@ var WorkflowEngine = class {
6051
6453
  }
6052
6454
  const oldStatePath = path4__namespace.join(this.openspecPath, ".workflow-state.json");
6053
6455
  try {
6054
- const content = await fs4__namespace.readFile(oldStatePath, "utf-8");
6456
+ const content = await fs9__namespace.readFile(oldStatePath, "utf-8");
6055
6457
  this.state = JSON.parse(content, (key, value) => {
6056
6458
  if (key.endsWith("At") && typeof value === "string") {
6057
6459
  return new Date(value);
@@ -6060,7 +6462,7 @@ var WorkflowEngine = class {
6060
6462
  });
6061
6463
  if (this.state) {
6062
6464
  await this.saveState();
6063
- await fs4__namespace.unlink(oldStatePath).catch(() => {
6465
+ await fs9__namespace.unlink(oldStatePath).catch(() => {
6064
6466
  });
6065
6467
  }
6066
6468
  } catch (e) {
@@ -6074,16 +6476,16 @@ var WorkflowEngine = class {
6074
6476
  async saveState() {
6075
6477
  if (!this.state) return;
6076
6478
  const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
6077
- await fs4__namespace.mkdir(statesDir, { recursive: true });
6479
+ await fs9__namespace.mkdir(statesDir, { recursive: true });
6078
6480
  const statePath = path4__namespace.join(statesDir, `${this.state.id}.json`);
6079
- await fs4__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
6481
+ await fs9__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
6080
6482
  const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
6081
- await fs4__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
6483
+ await fs9__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
6082
6484
  }
6083
6485
  async restoreSnapshots() {
6084
6486
  const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
6085
6487
  try {
6086
- const content = await fs4__namespace.readFile(snapshotsPath, "utf-8");
6488
+ const content = await fs9__namespace.readFile(snapshotsPath, "utf-8");
6087
6489
  const data = JSON.parse(content, (key, value) => {
6088
6490
  if (key === "timestamp" && typeof value === "string") {
6089
6491
  return new Date(value);
@@ -6100,7 +6502,7 @@ var WorkflowEngine = class {
6100
6502
  async saveSnapshots() {
6101
6503
  const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
6102
6504
  const data = Array.from(this.snapshots.values());
6103
- await fs4__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
6505
+ await fs9__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
6104
6506
  }
6105
6507
  async createSnapshot() {
6106
6508
  if (!this.state) return;
@@ -6121,7 +6523,7 @@ var WorkflowEngine = class {
6121
6523
  async createChangeRecord() {
6122
6524
  if (!this.state) return;
6123
6525
  const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
6124
- await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
6526
+ await fs9__namespace.writeFile(changePath, this.formatChangeRecord());
6125
6527
  }
6126
6528
  async updateChangeRecord(status) {
6127
6529
  if (!this.state) return;
@@ -6129,7 +6531,7 @@ var WorkflowEngine = class {
6129
6531
  this.state.status = status;
6130
6532
  }
6131
6533
  const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
6132
- await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
6534
+ await fs9__namespace.writeFile(changePath, this.formatChangeRecord());
6133
6535
  }
6134
6536
  formatChangeRecord() {
6135
6537
  if (!this.state) return "";
@@ -6193,7 +6595,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
6193
6595
 
6194
6596
  ${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
6195
6597
  `;
6196
- await fs4__namespace.writeFile(specPath, content);
6598
+ await fs9__namespace.writeFile(specPath, content);
6197
6599
  }
6198
6600
  };
6199
6601
  var ConfirmationRequiredError = class extends Error {
@@ -6242,11 +6644,11 @@ var NormsManager = class {
6242
6644
  const files = await this.collectProjectFiles(projectPath);
6243
6645
  for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
6244
6646
  try {
6245
- const stats = await fs4__namespace.stat(filePath);
6647
+ const stats = await fs9__namespace.stat(filePath);
6246
6648
  if (stats.size > MAX_FILE_SIZE) {
6247
6649
  continue;
6248
6650
  }
6249
- const content = await fs4__namespace.readFile(filePath, "utf-8");
6651
+ const content = await fs9__namespace.readFile(filePath, "utf-8");
6250
6652
  const patterns = await this.learnFromFile(filePath, content);
6251
6653
  result.patternsFound += patterns.length;
6252
6654
  result.filesScanned++;
@@ -6872,7 +7274,7 @@ ${response}`;
6872
7274
  const files = [];
6873
7275
  async function scan(dir) {
6874
7276
  try {
6875
- const entries = await fs4__namespace.readdir(dir, { withFileTypes: true });
7277
+ const entries = await fs9__namespace.readdir(dir, { withFileTypes: true });
6876
7278
  for (const entry of entries) {
6877
7279
  if (entry.isDirectory()) {
6878
7280
  if (!IGNORED_DIRS.includes(entry.name)) {
@@ -6915,8 +7317,8 @@ ${response}`;
6915
7317
  * 确保规范目录存在
6916
7318
  */
6917
7319
  async ensureNormsDir() {
6918
- await fs4__namespace.mkdir(this.config.normsDir, { recursive: true });
6919
- await fs4__namespace.mkdir(path4__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
7320
+ await fs9__namespace.mkdir(this.config.normsDir, { recursive: true });
7321
+ await fs9__namespace.mkdir(path4__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
6920
7322
  }
6921
7323
  /**
6922
7324
  * 加载已有规范
@@ -6924,7 +7326,7 @@ ${response}`;
6924
7326
  async loadStandards() {
6925
7327
  const standardsPath = path4__namespace.join(this.config.normsDir, "patterns.json");
6926
7328
  try {
6927
- const content = await fs4__namespace.readFile(standardsPath, "utf-8");
7329
+ const content = await fs9__namespace.readFile(standardsPath, "utf-8");
6928
7330
  const data = JSON.parse(content);
6929
7331
  for (const standard of data) {
6930
7332
  this.standards.set(standard.id, {
@@ -6942,7 +7344,7 @@ ${response}`;
6942
7344
  async loadWeights() {
6943
7345
  const weightsPath = path4__namespace.join(this.config.normsDir, "weights.json");
6944
7346
  try {
6945
- const content = await fs4__namespace.readFile(weightsPath, "utf-8");
7347
+ const content = await fs9__namespace.readFile(weightsPath, "utf-8");
6946
7348
  const data = JSON.parse(content);
6947
7349
  for (const weight of data) {
6948
7350
  this.weights.set(weight.standardId, {
@@ -6958,7 +7360,7 @@ ${response}`;
6958
7360
  */
6959
7361
  async saveStandards() {
6960
7362
  const standardsPath = path4__namespace.join(this.config.normsDir, "patterns.json");
6961
- await fs4__namespace.writeFile(
7363
+ await fs9__namespace.writeFile(
6962
7364
  standardsPath,
6963
7365
  JSON.stringify(Array.from(this.standards.values()), null, 2),
6964
7366
  "utf-8"
@@ -6969,7 +7371,7 @@ ${response}`;
6969
7371
  */
6970
7372
  async saveWeights() {
6971
7373
  const weightsPath = path4__namespace.join(this.config.normsDir, "weights.json");
6972
- await fs4__namespace.writeFile(
7374
+ await fs9__namespace.writeFile(
6973
7375
  weightsPath,
6974
7376
  JSON.stringify(Array.from(this.weights.values()), null, 2),
6975
7377
  "utf-8"
@@ -6997,7 +7399,7 @@ ${response}`;
6997
7399
  */
6998
7400
  async saveWeeklyReport(weekId, report) {
6999
7401
  const reportPath = path4__namespace.join(this.config.normsDir, "weekly", `${weekId}.json`);
7000
- await fs4__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
7402
+ await fs9__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
7001
7403
  }
7002
7404
  /**
7003
7405
  * 更新 devstanded.md
@@ -7006,7 +7408,7 @@ ${response}`;
7006
7408
  const standards = this.getEffectiveStandards();
7007
7409
  const content = this.generateDevStandedMd(standards);
7008
7410
  const mdPath = path4__namespace.join(this.config.normsDir, "devstanded.md");
7009
- await fs4__namespace.writeFile(mdPath, content, "utf-8");
7411
+ await fs9__namespace.writeFile(mdPath, content, "utf-8");
7010
7412
  }
7011
7413
  /**
7012
7414
  * 生成 devstanded.md 内容
@@ -7398,14 +7800,14 @@ ${summary}`,
7398
7800
  async persist() {
7399
7801
  if (!this.persistPath) return;
7400
7802
  const dir = path4__namespace.dirname(this.persistPath);
7401
- await fs4__namespace.mkdir(dir, { recursive: true });
7803
+ await fs9__namespace.mkdir(dir, { recursive: true });
7402
7804
  const data = {
7403
7805
  messages: this.state.messages,
7404
7806
  totalTokens: this.state.totalTokens,
7405
7807
  limit: this.state.limit,
7406
7808
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
7407
7809
  };
7408
- await fs4__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
7810
+ await fs9__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
7409
7811
  }
7410
7812
  /**
7411
7813
  * 加载持久化的上下文
@@ -7413,7 +7815,7 @@ ${summary}`,
7413
7815
  async loadPersistedContext() {
7414
7816
  if (!this.persistPath) return;
7415
7817
  try {
7416
- const content = await fs4__namespace.readFile(this.persistPath, "utf-8");
7818
+ const content = await fs9__namespace.readFile(this.persistPath, "utf-8");
7417
7819
  const data = JSON.parse(content);
7418
7820
  this.state.messages = data.messages || [];
7419
7821
  this.state.totalTokens = data.totalTokens || 0;
@@ -7630,7 +8032,7 @@ async function handleInit(args, ctx) {
7630
8032
  async function initProject(options = {}, workingDir) {
7631
8033
  const cwd = workingDir || process.cwd();
7632
8034
  try {
7633
- const stats = await fs4__namespace.stat(cwd);
8035
+ const stats = await fs9__namespace.stat(cwd);
7634
8036
  if (!stats.isDirectory()) {
7635
8037
  return {
7636
8038
  output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
@@ -7662,7 +8064,7 @@ async function initProject(options = {}, workingDir) {
7662
8064
  await normsManager.initialize();
7663
8065
  const scanResult = await normsManager.scanProject(cwd);
7664
8066
  const agentsContent = generateAgentsMd(projectInfo, scanResult);
7665
- await fs4__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
8067
+ await fs9__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
7666
8068
  await generateOpenSpecConfig(openspecDir, projectInfo);
7667
8069
  return {
7668
8070
  output: chalk9__default.default.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9__default.default.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
@@ -7703,7 +8105,7 @@ async function createSfCliDirectory(basePath) {
7703
8105
  "logs"
7704
8106
  ];
7705
8107
  for (const dir of dirs) {
7706
- await fs4__namespace.mkdir(path4__namespace.join(basePath, dir), { recursive: true });
8108
+ await fs9__namespace.mkdir(path4__namespace.join(basePath, dir), { recursive: true });
7707
8109
  }
7708
8110
  const config = {
7709
8111
  version: "1.0.0",
@@ -7711,22 +8113,22 @@ async function createSfCliDirectory(basePath) {
7711
8113
  yolo: false,
7712
8114
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
7713
8115
  };
7714
- await fs4__namespace.writeFile(
8116
+ await fs9__namespace.writeFile(
7715
8117
  path4__namespace.join(basePath, "config.json"),
7716
8118
  JSON.stringify(config, null, 2),
7717
8119
  "utf-8"
7718
8120
  );
7719
- await fs4__namespace.writeFile(
8121
+ await fs9__namespace.writeFile(
7720
8122
  path4__namespace.join(basePath, "agents", "registry.json"),
7721
8123
  JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
7722
8124
  "utf-8"
7723
8125
  );
7724
- await fs4__namespace.writeFile(
8126
+ await fs9__namespace.writeFile(
7725
8127
  path4__namespace.join(basePath, "skills", "registry.json"),
7726
8128
  JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
7727
8129
  "utf-8"
7728
8130
  );
7729
- await fs4__namespace.writeFile(
8131
+ await fs9__namespace.writeFile(
7730
8132
  path4__namespace.join(basePath, "health", "health.md"),
7731
8133
  generateHealthTemplate(),
7732
8134
  "utf-8"
@@ -7736,8 +8138,8 @@ async function createOpenSpecDirectory(basePath) {
7736
8138
  const changesDir = path4__namespace.join(basePath, "changes");
7737
8139
  const archiveDir = path4__namespace.join(changesDir, "archive");
7738
8140
  const specDir = path4__namespace.join(basePath, "spec");
7739
- await fs4__namespace.mkdir(archiveDir, { recursive: true });
7740
- await fs4__namespace.mkdir(specDir, { recursive: true });
8141
+ await fs9__namespace.mkdir(archiveDir, { recursive: true });
8142
+ await fs9__namespace.mkdir(specDir, { recursive: true });
7741
8143
  }
7742
8144
  async function analyzeProject(cwd) {
7743
8145
  const result = {
@@ -7762,7 +8164,7 @@ async function analyzeProject(cwd) {
7762
8164
  };
7763
8165
  const pkgPath = path4__namespace.join(cwd, "package.json");
7764
8166
  try {
7765
- const pkgContent = await fs4__namespace.readFile(pkgPath, "utf-8");
8167
+ const pkgContent = await fs9__namespace.readFile(pkgPath, "utf-8");
7766
8168
  const pkg = JSON.parse(pkgContent);
7767
8169
  result.name = pkg.name || result.name;
7768
8170
  result.dependencies = pkg.dependencies || {};
@@ -7845,7 +8247,7 @@ async function analyzeDirectoryStructure(cwd) {
7845
8247
  others: []
7846
8248
  };
7847
8249
  try {
7848
- const entries = await fs4__namespace.readdir(cwd, { withFileTypes: true });
8250
+ const entries = await fs9__namespace.readdir(cwd, { withFileTypes: true });
7849
8251
  for (const entry of entries) {
7850
8252
  if (!entry.isDirectory()) continue;
7851
8253
  const name = entry.name;
@@ -7890,7 +8292,7 @@ async function findEntryPoints(cwd) {
7890
8292
  }
7891
8293
  async function saveProjectAnalysis(sfCliDir, analysis) {
7892
8294
  const analysisPath = path4__namespace.join(sfCliDir, "cache", "analysis", "project-analysis.json");
7893
- await fs4__namespace.writeFile(
8295
+ await fs9__namespace.writeFile(
7894
8296
  analysisPath,
7895
8297
  JSON.stringify(analysis, null, 2),
7896
8298
  "utf-8"
@@ -7931,7 +8333,7 @@ architecture:
7931
8333
  # \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
7932
8334
  standards: []
7933
8335
  `;
7934
- await fs4__namespace.writeFile(configPath, content, "utf-8");
8336
+ await fs9__namespace.writeFile(configPath, content, "utf-8");
7935
8337
  }
7936
8338
  function generateAgentsMd(info, scanResult) {
7937
8339
  const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
@@ -8053,7 +8455,7 @@ function generateHealthTemplate() {
8053
8455
  }
8054
8456
  async function fileExists(filePath) {
8055
8457
  try {
8056
- await fs4__namespace.access(filePath);
8458
+ await fs9__namespace.access(filePath);
8057
8459
  return true;
8058
8460
  } catch {
8059
8461
  return false;
@@ -8218,6 +8620,36 @@ ${chalk9__default.default.yellow("\u793A\u4F8B:")}
8218
8620
 
8219
8621
  // src/commands/model.ts
8220
8622
  init_cjs_shims();
8623
+ var LoadingIndicator = class {
8624
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8625
+ frameIndex = 0;
8626
+ interval = null;
8627
+ message;
8628
+ constructor(message) {
8629
+ this.message = message;
8630
+ }
8631
+ start() {
8632
+ process.stdout.write("\x1B[?25l");
8633
+ this.interval = setInterval(() => {
8634
+ const frame = this.frames[this.frameIndex];
8635
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
8636
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
8637
+ }, 80);
8638
+ }
8639
+ stop(finalMessage) {
8640
+ if (this.interval) {
8641
+ clearInterval(this.interval);
8642
+ this.interval = null;
8643
+ }
8644
+ process.stdout.write("\x1B[?25h");
8645
+ if (finalMessage) {
8646
+ process.stdout.write(`\r${finalMessage}
8647
+ `);
8648
+ } else {
8649
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
8650
+ }
8651
+ }
8652
+ };
8221
8653
  async function handleModel(args, ctx) {
8222
8654
  const subCommand = args[0];
8223
8655
  const configManager = ctx.configManager;
@@ -8317,9 +8749,12 @@ async function verifyCurrentModel(configManager, modelService) {
8317
8749
  output: chalk9__default.default.red(`\u2717 \u672A\u77E5\u6A21\u578B: ${currentModel}`)
8318
8750
  };
8319
8751
  }
8752
+ const loader = new LoadingIndicator(`\u9A8C\u8BC1 ${modelInfo.name} API Key`);
8753
+ loader.start();
8320
8754
  try {
8321
8755
  const adapter = createAdapter(modelInfo.provider);
8322
8756
  const isValid = await adapter.validateApiKey(apiKey);
8757
+ loader.stop();
8323
8758
  if (isValid) {
8324
8759
  return {
8325
8760
  output: chalk9__default.default.green(`\u2713 API Key \u9A8C\u8BC1\u6210\u529F
@@ -8331,6 +8766,7 @@ async function verifyCurrentModel(configManager, modelService) {
8331
8766
  };
8332
8767
  }
8333
8768
  } catch (error) {
8769
+ loader.stop();
8334
8770
  return {
8335
8771
  output: chalk9__default.default.red(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${error.message}`)
8336
8772
  };
@@ -9454,9 +9890,9 @@ async function handleFileReference(filePath, ctx) {
9454
9890
  const cwd = ctx.options.workingDirectory;
9455
9891
  const absolutePath = path4__namespace.isAbsolute(filePath) ? filePath : path4__namespace.join(cwd, filePath);
9456
9892
  try {
9457
- const stats = await fs4__namespace.stat(absolutePath);
9893
+ const stats = await fs9__namespace.stat(absolutePath);
9458
9894
  if (stats.isDirectory()) {
9459
- const files = await fs4__namespace.readdir(absolutePath);
9895
+ const files = await fs9__namespace.readdir(absolutePath);
9460
9896
  return {
9461
9897
  output: chalk9__default.default.cyan(`\u{1F4C1} ${filePath}/`) + chalk9__default.default.gray(`
9462
9898
  ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.default.gray(`
@@ -9464,7 +9900,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.defaul
9464
9900
  contextUsed: 0
9465
9901
  };
9466
9902
  }
9467
- const content = await fs4__namespace.readFile(absolutePath, "utf-8");
9903
+ const content = await fs9__namespace.readFile(absolutePath, "utf-8");
9468
9904
  const lines = content.split("\n");
9469
9905
  ctx.contextManager.addMessage({
9470
9906
  role: "user",
@@ -9542,6 +9978,36 @@ async function executeShell(command, ctx) {
9542
9978
  // src/commands/natural.ts
9543
9979
  init_cjs_shims();
9544
9980
  init_new();
9981
+ var LoadingIndicator3 = class {
9982
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
9983
+ frameIndex = 0;
9984
+ interval = null;
9985
+ message;
9986
+ constructor(message) {
9987
+ this.message = message;
9988
+ }
9989
+ start() {
9990
+ process.stdout.write("\x1B[?25l");
9991
+ this.interval = setInterval(() => {
9992
+ const frame = this.frames[this.frameIndex];
9993
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
9994
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
9995
+ }, 80);
9996
+ }
9997
+ stop(finalMessage) {
9998
+ if (this.interval) {
9999
+ clearInterval(this.interval);
10000
+ this.interval = null;
10001
+ }
10002
+ process.stdout.write("\x1B[?25h");
10003
+ if (finalMessage) {
10004
+ process.stdout.write(`\r${finalMessage}
10005
+ `);
10006
+ } else {
10007
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
10008
+ }
10009
+ }
10010
+ };
9545
10011
  async function handleNaturalLanguage(input, ctx) {
9546
10012
  const trimmedInput = input.trim();
9547
10013
  const session = getActiveSession();
@@ -9561,6 +10027,8 @@ async function handleNaturalLanguage(input, ctx) {
9561
10027
  contextUsed: 0
9562
10028
  };
9563
10029
  }
10030
+ const loader = new LoadingIndicator3("AI \u601D\u8003\u4E2D");
10031
+ loader.start();
9564
10032
  try {
9565
10033
  const response = await ctx.modelService.sendMessage(
9566
10034
  [
@@ -9578,6 +10046,7 @@ async function handleNaturalLanguage(input, ctx) {
9578
10046
  maxTokens: 2e3
9579
10047
  }
9580
10048
  );
10049
+ loader.stop();
9581
10050
  ctx.contextManager.addMessage({
9582
10051
  role: "user",
9583
10052
  content: trimmedInput
@@ -9591,6 +10060,7 @@ async function handleNaturalLanguage(input, ctx) {
9591
10060
  contextUsed: response.usage?.totalTokens || 0
9592
10061
  };
9593
10062
  } catch (error) {
10063
+ loader.stop();
9594
10064
  const errorMessage = error.message;
9595
10065
  if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
9596
10066
  return {
@@ -9859,7 +10329,7 @@ var Completer = class {
9859
10329
  prefix = filePath.slice(lastSlash + 1);
9860
10330
  }
9861
10331
  try {
9862
- const entries = await fs4__namespace.readdir(dirPath, { withFileTypes: true });
10332
+ const entries = await fs9__namespace.readdir(dirPath, { withFileTypes: true });
9863
10333
  const matches = [];
9864
10334
  for (const entry of entries) {
9865
10335
  if (entry.name.startsWith(prefix)) {