@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/cli/index.js CHANGED
@@ -244,9 +244,10 @@ var init_base = __esm({
244
244
  }
245
245
  /**
246
246
  * 获取超时时间
247
+ * 默认 5 分钟,对于复杂的代码生成任务足够
247
248
  */
248
249
  getTimeout() {
249
- return this.config?.timeout || 6e4;
250
+ return this.config?.timeout || 3e5;
250
251
  }
251
252
  /**
252
253
  * 获取重试次数
@@ -1057,9 +1058,12 @@ async function verifyCurrentModel(configManager, modelService) {
1057
1058
  output: chalk9__default.default.red(`\u2717 \u672A\u77E5\u6A21\u578B: ${currentModel}`)
1058
1059
  };
1059
1060
  }
1061
+ const loader = new LoadingIndicator(`\u9A8C\u8BC1 ${modelInfo.name} API Key`);
1062
+ loader.start();
1060
1063
  try {
1061
1064
  const adapter = createAdapter(modelInfo.provider);
1062
1065
  const isValid = await adapter.validateApiKey(apiKey);
1066
+ loader.stop();
1063
1067
  if (isValid) {
1064
1068
  return {
1065
1069
  output: chalk9__default.default.green(`\u2713 API Key \u9A8C\u8BC1\u6210\u529F
@@ -1071,6 +1075,7 @@ async function verifyCurrentModel(configManager, modelService) {
1071
1075
  };
1072
1076
  }
1073
1077
  } catch (error) {
1078
+ loader.stop();
1074
1079
  return {
1075
1080
  output: chalk9__default.default.red(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${error.message}`)
1076
1081
  };
@@ -1192,12 +1197,42 @@ function createSpinner(message) {
1192
1197
  stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
1193
1198
  };
1194
1199
  }
1195
- var model_default;
1200
+ var LoadingIndicator, model_default;
1196
1201
  var init_model2 = __esm({
1197
1202
  "src/commands/model.ts"() {
1198
1203
  init_cjs_shims();
1199
1204
  init_adapters();
1200
1205
  init_model();
1206
+ LoadingIndicator = class {
1207
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1208
+ frameIndex = 0;
1209
+ interval = null;
1210
+ message;
1211
+ constructor(message) {
1212
+ this.message = message;
1213
+ }
1214
+ start() {
1215
+ process.stdout.write("\x1B[?25l");
1216
+ this.interval = setInterval(() => {
1217
+ const frame = this.frames[this.frameIndex];
1218
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
1219
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
1220
+ }, 80);
1221
+ }
1222
+ stop(finalMessage) {
1223
+ if (this.interval) {
1224
+ clearInterval(this.interval);
1225
+ this.interval = null;
1226
+ }
1227
+ process.stdout.write("\x1B[?25h");
1228
+ if (finalMessage) {
1229
+ process.stdout.write(`\r${finalMessage}
1230
+ `);
1231
+ } else {
1232
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
1233
+ }
1234
+ }
1235
+ };
1201
1236
  model_default = selectModel;
1202
1237
  }
1203
1238
  });
@@ -1459,7 +1494,10 @@ async function executeWorkflow(ctx) {
1459
1494
  if (activeSession.phase === "context") {
1460
1495
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
1461
1496
  lines.push("");
1497
+ const loader = new LoadingIndicator2("\u8BFB\u53D6\u9879\u76EE\u914D\u7F6E");
1498
+ loader.start();
1462
1499
  activeSession.context = await readProjectContext(ctx.options.workingDirectory);
1500
+ loader.stop();
1463
1501
  lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
1464
1502
  lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
1465
1503
  lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
@@ -1549,7 +1587,7 @@ ${resource.analysis}`;
1549
1587
  lines.push("");
1550
1588
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
1551
1589
  lines.push("");
1552
- const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
1590
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
1553
1591
  loader.start();
1554
1592
  try {
1555
1593
  activeSession.bddScenarios = await generateBDDScenariosWithAI(
@@ -1584,7 +1622,7 @@ ${resource.analysis}`;
1584
1622
  lines.push("");
1585
1623
  lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
1586
1624
  lines.push("");
1587
- const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1625
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
1588
1626
  loader.start();
1589
1627
  try {
1590
1628
  activeSession.specItems = await generateSpecItemsWithAI(
@@ -1753,31 +1791,85 @@ async function handleWorkflowInput(input, ctx) {
1753
1791
  activeSession.phase = "analysis";
1754
1792
  return executeWorkflow(ctx);
1755
1793
  }
1756
- if (activeSession.phase === "spec") {
1794
+ if (activeSession.phase === "spec" || activeSession.specFeedbackState === "waiting_confirm") {
1757
1795
  if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
1796
+ activeSession.specFeedbackState = void 0;
1758
1797
  activeSession.phase = "tdd";
1759
1798
  return executeWorkflow(ctx);
1760
1799
  }
1761
- if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
1762
- activeSession.bddScenarios = generateBDDScenarios(
1763
- activeSession.refinedRequirement,
1764
- activeSession.context,
1765
- activeSession.clarificationQuestions,
1766
- activeSession.referenceResources
1767
- );
1768
- activeSession.specItems = generateSpecItems(
1769
- activeSession.refinedRequirement,
1770
- activeSession.context,
1771
- activeSession.bddScenarios,
1772
- activeSession.clarificationQuestions,
1773
- activeSession.referenceResources
1774
- );
1800
+ if (trimmed === "n" || trimmed === "no" || trimmed === "\u4E0D\u6EE1\u610F" || trimmed === "\u91CD\u65B0") {
1801
+ activeSession.specFeedbackState = "collecting_feedback";
1802
+ if (!activeSession.specFeedbacks) {
1803
+ activeSession.specFeedbacks = [];
1804
+ }
1805
+ return {
1806
+ 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')
1807
+ };
1808
+ }
1809
+ }
1810
+ if (activeSession.specFeedbackState === "collecting_feedback") {
1811
+ if (trimmed === "" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
1812
+ if (!activeSession.specFeedbacks || activeSession.specFeedbacks.length === 0) {
1813
+ return {
1814
+ 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')
1815
+ };
1816
+ }
1817
+ activeSession.specFeedbackState = "feedback_received";
1818
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210\u89C4\u683C");
1819
+ loader.start();
1820
+ try {
1821
+ activeSession.specItems = await generateSpecItemsWithFeedback(
1822
+ activeSession.refinedRequirement,
1823
+ activeSession.context,
1824
+ activeSession.bddScenarios,
1825
+ activeSession.clarificationQuestions,
1826
+ activeSession.referenceResources,
1827
+ activeSession.specFeedbacks,
1828
+ ctx
1829
+ );
1830
+ loader.stop(chalk9__default.default.green(" \u2713 \u89C4\u683C\u5DF2\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210"));
1831
+ } catch (error) {
1832
+ loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u65B9\u6CD5\u91CD\u65B0\u751F\u6210"));
1833
+ activeSession.specItems = generateSpecItems(
1834
+ activeSession.refinedRequirement,
1835
+ activeSession.context,
1836
+ activeSession.bddScenarios,
1837
+ activeSession.clarificationQuestions,
1838
+ activeSession.referenceResources
1839
+ );
1840
+ }
1775
1841
  const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
1842
+ activeSession.specFeedbackState = "waiting_confirm";
1843
+ const lines = [];
1844
+ lines.push(chalk9__default.default.cyan("\n\u2501\u2501\u2501 \u66F4\u65B0\u540E\u7684\u4EFB\u52A1\u6982\u89C8 \u2501\u2501\u2501"));
1845
+ for (const item of activeSession.specItems.slice(0, 8)) {
1846
+ const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
1847
+ lines.push(chalk9__default.default.gray(` ${icon} [${item.id}] ${item.title}`));
1848
+ }
1849
+ if (activeSession.specItems.length > 8) {
1850
+ lines.push(chalk9__default.default.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
1851
+ }
1776
1852
  return {
1777
- output: chalk9__default.default.cyan("\u{1F504} \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210") + chalk9__default.default.gray(`
1778
- \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")
1853
+ output: lines.join("\n") + chalk9__default.default.green(`
1854
+
1855
+ \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u66F4\u65B0`) + chalk9__default.default.gray(`
1856
+ \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")
1779
1857
  };
1780
1858
  }
1859
+ if (trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
1860
+ activeSession.specFeedbackState = "waiting_confirm";
1861
+ activeSession.specFeedbacks = [];
1862
+ return {
1863
+ 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")
1864
+ };
1865
+ }
1866
+ if (!activeSession.specFeedbacks) {
1867
+ activeSession.specFeedbacks = [];
1868
+ }
1869
+ activeSession.specFeedbacks.push(trimmed);
1870
+ return {
1871
+ 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')
1872
+ };
1781
1873
  }
1782
1874
  if (activeSession.phase === "develop") {
1783
1875
  if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
@@ -1929,107 +2021,115 @@ function getCategoryLabel(category) {
1929
2021
  async function executeDevelopment(ctx, session) {
1930
2022
  const workingDir = ctx.options.workingDirectory;
1931
2023
  const files = [];
1932
- const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
1933
- loader.start();
1934
- try {
1935
- const systemPrompt = buildDevelopmentPrompt(session);
1936
- const messages = [
1937
- {
1938
- role: "system",
1939
- 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
2024
+ console.log("");
2025
+ for (let i = 0; i < session.specItems.length; i++) {
2026
+ const item = session.specItems[i];
2027
+ const prefix = `[${i + 1}/${session.specItems.length}]`;
2028
+ if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
2029
+ continue;
2030
+ }
2031
+ const loader = new LoadingIndicator2(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
2032
+ loader.start();
2033
+ try {
2034
+ const taskPrompt = buildTaskPrompt(session, item, i);
2035
+ const messages = [
2036
+ {
2037
+ role: "system",
2038
+ 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
1940
2039
 
1941
2040
  \u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
1942
- 1. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
1943
- 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
1944
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808\u548C\u6846\u67B6
1945
-
1946
- \u4EE3\u7801\u8981\u6C42\uFF1A
1947
- 1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
1948
- 2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
1949
- 3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
1950
- 4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
1951
- 5. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
2041
+ 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
2042
+ 2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
2043
+ 3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
2044
+ 4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
1952
2045
 
1953
2046
  \u9879\u76EE\u4FE1\u606F\uFF1A
1954
2047
  - \u540D\u79F0: ${session.context?.name}
1955
2048
  - \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
1956
2049
  - \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
1957
2050
 
1958
- ${session.context?.devStandards ? `\u3010\u5FC5\u987B\u9075\u5FAA\u7684\u5F00\u53D1\u89C4\u8303\u3011
1959
- ${session.context.devStandards.slice(0, 2500)}` : ""}`
1960
- },
1961
- {
1962
- role: "user",
1963
- content: systemPrompt
2051
+ ${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
2052
+ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
2053
+ },
2054
+ {
2055
+ role: "user",
2056
+ content: taskPrompt
2057
+ }
2058
+ ];
2059
+ const response = await ctx.modelService.sendMessage(messages, {
2060
+ temperature: 0.3,
2061
+ maxTokens: 4e3,
2062
+ // 单个任务减少 token
2063
+ agent: "frontend-dev",
2064
+ timeout: 3e5
2065
+ // 5分钟超时,确保复杂代码有足够时间
2066
+ });
2067
+ const codeBlocks = parseCodeBlocks(response.content);
2068
+ if (codeBlocks.length > 0) {
2069
+ for (const block of codeBlocks) {
2070
+ const filePath = path5__namespace.join(workingDir, block.filename);
2071
+ const dir = path5__namespace.dirname(filePath);
2072
+ await fs5__namespace.mkdir(dir, { recursive: true });
2073
+ await fs5__namespace.writeFile(filePath, block.code, "utf-8");
2074
+ files.push(block.filename);
2075
+ }
2076
+ loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
2077
+ } else {
2078
+ const implDir = path5__namespace.join(workingDir, "src");
2079
+ await fs5__namespace.mkdir(implDir, { recursive: true });
2080
+ const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
2081
+ const filePath = path5__namespace.join(implDir, fileName);
2082
+ await fs5__namespace.writeFile(filePath, `// TODO: ${item.title}
2083
+ // ${item.description}
2084
+ `, "utf-8");
2085
+ files.push(`src/${fileName}`);
2086
+ loader.stop(chalk9__default.default.yellow(`${prefix} \u26A0 \u751F\u6210\u57FA\u7840\u6A21\u677F: ${item.title.slice(0, 20)}`));
2087
+ }
2088
+ if (i < session.specItems.length - 1) {
2089
+ await new Promise((resolve5) => setTimeout(resolve5, 500));
1964
2090
  }
1965
- ];
1966
- loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
1967
- const response = await ctx.modelService.sendMessage(messages, {
1968
- temperature: 0.3,
1969
- maxTokens: 8e3,
1970
- agent: "frontend-dev",
1971
- timeout: 18e4
1972
- // 3 分钟超时
1973
- });
1974
- loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
1975
- const codeBlocks = parseCodeBlocks(response.content);
1976
- for (const block of codeBlocks) {
1977
- const filePath = path5__namespace.join(workingDir, block.filename);
1978
- const dir = path5__namespace.dirname(filePath);
1979
- await fs5__namespace.mkdir(dir, { recursive: true });
1980
- await fs5__namespace.writeFile(filePath, block.code, "utf-8");
1981
- files.push(block.filename);
1982
- }
1983
- if (files.length === 0) {
1984
- const implDir = path5__namespace.join(workingDir, "src", "features");
1985
- await fs5__namespace.mkdir(implDir, { recursive: true });
1986
- const featureName = session.specItems[0]?.title || "feature";
1987
- const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
1988
- const filePath = path5__namespace.join(implDir, fileName);
1989
- const stubCode = `/**
1990
- * ${session.requirement}
1991
- *
1992
- * TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
1993
- */
1994
-
1995
- export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
1996
- // TODO: \u5B9E\u73B0\u529F\u80FD
1997
- console.log('${featureName} - \u5F85\u5B9E\u73B0');
1998
- }
1999
- `;
2000
- await fs5__namespace.writeFile(filePath, stubCode, "utf-8");
2001
- files.push(`src/features/${fileName}`);
2091
+ } catch (error) {
2092
+ loader.stop(chalk9__default.default.red(`${prefix} \u2717 \u5931\u8D25: ${error.message.slice(0, 40)}`));
2002
2093
  }
2003
- loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
2094
+ }
2095
+ if (files.length > 0) {
2004
2096
  return { success: true, files };
2005
- } catch (error) {
2006
- loader.stop();
2097
+ } else {
2007
2098
  return {
2008
2099
  success: false,
2009
2100
  files: [],
2010
- error: error.message
2101
+ error: "\u6240\u6709\u4EFB\u52A1\u4EE3\u7801\u751F\u6210\u5931\u8D25"
2011
2102
  };
2012
2103
  }
2013
2104
  }
2014
- function buildDevelopmentPrompt(session) {
2105
+ function buildTaskPrompt(session, item, index) {
2015
2106
  const lines = [];
2016
- lines.push("## \u9700\u6C42\u63CF\u8FF0");
2017
- lines.push(session.refinedRequirement);
2018
- lines.push("");
2019
- lines.push("## BDD \u573A\u666F");
2020
- for (const scenario of session.bddScenarios) {
2021
- lines.push(`### ${scenario.feature}`);
2022
- for (const s of scenario.scenarios) {
2023
- lines.push(`- ${s.name}`);
2107
+ lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
2108
+ lines.push(`**\u6807\u9898**: ${item.title}`);
2109
+ lines.push(`**\u63CF\u8FF0**: ${item.description}`);
2110
+ lines.push(`**\u4F18\u5148\u7EA7**: ${item.priority}`);
2111
+ if (item.tests && item.tests.length > 0) {
2112
+ lines.push(`**\u9A8C\u6536\u6807\u51C6**:`);
2113
+ for (const t of item.tests) {
2114
+ lines.push(`- ${t}`);
2024
2115
  }
2025
2116
  }
2026
2117
  lines.push("");
2027
- lines.push("## \u4EFB\u52A1\u5217\u8868");
2028
- for (const item of session.specItems) {
2029
- lines.push(`- [${item.id}] ${item.title}: ${item.description}`);
2118
+ lines.push(`## \u6574\u4F53\u9700\u6C42\u80CC\u666F`);
2119
+ lines.push(session.requirement);
2120
+ const relatedScenario = session.bddScenarios.find(
2121
+ (s) => item.title.includes(s.feature) || s.feature.includes(item.title)
2122
+ );
2123
+ if (relatedScenario) {
2124
+ lines.push("");
2125
+ lines.push(`## \u76F8\u5173 BDD \u573A\u666F`);
2126
+ lines.push(`Feature: ${relatedScenario.feature}`);
2127
+ for (const s of relatedScenario.scenarios.slice(0, 2)) {
2128
+ lines.push(`- ${s.name}`);
2129
+ }
2030
2130
  }
2031
2131
  lines.push("");
2032
- lines.push("\u8BF7\u6839\u636E\u4EE5\u4E0A\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002");
2132
+ 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");
2033
2133
  return lines.join("\n");
2034
2134
  }
2035
2135
  function parseCodeBlocks(content) {
@@ -2050,6 +2150,8 @@ async function executeReview(ctx, session) {
2050
2150
  const workingDir = ctx.options.workingDirectory;
2051
2151
  const issues = [];
2052
2152
  const suggestions = [];
2153
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5BA1\u6838\u4EE3\u7801");
2154
+ loader.start();
2053
2155
  try {
2054
2156
  const codeContents = [];
2055
2157
  for (const file of session.implFiles) {
@@ -2112,7 +2214,9 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
2112
2214
  const response = await ctx.modelService.sendMessage(messages, {
2113
2215
  temperature: 0.2,
2114
2216
  maxTokens: 2e3,
2115
- agent: "code-reviewer"
2217
+ agent: "code-reviewer",
2218
+ timeout: 18e4
2219
+ // 3分钟超时
2116
2220
  });
2117
2221
  const result = response.content;
2118
2222
  const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
@@ -2134,8 +2238,10 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
2134
2238
  const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
2135
2239
  suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
2136
2240
  }
2241
+ loader.stop(passed ? chalk9__default.default.green(" \u2713 \u5BA1\u6838\u901A\u8FC7") : chalk9__default.default.yellow(" \u26A0 \u53D1\u73B0\u95EE\u9898"));
2137
2242
  return { passed, issues, suggestions };
2138
2243
  } catch (error) {
2244
+ loader.stop(chalk9__default.default.red(" \u2717 \u5BA1\u6838\u51FA\u9519"));
2139
2245
  suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
2140
2246
  return { passed: true, issues, suggestions };
2141
2247
  }
@@ -2341,7 +2447,8 @@ ${r.analysis}`).join("\n\n") : "\u65E0"}
2341
2447
  ], {
2342
2448
  temperature: 0.3,
2343
2449
  maxTokens: 4e3,
2344
- timeout: 12e4
2450
+ timeout: 3e5
2451
+ // 5分钟超时
2345
2452
  });
2346
2453
  try {
2347
2454
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
@@ -2549,7 +2656,7 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
2549
2656
  - \u96C6\u6210\u6D4B\u8BD5
2550
2657
 
2551
2658
  \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
2552
- const loader = new LoadingIndicator("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
2659
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
2553
2660
  loader.start();
2554
2661
  try {
2555
2662
  const response = await ctx.modelService.sendMessage([
@@ -2557,25 +2664,131 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
2557
2664
  ], {
2558
2665
  temperature: 0.3,
2559
2666
  maxTokens: 4e3,
2560
- timeout: 12e4
2667
+ timeout: 3e5
2668
+ // 5分钟超时
2561
2669
  });
2562
2670
  const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
2563
2671
  if (jsonMatch) {
2564
- const parsed = JSON.parse(jsonMatch[1].trim());
2565
- loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
2566
- return parsed.map((item) => ({
2567
- id: item.id || `T${String(parsed.indexOf(item) + 1).padStart(3, "0")}`,
2568
- title: item.title,
2569
- description: item.description,
2570
- priority: item.priority || "medium",
2571
- files: [],
2572
- tests: item.acceptanceCriteria || []
2573
- }));
2574
- }
2575
- loader.stop(chalk9__default.default.yellow(" \u26A0 \u89E3\u6790\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
2672
+ try {
2673
+ const parsed = JSON.parse(jsonMatch[1].trim());
2674
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
2675
+ return parsed.map((item, index) => ({
2676
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
2677
+ title: item.title,
2678
+ description: item.description,
2679
+ priority: item.priority || "medium",
2680
+ files: [],
2681
+ tests: item.acceptanceCriteria || []
2682
+ }));
2683
+ } catch (parseError) {
2684
+ loader.stop(chalk9__default.default.yellow(` \u26A0 JSON \u89E3\u6790\u5931\u8D25: ${parseError.message.slice(0, 50)}`));
2685
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
2686
+ }
2687
+ }
2688
+ try {
2689
+ const parsed = JSON.parse(response.content);
2690
+ if (Array.isArray(parsed)) {
2691
+ loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u62C6\u5206 ${parsed.length} \u4E2A\u4EFB\u52A1`));
2692
+ return parsed.map((item, index) => ({
2693
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
2694
+ title: item.title,
2695
+ description: item.description,
2696
+ priority: item.priority || "medium",
2697
+ files: [],
2698
+ tests: item.acceptanceCriteria || []
2699
+ }));
2700
+ }
2701
+ } catch {
2702
+ }
2703
+ loader.stop(chalk9__default.default.yellow(" \u26A0 \u672A\u80FD\u89E3\u6790 AI \u54CD\u5E94\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
2576
2704
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
2577
2705
  } catch (error) {
2578
- loader.stop(chalk9__default.default.yellow(" \u26A0 AI \u62C6\u5206\u5931\u8D25\uFF0C\u4F7F\u7528\u57FA\u7840\u62C6\u5206"));
2706
+ const errMsg = error.message.slice(0, 80);
2707
+ loader.stop(chalk9__default.default.yellow(` \u26A0 AI \u8C03\u7528\u5931\u8D25: ${errMsg}`));
2708
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
2709
+ }
2710
+ }
2711
+ async function generateSpecItemsWithFeedback(requirement, context, bddScenarios, questions, references, feedbacks, ctx) {
2712
+ 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
2713
+
2714
+ ## \u9700\u6C42\u63CF\u8FF0
2715
+ ${requirement}
2716
+
2717
+ ## \u9879\u76EE\u4E0A\u4E0B\u6587
2718
+ - \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
2719
+ - \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
2720
+ ${context.devStandards ? `
2721
+ ## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
2722
+ ${context.devStandards.slice(0, 2e3)}
2723
+ ` : ""}
2724
+
2725
+ ## \u4E4B\u524D\u7684 BDD \u573A\u666F
2726
+ ${bddScenarios.map((s) => `- Feature: ${s.feature}`).join("\n")}
2727
+
2728
+ ## \u7528\u6237\u53CD\u9988\uFF08\u5FC5\u987B\u89E3\u51B3\u8FD9\u4E9B\u95EE\u9898\uFF09
2729
+ ${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
2730
+
2731
+ ## \u8981\u6C42
2732
+ 1. **\u5FC5\u987B\u89E3\u51B3\u7528\u6237\u53CD\u9988\u4E2D\u7684\u6240\u6709\u95EE\u9898**
2733
+ 2. \u5982\u679C\u7528\u6237\u6307\u51FA\u9057\u6F0F\u7684\u529F\u80FD\u70B9\uFF0C\u8BF7\u8865\u5145\u76F8\u5173\u4EFB\u52A1
2734
+ 3. \u5982\u679C\u7528\u6237\u6307\u51FA\u62C6\u5206\u4E0D\u5408\u7406\uFF0C\u8BF7\u91CD\u65B0\u8C03\u6574\u7C92\u5EA6
2735
+ 4. \u5982\u679C\u7528\u6237\u9700\u8981\u66F4\u591A\u7EC6\u8282\uFF0C\u8BF7\u62C6\u5206\u5F97\u66F4\u7EC6\u81F4
2736
+ 5. \u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\u5355\u4E00\u804C\u8D23\u30012-4\u5C0F\u65F6\u53EF\u5B8C\u6210
2737
+
2738
+ ## \u8F93\u51FA\u683C\u5F0F (JSON)
2739
+ \`\`\`json
2740
+ [
2741
+ {
2742
+ "id": "T001",
2743
+ "title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
2744
+ "description": "\u8BE6\u7EC6\u63CF\u8FF0",
2745
+ "priority": "high",
2746
+ "acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
2747
+ }
2748
+ ]
2749
+ \`\`\`
2750
+
2751
+ \u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u786E\u4FDD\u89E3\u51B3\u4E86\u7528\u6237\u7684\u6240\u6709\u53CD\u9988\u3002`;
2752
+ try {
2753
+ const response = await ctx.modelService.sendMessage([
2754
+ { role: "user", content: prompt2 }
2755
+ ], {
2756
+ temperature: 0.3,
2757
+ maxTokens: 4e3,
2758
+ timeout: 3e5
2759
+ // 5分钟超时
2760
+ });
2761
+ const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
2762
+ if (jsonMatch) {
2763
+ try {
2764
+ const parsed = JSON.parse(jsonMatch[1].trim());
2765
+ return parsed.map((item, index) => ({
2766
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
2767
+ title: item.title,
2768
+ description: item.description,
2769
+ priority: item.priority || "medium",
2770
+ files: [],
2771
+ tests: item.acceptanceCriteria || []
2772
+ }));
2773
+ } catch {
2774
+ }
2775
+ }
2776
+ try {
2777
+ const parsed = JSON.parse(response.content);
2778
+ if (Array.isArray(parsed)) {
2779
+ return parsed.map((item, index) => ({
2780
+ id: item.id || `T${String(index + 1).padStart(3, "0")}`,
2781
+ title: item.title,
2782
+ description: item.description,
2783
+ priority: item.priority || "medium",
2784
+ files: [],
2785
+ tests: item.acceptanceCriteria || []
2786
+ }));
2787
+ }
2788
+ } catch {
2789
+ }
2790
+ return generateSpecItems(requirement, context, bddScenarios, questions, references);
2791
+ } catch {
2579
2792
  return generateSpecItems(requirement, context, bddScenarios, questions, references);
2580
2793
  }
2581
2794
  }
@@ -2645,6 +2858,16 @@ function formatSpecFile(session) {
2645
2858
  lines.push("");
2646
2859
  }
2647
2860
  }
2861
+ if (session.specFeedbacks && session.specFeedbacks.length > 0) {
2862
+ lines.push("## \u89C4\u683C\u8FED\u4EE3\u53CD\u9988");
2863
+ lines.push("");
2864
+ for (let i = 0; i < session.specFeedbacks.length; i++) {
2865
+ lines.push(`${i + 1}. ${session.specFeedbacks[i]}`);
2866
+ }
2867
+ lines.push("");
2868
+ lines.push("---");
2869
+ lines.push("");
2870
+ }
2648
2871
  lines.push("## \u4EFB\u52A1\u5217\u8868");
2649
2872
  lines.push("");
2650
2873
  for (const item of session.specItems) {
@@ -2665,7 +2888,7 @@ async function generateTests(workingDir, session, ctx) {
2665
2888
  for (const scenario of session.bddScenarios) {
2666
2889
  const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
2667
2890
  const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
2668
- const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
2891
+ const loader = new LoadingIndicator2(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
2669
2892
  loader.start();
2670
2893
  try {
2671
2894
  const content = await generateTestFileWithAI(scenario, session, ctx);
@@ -2720,7 +2943,9 @@ ${scenario.scenarios.map((s) => `
2720
2943
  { role: "user", content: prompt2 }
2721
2944
  ], {
2722
2945
  temperature: 0.3,
2723
- maxTokens: 4e3
2946
+ maxTokens: 4e3,
2947
+ timeout: 18e4
2948
+ // 3分钟超时
2724
2949
  });
2725
2950
  const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
2726
2951
  if (codeMatch) {
@@ -2819,7 +3044,10 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
2819
3044
  const type = detectResourceType(url);
2820
3045
  let content = "";
2821
3046
  let analysis = "";
3047
+ const loader = new LoadingIndicator2(`\u83B7\u53D6 ${url.slice(0, 40)}...`);
3048
+ loader.start();
2822
3049
  try {
3050
+ loader.update("\u6B63\u5728\u83B7\u53D6\u7F51\u9875\u5185\u5BB9");
2823
3051
  const response = await fetch(url, {
2824
3052
  headers: {
2825
3053
  "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
@@ -2830,11 +3058,14 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
2830
3058
  }
2831
3059
  content = await response.text();
2832
3060
  if (ctx.modelService.getCurrentModel()) {
3061
+ loader.update("\u6B63\u5728\u5206\u6790\u5185\u5BB9");
2833
3062
  analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
2834
3063
  } else {
2835
3064
  analysis = extractBasicInfo(content, type);
2836
3065
  }
3066
+ loader.stop();
2837
3067
  } catch (error) {
3068
+ loader.stop();
2838
3069
  throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
2839
3070
  }
2840
3071
  return { url, type, content: content.slice(0, 1e4), analysis };
@@ -2903,14 +3134,16 @@ ${content.slice(0, 8e3)}
2903
3134
  \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
2904
3135
  \u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
2905
3136
  `;
2906
- const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
3137
+ const loader = new LoadingIndicator2("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
2907
3138
  loader.start();
2908
3139
  try {
2909
3140
  const response = await ctx.modelService.sendMessage([
2910
3141
  { role: "user", content: prompt2 }
2911
3142
  ], {
2912
3143
  temperature: 0.3,
2913
- maxTokens: 4e3
3144
+ maxTokens: 4e3,
3145
+ timeout: 18e4
3146
+ // 3分钟超时
2914
3147
  });
2915
3148
  loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
2916
3149
  return response.content;
@@ -2957,11 +3190,11 @@ function getActiveSession() {
2957
3190
  function clearActiveSession() {
2958
3191
  activeSession = null;
2959
3192
  }
2960
- var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
3193
+ var LoadingIndicator2, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
2961
3194
  var init_new = __esm({
2962
3195
  "src/commands/new.ts"() {
2963
3196
  init_cjs_shims();
2964
- LoadingIndicator = class {
3197
+ LoadingIndicator2 = class {
2965
3198
  frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
2966
3199
  frameIndex = 0;
2967
3200
  interval = null;
@@ -7131,6 +7364,36 @@ async function executeShell(command, ctx) {
7131
7364
  // src/commands/natural.ts
7132
7365
  init_cjs_shims();
7133
7366
  init_new();
7367
+ var LoadingIndicator3 = class {
7368
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
7369
+ frameIndex = 0;
7370
+ interval = null;
7371
+ message;
7372
+ constructor(message) {
7373
+ this.message = message;
7374
+ }
7375
+ start() {
7376
+ process.stdout.write("\x1B[?25l");
7377
+ this.interval = setInterval(() => {
7378
+ const frame = this.frames[this.frameIndex];
7379
+ process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
7380
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
7381
+ }, 80);
7382
+ }
7383
+ stop(finalMessage) {
7384
+ if (this.interval) {
7385
+ clearInterval(this.interval);
7386
+ this.interval = null;
7387
+ }
7388
+ process.stdout.write("\x1B[?25h");
7389
+ if (finalMessage) {
7390
+ process.stdout.write(`\r${finalMessage}
7391
+ `);
7392
+ } else {
7393
+ process.stdout.write("\r" + " ".repeat(60) + "\r");
7394
+ }
7395
+ }
7396
+ };
7134
7397
  async function handleNaturalLanguage(input, ctx) {
7135
7398
  const trimmedInput = input.trim();
7136
7399
  const session = getActiveSession();
@@ -7150,6 +7413,8 @@ async function handleNaturalLanguage(input, ctx) {
7150
7413
  contextUsed: 0
7151
7414
  };
7152
7415
  }
7416
+ const loader = new LoadingIndicator3("AI \u601D\u8003\u4E2D");
7417
+ loader.start();
7153
7418
  try {
7154
7419
  const response = await ctx.modelService.sendMessage(
7155
7420
  [
@@ -7167,6 +7432,7 @@ async function handleNaturalLanguage(input, ctx) {
7167
7432
  maxTokens: 2e3
7168
7433
  }
7169
7434
  );
7435
+ loader.stop();
7170
7436
  ctx.contextManager.addMessage({
7171
7437
  role: "user",
7172
7438
  content: trimmedInput
@@ -7180,6 +7446,7 @@ async function handleNaturalLanguage(input, ctx) {
7180
7446
  contextUsed: response.usage?.totalTokens || 0
7181
7447
  };
7182
7448
  } catch (error) {
7449
+ loader.stop();
7183
7450
  const errorMessage = error.message;
7184
7451
  if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
7185
7452
  return {