qat-cli 0.3.3 → 0.3.5

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.js CHANGED
@@ -8,10 +8,10 @@ import {
8
8
 
9
9
  // src/cli.ts
10
10
  import { Command } from "commander";
11
- import chalk12 from "chalk";
11
+ import chalk13 from "chalk";
12
12
 
13
13
  // src/commands/init.ts
14
- import chalk2 from "chalk";
14
+ import chalk3 from "chalk";
15
15
  import inquirer from "inquirer";
16
16
  import ora from "ora";
17
17
  import fs6 from "fs";
@@ -524,71 +524,6 @@ function validateConfig(config) {
524
524
  }
525
525
  return merged;
526
526
  }
527
- function generateConfigFile(overrides = {}) {
528
- const config = validateConfig(overrides);
529
- return `// @ts-check
530
- /**
531
- * QAT \u914D\u7F6E\u6587\u4EF6 - Quick Auto Testing
532
- * \u4FEE\u6539\u540E\u65E0\u9700\u91CD\u542F\uFF0C\u4E0B\u6B21\u8FD0\u884C qat \u547D\u4EE4\u65F6\u81EA\u52A8\u751F\u6548
533
- *
534
- * AI \u6A21\u578B\u914D\u7F6E\u8BF7\u8FD0\u884C: qat change
535
- * AI \u72B6\u6001\u67E5\u770B\u8BF7\u8FD0\u884C: qat status
536
- */
537
- import { defineConfig } from 'qat-cli';
538
-
539
- export default defineConfig({
540
- project: {
541
- framework: '${config.project.framework}',
542
- vite: ${config.project.vite ?? true},
543
- srcDir: '${config.project.srcDir}',
544
- },
545
- vitest: {
546
- enabled: ${config.vitest.enabled},
547
- coverage: ${config.vitest.coverage},
548
- globals: ${config.vitest.globals},
549
- environment: '${config.vitest.environment}',
550
- },
551
- playwright: {
552
- enabled: ${config.playwright.enabled},
553
- browsers: [${config.playwright.browsers.map((b) => `'${b}'`).join(", ")}],
554
- baseURL: '${config.playwright.baseURL}',
555
- screenshot: '${config.playwright.screenshot}',
556
- },
557
- visual: {
558
- enabled: ${config.visual.enabled},
559
- threshold: ${config.visual.threshold},
560
- baselineDir: '${config.visual.baselineDir}',
561
- diffDir: '${config.visual.diffDir}',
562
- },
563
- lighthouse: {
564
- enabled: ${config.lighthouse.enabled},
565
- urls: [${config.lighthouse.urls.map((u) => `'${u}'`).join(", ")}],
566
- runs: ${config.lighthouse.runs},
567
- thresholds: {${Object.entries(config.lighthouse.thresholds).map(([k, v]) => `
568
- ${k}: ${v},`).join("")}
569
- },
570
- },
571
- mock: {
572
- enabled: ${config.mock.enabled},
573
- port: ${config.mock.port},
574
- routesDir: '${config.mock.routesDir}',
575
- },
576
- report: {
577
- outputDir: '${config.report.outputDir}',
578
- open: ${config.report.open},
579
- },
580
- });
581
- `;
582
- }
583
- async function writeConfigFile(cwd, overrides = {}, force = false) {
584
- const configPath = path2.join(cwd, "qat.config.js");
585
- if (fs2.existsSync(configPath) && !force) {
586
- throw new Error(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: ${configPath}\uFF0C\u4F7F\u7528 --force \u8986\u76D6`);
587
- }
588
- const content = generateConfigFile(overrides);
589
- fs2.writeFileSync(configPath, content, "utf-8");
590
- return configPath;
591
- }
592
527
  function isFileNotFoundError(error) {
593
528
  if (error instanceof Error) {
594
529
  return error.message.includes("Cannot find") || error.message.includes("ENOENT") || error.message.includes("\u65E0\u6CD5\u52A0\u8F7D\u914D\u7F6E\u6587\u4EF6");
@@ -843,6 +778,15 @@ var NoopAIProvider = class {
843
778
  };
844
779
 
845
780
  // src/ai/openai-provider.ts
781
+ import chalk2 from "chalk";
782
+ function isDebug() {
783
+ return process.env.QAT_DEBUG === "true";
784
+ }
785
+ function debugLog(tag, ...args) {
786
+ if (!isDebug()) return;
787
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
788
+ console.error(chalk2.gray(`[DEBUG ${timestamp}] [${tag}]`), ...args);
789
+ }
846
790
  var OpenAICompatibleProvider = class {
847
791
  constructor(config) {
848
792
  this.capabilities = {
@@ -856,25 +800,20 @@ var OpenAICompatibleProvider = class {
856
800
  this.baseUrl = config.baseUrl || this.getDefaultBaseUrl(config.provider);
857
801
  }
858
802
  async generateTest(req) {
803
+ debugLog("GENERATE", `type=${req.type} target=${req.target}`);
859
804
  const systemPrompt = this.buildGenerateTestSystemPrompt(req);
860
805
  const userPrompt = this.buildGenerateTestUserPrompt(req);
861
806
  const content = await this.chat(systemPrompt, userPrompt);
862
- return this.parseGenerateTestResponse(content);
807
+ const result = this.parseGenerateTestResponse(content);
808
+ debugLog("GENERATE", `done code=${result.code.length}chars confidence=${result.confidence}`);
809
+ return result;
863
810
  }
864
811
  async analyzeResult(req) {
865
- const systemPrompt = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u6D4B\u8BD5\u5206\u6790\u4E13\u5BB6\u3002\u5206\u6790\u6D4B\u8BD5\u8FD0\u884C\u7ED3\u679C\uFF0C\u627E\u51FA\u95EE\u9898\u6839\u56E0\uFF0C\u7ED9\u51FA\u5177\u4F53\u53EF\u64CD\u4F5C\u7684\u6539\u8FDB\u5EFA\u8BAE\u3002
866
- \u8F93\u51FA\u683C\u5F0F\uFF1A
867
- 1. \u5206\u6790\u6458\u8981\uFF081-3\u53E5\u8BDD\uFF09
868
- 2. \u6539\u8FDB\u5EFA\u8BAE\u5217\u8868\uFF08\u6BCF\u6761\u5EFA\u8BAE\u5177\u4F53\u3001\u53EF\u64CD\u4F5C\uFF09`;
869
- const resultSummary = req.testResults.map((r) => {
870
- const failed = r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed"));
871
- return `\u7C7B\u578B: ${r.type}, \u72B6\u6001: ${r.status}, \u8017\u65F6: ${r.duration}ms, \u5931\u8D25\u7528\u4F8B: ${failed.length}`;
872
- }).join("\n");
873
- const errorDetails = req.errorLogs?.join("\n") || req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed" && t.error))).map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0\u9519\u8BEF\u8BE6\u60C5";
874
- const userPrompt = `\u6D4B\u8BD5\u7ED3\u679C:
875
- ${resultSummary}
876
-
877
- \u9519\u8BEF\u8BE6\u60C5:
812
+ const systemPrompt = `\u6D4B\u8BD5\u5206\u6790\u4E13\u5BB6\u3002\u627E\u95EE\u9898\u6839\u56E0\uFF0C\u7ED9\u53EF\u64CD\u4F5C\u5EFA\u8BAE\u3002
813
+ \u8F93\u51FA:1.\u6458\u8981(1-3\u53E5) 2.\u5EFA\u8BAE\u5217\u8868`;
814
+ const failed = req.testResults.flatMap((r) => r.suites.flatMap((s) => s.tests.filter((t) => t.status === "failed")));
815
+ const errorDetails = req.errorLogs?.join("\n") || failed.map((t) => `[${t.name}] ${t.error?.message}`).join("\n") || "\u65E0";
816
+ const userPrompt = `\u5931\u8D25:${failed.length}
878
817
  ${errorDetails}`;
879
818
  const content = await this.chat(systemPrompt, userPrompt);
880
819
  return {
@@ -884,23 +823,22 @@ ${errorDetails}`;
884
823
  };
885
824
  }
886
825
  async suggestFix(error) {
887
- const systemPrompt = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4EE3\u7801\u4FEE\u590D\u4E13\u5BB6\u3002\u6839\u636E\u6D4B\u8BD5\u9519\u8BEF\u4FE1\u606F\uFF0C\u7ED9\u51FA\u5177\u4F53\u7684\u4FEE\u590D\u5EFA\u8BAE\u3002
888
- \u6BCF\u6761\u5EFA\u8BAE\u5E94\u8BE5\u5305\u542B\uFF1A
889
- 1. \u95EE\u9898\u5B9A\u4F4D
890
- 2. \u4FEE\u590D\u65B9\u6848
891
- 3. \u793A\u4F8B\u4EE3\u7801\uFF08\u5982\u679C\u9002\u7528\uFF09`;
892
- const userPrompt = `\u9519\u8BEF\u4FE1\u606F: ${error.message}
893
- ${error.stack ? `\u5806\u6808: ${error.stack}` : ""}
894
- ${error.expected ? `\u671F\u671B\u503C: ${error.expected}` : ""}
895
- ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
826
+ const systemPrompt = `\u4EE3\u7801\u4FEE\u590D\u4E13\u5BB6\u3002\u7ED9\u51FA:1.\u95EE\u9898\u5B9A\u4F4D 2.\u4FEE\u590D\u65B9\u6848 3.\u793A\u4F8B\u4EE3\u7801`;
827
+ const userPrompt = `\u9519\u8BEF:${error.message}${error.stack ? `
828
+ \u5806\u6808:${error.stack}` : ""}${error.expected ? `
829
+ \u671F\u671B:${error.expected}` : ""}${error.actual ? `
830
+ \u5B9E\u9645:${error.actual}` : ""}`;
896
831
  const content = await this.chat(systemPrompt, userPrompt);
897
832
  return content.split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("\u2022") || l.trim().match(/^\d+\./)).map((l) => l.replace(/^[-•\d.]+\s*/, "").trim()).filter(Boolean);
898
833
  }
899
834
  async reviewTest(req) {
835
+ debugLog("REVIEW", `target=${req.target} type=${req.testType}`);
900
836
  const systemPrompt = this.buildReviewTestSystemPrompt(req);
901
837
  const userPrompt = this.buildReviewTestUserPrompt(req);
902
838
  const content = await this.chat(systemPrompt, userPrompt);
903
- return this.parseReviewTestResponse(content);
839
+ const result = this.parseReviewTestResponse(content);
840
+ debugLog("REVIEW", `approved=${result.approved} score=${result.score} feedback=${result.feedback}`);
841
+ return result;
904
842
  }
905
843
  // ─── 内部方法 ──────────────────────────────────────────────
906
844
  /**
@@ -964,8 +902,9 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
964
902
  return { ok: false, message: `\u8FDE\u63A5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`, latencyMs };
965
903
  }
966
904
  }
967
- async chat(systemPrompt, userPrompt) {
905
+ async chat(systemPrompt, userPrompt, retries = 2) {
968
906
  const url = `${this.baseUrl}/chat/completions`;
907
+ const useStream = isDebug();
969
908
  const body = {
970
909
  model: this.model,
971
910
  messages: [
@@ -975,101 +914,202 @@ ${error.actual ? `\u5B9E\u9645\u503C: ${error.actual}` : ""}`;
975
914
  temperature: 0.3,
976
915
  max_tokens: 4096
977
916
  };
917
+ if (useStream) {
918
+ body.stream = true;
919
+ }
978
920
  const headers = {
979
921
  "Content-Type": "application/json"
980
922
  };
981
923
  if (this.apiKey) {
982
924
  headers["Authorization"] = `Bearer ${this.apiKey}`;
983
925
  }
984
- const response = await fetch(url, {
985
- method: "POST",
986
- headers,
987
- body: JSON.stringify(body),
988
- signal: AbortSignal.timeout(6e4)
989
- // 60s timeout
990
- });
991
- if (!response.ok) {
992
- const text = await response.text().catch(() => "");
993
- throw new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 500)}`);
926
+ debugLog("REQUEST", `POST ${url}`);
927
+ debugLog("REQUEST", `model=${this.model} stream=${useStream}`);
928
+ debugLog("SYSTEM", systemPrompt.length > 500 ? `${systemPrompt.slice(0, 500)}...` : systemPrompt);
929
+ debugLog("USER", userPrompt.length > 1e3 ? `${userPrompt.slice(0, 1e3)}...` : userPrompt);
930
+ let lastError = null;
931
+ for (let attempt = 0; attempt <= retries; attempt++) {
932
+ try {
933
+ if (attempt > 0) {
934
+ debugLog("RETRY", `\u7B2C${attempt}\u6B21\u91CD\u8BD5...`);
935
+ }
936
+ const response = await fetch(url, {
937
+ method: "POST",
938
+ headers,
939
+ body: JSON.stringify(body),
940
+ signal: AbortSignal.timeout(12e4)
941
+ // 120s timeout
942
+ });
943
+ if (!response.ok) {
944
+ const text = await response.text().catch(() => "");
945
+ if ((response.status === 429 || response.status >= 500) && attempt < retries) {
946
+ const delay = Math.min(1e3 * Math.pow(2, attempt), 8e3);
947
+ debugLog("RETRY", `HTTP ${response.status}, ${delay}ms\u540E\u91CD\u8BD5`);
948
+ await new Promise((r) => setTimeout(r, delay));
949
+ lastError = new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 200)}`);
950
+ continue;
951
+ }
952
+ throw new Error(`AI API \u8BF7\u6C42\u5931\u8D25 (${response.status}): ${text.slice(0, 500)}`);
953
+ }
954
+ if (useStream && response.body) {
955
+ const content = await this.readStream(response.body);
956
+ debugLog("RESPONSE", content.length > 500 ? `${content.slice(0, 500)}...` : content);
957
+ return content;
958
+ }
959
+ const data = await response.json();
960
+ if (!data.choices?.[0]?.message?.content) {
961
+ throw new Error("AI API \u8FD4\u56DE\u7A7A\u54CD\u5E94");
962
+ }
963
+ debugLog("RESPONSE", `tokens: prompt=${data.usage?.prompt_tokens} completion=${data.usage?.completion_tokens} total=${data.usage?.total_tokens}`);
964
+ debugLog("RESPONSE", data.choices[0].message.content.length > 500 ? `${data.choices[0].message.content.slice(0, 500)}...` : data.choices[0].message.content);
965
+ return data.choices[0].message.content;
966
+ } catch (error) {
967
+ if (error instanceof Error && error.name === "TimeoutError" && attempt < retries) {
968
+ debugLog("TIMEOUT", `\u7B2C${attempt}\u6B21\u8D85\u65F6\uFF0C\u91CD\u8BD5\u4E2D...`);
969
+ lastError = error;
970
+ continue;
971
+ }
972
+ throw error;
973
+ }
994
974
  }
995
- const data = await response.json();
996
- if (!data.choices?.[0]?.message?.content) {
997
- throw new Error("AI API \u8FD4\u56DE\u7A7A\u54CD\u5E94");
975
+ throw lastError || new Error("AI API \u8BF7\u6C42\u5931\u8D25");
976
+ }
977
+ /**
978
+ * 读取 SSE 流式响应,实时输出内容
979
+ */
980
+ async readStream(body) {
981
+ const chunks = [];
982
+ const decoder = new TextDecoder();
983
+ const reader = body.getReader();
984
+ let buffer = "";
985
+ let lineCount = 0;
986
+ try {
987
+ while (true) {
988
+ const { done, value } = await reader.read();
989
+ if (done) break;
990
+ buffer += decoder.decode(value, { stream: true });
991
+ const lines = buffer.split("\n");
992
+ buffer = lines.pop() || "";
993
+ for (const line of lines) {
994
+ const trimmed = line.trim();
995
+ if (!trimmed || trimmed === "data: [DONE]") continue;
996
+ if (!trimmed.startsWith("data: ")) continue;
997
+ try {
998
+ const json = JSON.parse(trimmed.slice(6));
999
+ const delta = json.choices?.[0]?.delta?.content;
1000
+ if (delta) {
1001
+ chunks.push(delta);
1002
+ lineCount++;
1003
+ process.stderr.write(chalk2.gray(delta));
1004
+ if (lineCount % 20 === 0) {
1005
+ process.stderr.write("\n");
1006
+ }
1007
+ }
1008
+ if (json.choices?.[0]?.finish_reason === "stop") {
1009
+ debugLog("STREAM", "\u5B8C\u6210");
1010
+ }
1011
+ } catch {
1012
+ }
1013
+ }
1014
+ }
1015
+ } finally {
1016
+ reader.releaseLock();
1017
+ }
1018
+ if (chunks.length > 0) {
1019
+ process.stderr.write("\n");
1020
+ }
1021
+ return chunks.join("");
1022
+ }
1023
+ /**
1024
+ * 压缩源码:保留签名和关键逻辑,剔除注释、空行、样式块
1025
+ * 目标:在保留准确性的前提下减少 token 消耗
1026
+ * @param code 源码内容
1027
+ * @param maxLength 最大长度
1028
+ * @param importPathRewrites import 路径重写映射(原路径→正确路径)
1029
+ */
1030
+ compressSourceCode(code, maxLength = 3e3, importPathRewrites) {
1031
+ let compressed = code;
1032
+ if (importPathRewrites && importPathRewrites.size > 0) {
1033
+ compressed = this.rewriteImportPaths(compressed, importPathRewrites);
1034
+ }
1035
+ compressed = compressed.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "");
1036
+ compressed = compressed.replace(/<template[^>]*>([\s\S]*?)<\/template>/gi, (_match, content) => {
1037
+ return `<template>${content.replace(/\s*(?:class|style)\s*=\s*["'][^"']*["']/gi, "")}</template>`;
1038
+ });
1039
+ compressed = compressed.replace(/\/\*[\s\S]*?\*\//g, "");
1040
+ compressed = compressed.replace(/(^|[^:])(\/\/.*$)/gm, "$1");
1041
+ compressed = compressed.replace(/\n\s*\n\s*\n/g, "\n\n");
1042
+ compressed = compressed.split("\n").map((line) => line.trimEnd()).join("\n").trim();
1043
+ if (compressed.length > maxLength) {
1044
+ const scriptMatch = compressed.match(/<script[^>]*>([\s\S]*?)<\/script>/i);
1045
+ const templateMatch = compressed.match(/<template[^>]*>([\s\S]*?)<\/template>/i);
1046
+ if (scriptMatch) {
1047
+ let scriptPart = scriptMatch[1].trim();
1048
+ scriptPart = compressFunctionBodies(scriptPart, maxLength * 0.7);
1049
+ const templatePart = templateMatch ? `<template>${templateMatch[1].replace(/\s+/g, " ").trim().slice(0, 300)}...</template>` : "";
1050
+ compressed = `${templatePart}
1051
+ <script${scriptMatch[0].match(/<script[^>]*>/)?.[0]?.slice(7) || ">"}>${scriptPart}</script>`;
1052
+ } else {
1053
+ compressed = compressFunctionBodies(compressed, maxLength);
1054
+ }
998
1055
  }
999
- return data.choices[0].message.content;
1056
+ return compressed;
1000
1057
  }
1001
1058
  buildGenerateTestSystemPrompt(req) {
1002
1059
  const typeMap = {
1003
- unit: "\u5355\u5143\u6D4B\u8BD5\uFF08Vitest + @vue/test-utils\uFF09",
1004
- component: "\u7EC4\u4EF6\u6D4B\u8BD5\uFF08Vitest + @vue/test-utils + mount\uFF09",
1005
- e2e: "E2E\u7AEF\u5230\u7AEF\u6D4B\u8BD5\uFF08Playwright\uFF09",
1006
- api: "API\u63A5\u53E3\u6D4B\u8BD5\uFF08Vitest + fetch\uFF09",
1007
- visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5\uFF08Playwright screenshot\uFF09",
1008
- performance: "\u6027\u80FD\u6D4B\u8BD5\uFF08Playwright + performance metrics\uFF09"
1060
+ unit: "\u5355\u5143\u6D4B\u8BD5(Vitest)",
1061
+ component: "\u7EC4\u4EF6\u6D4B\u8BD5(Vitest+@vue/test-utils)",
1062
+ e2e: "E2E\u6D4B\u8BD5(Playwright)",
1063
+ api: "API\u6D4B\u8BD5(Vitest+fetch)",
1064
+ visual: "\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5(Playwright)",
1065
+ performance: "\u6027\u80FD\u6D4B\u8BD5(Playwright)"
1009
1066
  };
1010
- return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\uFF0C\u64C5\u957F\u7F16\u5199\u9AD8\u8D28\u91CF\u7684${typeMap[req.type] || req.type}\u3002
1011
- \u8981\u6C42\uFF1A
1012
- 1. \u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u8981\u591A\u4F59\u7684\u89E3\u91CA
1013
- 2. \u4EE3\u7801\u5FC5\u987B\u53EF\u76F4\u63A5\u8FD0\u884C\uFF0C\u5305\u542B\u6240\u6709\u5FC5\u8981\u7684 import
1014
- 3. \u6D4B\u8BD5\u7528\u4F8B\u8986\u76D6\uFF1A\u6B63\u5E38\u8DEF\u5F84\u3001\u8FB9\u754C\u6761\u4EF6\u3001\u9519\u8BEF\u5904\u7406
1015
- 4. \u4F7F\u7528\u4E2D\u6587\u63CF\u8FF0 it/test \u5757\u540D\u79F0
1016
- 5. Vue \u7EC4\u4EF6\u6D4B\u8BD5\u4F7F\u7528 @vue/test-utils \u7684 mount
1017
- 6. \u5982\u679C\u6709 props/emits \u4FE1\u606F\uFF0C\u52A1\u5FC5\u9488\u5BF9\u6BCF\u4E2A prop \u548C emit \u751F\u6210\u6D4B\u8BD5`;
1067
+ return `\u4F60\u662F\u524D\u7AEF\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\uFF0C\u7F16\u5199${typeMap[req.type] || req.type}\u3002
1068
+ \u89C4\u5219:1.\u53EA\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801 2.\u5305\u542B\u6240\u6709import 3.\u8986\u76D6\u6B63\u5E38/\u8FB9\u754C/\u9519\u8BEF 4.it\u540D\u7528\u4E2D\u6587 5.Vue\u7528mount 6.\u5FC5\u6D4B\u6BCF\u4E2Aprop\u548Cemit`;
1018
1069
  }
1019
1070
  buildGenerateTestUserPrompt(req) {
1020
1071
  const importPath = this.computeTestImportPath(req.type, req.target);
1021
- let prompt = `\u8BF7\u4E3A\u4EE5\u4E0B\u6587\u4EF6\u751F\u6210${req.type}\u6D4B\u8BD5\u4EE3\u7801:
1022
- \u76EE\u6807\u6587\u4EF6: ${req.target}
1023
- \u6D4B\u8BD5\u6587\u4EF6\u5C06\u653E\u5728: ${this.getTestOutputDir(req.type)}/
1024
- \u6B63\u786E\u7684 import \u8DEF\u5F84: ${importPath}
1025
-
1026
- \u91CD\u8981\uFF1Aimport \u8BED\u53E5\u4E2D\u5FC5\u987B\u4F7F\u7528\u4E0A\u8FF0\u6B63\u786E\u7684\u76F8\u5BF9\u8DEF\u5F84 ${importPath}\uFF0C\u4E0D\u8981\u4F7F\u7528 ${req.target} \u6216\u5176\u4ED6\u8DEF\u5F84\uFF01
1072
+ let prompt = `\u4E3A${req.target}\u751F\u6210${req.type}\u6D4B\u8BD5\u3002
1073
+ \u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
1027
1074
  `;
1028
1075
  if (req.analysis) {
1029
- prompt += "\n\u6E90\u7801\u5206\u6790\u7ED3\u679C:\n";
1076
+ const parts = [];
1030
1077
  if (req.analysis.exports?.length > 0) {
1031
- prompt += `\u5BFC\u51FA\u9879:
1032
- ${req.analysis.exports.map((e) => {
1033
- const params = e.params?.length ? `(${e.params.join(", ")})` : "";
1034
- const asyncFlag = e.isAsync ? "async " : "";
1035
- return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
1036
- }).join("\n")}
1037
- `;
1078
+ parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => {
1079
+ const p = e.params?.length ? `(${e.params.join(",")})` : "";
1080
+ return `${e.isAsync ? "async " : ""}${e.name}${p}[${e.kind}]`;
1081
+ }).join(",")}`);
1038
1082
  }
1039
1083
  if (req.analysis.props?.length) {
1040
- prompt += `Props:
1041
- ${req.analysis.props.map(
1042
- (p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
1043
- ).join("\n")}
1044
- `;
1084
+ parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
1045
1085
  }
1046
1086
  if (req.analysis.emits?.length) {
1047
- prompt += `Emits:
1048
- ${req.analysis.emits.map(
1049
- (e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
1050
- ).join("\n")}
1051
- `;
1087
+ parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
1052
1088
  }
1053
1089
  if (req.analysis.methods?.length) {
1054
- prompt += `Methods: ${req.analysis.methods.join(", ")}
1055
- `;
1090
+ parts.push(`Methods:${req.analysis.methods.join(",")}`);
1056
1091
  }
1057
1092
  if (req.analysis.computed?.length) {
1058
- prompt += `Computed: ${req.analysis.computed.join(", ")}
1059
- `;
1093
+ parts.push(`Computed:${req.analysis.computed.join(",")}`);
1094
+ }
1095
+ if (req.analysis.importSignatures?.length) {
1096
+ parts.push(`\u4F9D\u8D56\u7B7E\u540D:${req.analysis.importSignatures.map(
1097
+ (imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
1098
+ ).join(";")}`);
1060
1099
  }
1100
+ prompt += parts.join("\n") + "\n";
1061
1101
  }
1062
1102
  if (req.context) {
1103
+ const importPathRewrites = this.buildImportPathRewrites(req.type, req.target);
1063
1104
  prompt += `
1064
- \u6E90\u7801\u5185\u5BB9:
1065
- \`\`\`typescript
1066
- ${req.context}
1105
+ \u6E90\u7801:
1106
+ \`\`\`
1107
+ ${this.compressSourceCode(req.context, 3e3, importPathRewrites)}
1067
1108
  \`\`\`
1068
1109
  `;
1069
1110
  }
1070
1111
  if (req.framework) {
1071
- prompt += `
1072
- \u6846\u67B6: ${req.framework}`;
1112
+ prompt += `\u6846\u67B6:${req.framework}`;
1073
1113
  }
1074
1114
  return prompt;
1075
1115
  }
@@ -1100,18 +1140,54 @@ ${req.context}
1100
1140
  return `${prefix}${cleanPath}`;
1101
1141
  }
1102
1142
  /**
1103
- * 获取测试输出目录
1143
+ * 构建 import 路径重写映射
1144
+ * 将源码中可能引用自身的各种写法,映射到测试文件中正确的导入路径
1104
1145
  */
1105
- getTestOutputDir(testType) {
1106
- const dirMap = {
1107
- unit: "tests/unit",
1108
- component: "tests/component",
1109
- e2e: "tests/e2e",
1110
- api: "tests/api",
1111
- visual: "tests/visual",
1112
- performance: "tests/e2e"
1113
- };
1114
- return dirMap[testType] || "tests/unit";
1146
+ buildImportPathRewrites(testType, targetPath) {
1147
+ const rewrites = /* @__PURE__ */ new Map();
1148
+ if (!targetPath) return rewrites;
1149
+ const correctImportPath = this.computeTestImportPath(testType, targetPath);
1150
+ const variations = /* @__PURE__ */ new Set();
1151
+ variations.add(targetPath);
1152
+ variations.add(targetPath.replace(/^\.\//, ""));
1153
+ const withoutExt = targetPath.replace(/\.(ts|js|tsx|jsx)$/, "");
1154
+ variations.add(withoutExt);
1155
+ const srcDirMatch = targetPath.match(/^(?:\.\/)?(src\/.+)$/);
1156
+ if (srcDirMatch) {
1157
+ variations.add(`@/${srcDirMatch[1]}`);
1158
+ variations.add(`@/${srcDirMatch[1].replace(/\.(ts|js|tsx|jsx)$/, "")}`);
1159
+ }
1160
+ const pathParts = targetPath.replace(/^\.\//, "").split("/");
1161
+ const fileName = pathParts[pathParts.length - 1];
1162
+ const fileNameNoExt = fileName.replace(/\.(ts|js|tsx|jsx|vue)$/, "");
1163
+ variations.add(`./${fileName}`);
1164
+ variations.add(`./${fileNameNoExt}`);
1165
+ for (let i = 1; i < pathParts.length; i++) {
1166
+ const relPath = "../".repeat(i) + pathParts.slice(i).join("/");
1167
+ variations.add(relPath);
1168
+ variations.add(relPath.replace(/\.(ts|js|tsx|jsx)$/, ""));
1169
+ }
1170
+ for (const variant of variations) {
1171
+ if (variant && variant !== correctImportPath) {
1172
+ rewrites.set(variant, correctImportPath);
1173
+ }
1174
+ }
1175
+ return rewrites;
1176
+ }
1177
+ /**
1178
+ * 重写源码中的 import 路径
1179
+ * 将 from 'oldPath' / from "oldPath" 替换为正确的路径
1180
+ */
1181
+ rewriteImportPaths(code, rewrites) {
1182
+ let result = code;
1183
+ for (const [oldPath, newPath] of rewrites) {
1184
+ const escaped = oldPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1185
+ const singleQuoteRegex = new RegExp(`(from\\s+')${escaped}(')`, "g");
1186
+ const doubleQuoteRegex = new RegExp(`(from\\s+")${escaped}(")`, "g");
1187
+ result = result.replace(singleQuoteRegex, `$1${newPath}$2`);
1188
+ result = result.replace(doubleQuoteRegex, `$1${newPath}$2`);
1189
+ }
1190
+ return result;
1115
1191
  }
1116
1192
  parseGenerateTestResponse(content) {
1117
1193
  const codeBlockMatch = content.match(/```(?:typescript|ts|javascript|js)?\s*\n([\s\S]*?)```/);
@@ -1124,69 +1200,56 @@ ${req.context}
1124
1200
  };
1125
1201
  }
1126
1202
  buildReviewTestSystemPrompt(_req) {
1127
- return `\u4F60\u662F\u4E00\u4F4D\u4E25\u8C28\u7684\u6D4B\u8BD5\u5BA1\u8BA1\u4E13\u5BB6\u3002\u4F60\u7684\u804C\u8D23\u662F\u5BA1\u67E5 AI \u751F\u6210\u7684\u6D4B\u8BD5\u7528\u4F8B\u662F\u5426\u4E0E\u6E90\u7801\u8D34\u5207\u4E14\u51C6\u786E\u3002
1128
-
1129
- \u5BA1\u67E5\u6807\u51C6\uFF1A
1130
- 1. **\u6D4B\u8BD5\u7C7B\u578B\u5339\u914D**\uFF1A\u6D4B\u8BD5\u7C7B\u578B\u662F\u5426\u4E0E\u6E90\u7801\u6587\u4EF6\u6027\u8D28\u5339\u914D\uFF08\u5982 .vue \u5E94\u4E3A\u7EC4\u4EF6\u6D4B\u8BD5\uFF0Cutils \u5E94\u4E3A\u5355\u5143\u6D4B\u8BD5\uFF09
1131
- 2. **\u8986\u76D6\u5B8C\u6574\u6027**\uFF1A\u662F\u5426\u8986\u76D6\u4E86\u6E90\u7801\u4E2D\u7684\u6838\u5FC3\u5BFC\u51FA\u9879\uFF08\u51FD\u6570\u3001\u7EC4\u4EF6\u7684 props/emits/methods\uFF09
1132
- 3. **\u65AD\u8A00\u6709\u6548\u6027**\uFF1A\u65AD\u8A00\u662F\u5426\u771F\u5B9E\u68C0\u9A8C\u4E86\u88AB\u6D4B\u884C\u4E3A\uFF0C\u800C\u975E\u7A7A\u65AD\u8A00\u6216\u6C38\u771F\u65AD\u8A00
1133
- 4. **\u5BFC\u5165\u6B63\u786E\u6027**\uFF1Aimport \u8DEF\u5F84\u548C\u6A21\u5757\u662F\u5426\u6B63\u786E
1134
- 5. **\u4EE3\u7801\u53EF\u8FD0\u884C\u6027**\uFF1A\u6D4B\u8BD5\u4EE3\u7801\u662F\u5426\u53EF\u76F4\u63A5\u8FD0\u884C\uFF0C\u65E0\u8BED\u6CD5\u9519\u8BEF
1135
-
1136
- \u8F93\u51FA\u683C\u5F0F\uFF08\u4E25\u683C\u9075\u5B88\uFF09\uFF1A
1137
- APPROVED: true \u6216 false
1138
- SCORE: 0.0 \u5230 1.0 \u4E4B\u95F4\u7684\u8BC4\u5206
1139
- FEEDBACK: \u4E00\u53E5\u8BDD\u5BA1\u8BA1\u610F\u89C1
1140
- ISSUES: \u95EE\u9898\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u95EE\u9898\u63CF\u8FF0"\uFF09
1141
- SUGGESTIONS: \u6539\u8FDB\u5EFA\u8BAE\u5217\u8868\uFF08\u6BCF\u884C\u4E00\u4E2A\uFF0C\u683C\u5F0F "- \u5EFA\u8BAE\u63CF\u8FF0"\uFF09`;
1203
+ return `\u4F60\u662F\u6D4B\u8BD5\u5BA1\u8BA1\u4E13\u5BB6\u3002\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u662F\u5426\u51C6\u786E\u3002
1204
+ \u6807\u51C6:1.\u7C7B\u578B\u5339\u914D 2.\u8986\u76D6\u6838\u5FC3\u5BFC\u51FA/props/emits 3.\u65AD\u8A00\u6709\u6548(\u975E\u6C38\u771F) 4.import\u6B63\u786E 5.\u53EF\u8FD0\u884C
1205
+ \u8F93\u51FA:
1206
+ APPROVED:true\u6216false
1207
+ SCORE:0.0-1.0
1208
+ FEEDBACK:\u4E00\u53E5\u8BDD
1209
+ ISSUES:- \u95EE\u9898(\u6BCF\u884C\u4E00\u4E2A)
1210
+ SUGGESTIONS:- \u5EFA\u8BAE(\u6BCF\u884C\u4E00\u4E2A)`;
1142
1211
  }
1143
1212
  buildReviewTestUserPrompt(req) {
1144
1213
  const importPath = this.computeTestImportPath(req.testType, req.target);
1145
- let prompt = `\u8BF7\u5BA1\u67E5\u4EE5\u4E0B\u6D4B\u8BD5\u7528\u4F8B\u662F\u5426\u4E0E\u6E90\u7801\u8D34\u5207\u4E14\u51C6\u786E\u3002
1146
-
1147
- \u88AB\u6D4B\u6587\u4EF6: ${req.target}
1148
- \u6D4B\u8BD5\u7C7B\u578B: ${req.testType}
1149
- \u6B63\u786E\u7684 import \u8DEF\u5F84\u5E94\u4E3A: ${importPath}\uFF08\u6D4B\u8BD5\u6587\u4EF6\u4F4D\u4E8E ${this.getTestOutputDir(req.testType)}/\uFF09
1150
-
1151
- --- \u6E90\u7801\u5185\u5BB9 ---
1152
- \`\`\`typescript
1153
- ${req.sourceCode}
1214
+ let prompt = `\u5BA1\u67E5\u6D4B\u8BD5\u4EE3\u7801\u3002\u88AB\u6D4B:${req.target} \u7C7B\u578B:${req.testType}
1215
+ \u3010\u5F3A\u5236\u3011import\u88AB\u6D4B\u6A21\u5757\u5FC5\u987B\u7528: ${importPath}
1216
+ `;
1217
+ const importPathRewrites = this.buildImportPathRewrites(req.testType, req.target);
1218
+ prompt += `
1219
+ \u6E90\u7801:
1220
+ \`\`\`
1221
+ ${this.compressSourceCode(req.sourceCode, 2e3, importPathRewrites)}
1222
+ \`\`\`
1223
+ `;
1224
+ prompt += `
1225
+ \u6D4B\u8BD5\u4EE3\u7801:
1154
1226
  \`\`\`
1155
-
1156
- --- \u751F\u6210\u7684\u6D4B\u8BD5\u4EE3\u7801 ---
1157
- \`\`\`typescript
1158
1227
  ${req.testCode}
1159
1228
  \`\`\``;
1160
1229
  if (req.analysis) {
1161
- prompt += "\n\n--- \u6E90\u7801\u5206\u6790 ---";
1230
+ const parts = [];
1162
1231
  if (req.analysis.exports?.length > 0) {
1163
- prompt += `
1164
- \u5BFC\u51FA\u9879:
1165
- ${req.analysis.exports.map((e) => {
1166
- const params = e.params?.length ? `(${e.params.join(", ")})` : "";
1167
- const asyncFlag = e.isAsync ? "async " : "";
1168
- return ` - ${asyncFlag}${e.name}${params} [${e.kind}]`;
1169
- }).join("\n")}`;
1232
+ parts.push(`\u5BFC\u51FA:${req.analysis.exports.map((e) => `${e.isAsync ? "async " : ""}${e.name}(${e.params?.join(",") || ""})[${e.kind}]`).join(",")}`);
1170
1233
  }
1171
1234
  if (req.analysis.props?.length) {
1172
- prompt += `
1173
- Props:
1174
- ${req.analysis.props.map(
1175
- (p) => ` - ${p.name}: ${p.type}${p.required ? " (\u5FC5\u586B)" : " (\u53EF\u9009)"}`
1176
- ).join("\n")}`;
1235
+ parts.push(`Props:${req.analysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",")}`);
1177
1236
  }
1178
1237
  if (req.analysis.emits?.length) {
1238
+ parts.push(`Emits:${req.analysis.emits.map((e) => `${e.name}(${e.params?.join(",") || ""})`).join(",")}`);
1239
+ }
1240
+ if (req.analysis.importSignatures?.length) {
1241
+ parts.push(`\u4F9D\u8D56:${req.analysis.importSignatures.map(
1242
+ (imp) => `${imp.source}{${Object.entries(imp.signatures).map(([k, v]) => `${k}:${v}`).join(",")}}`
1243
+ ).join(";")}`);
1244
+ }
1245
+ if (parts.length > 0) {
1179
1246
  prompt += `
1180
- Emits:
1181
- ${req.analysis.emits.map(
1182
- (e) => ` - ${e.name}${e.params?.length ? `(${e.params.join(", ")})` : ""}`
1183
- ).join("\n")}`;
1247
+ \u5206\u6790:${parts.join("|")}`;
1184
1248
  }
1185
1249
  }
1186
1250
  if (req.generationDescription) {
1187
1251
  prompt += `
1188
-
1189
- \u751F\u6210\u8005\u8BF4\u660E: ${req.generationDescription}`;
1252
+ \u8BF4\u660E:${req.generationDescription}`;
1190
1253
  }
1191
1254
  return prompt;
1192
1255
  }
@@ -1246,6 +1309,61 @@ ${req.analysis.emits.map(
1246
1309
  return urlMap[provider] || "https://api.openai.com/v1";
1247
1310
  }
1248
1311
  };
1312
+ function compressFunctionBodies(code, maxLength) {
1313
+ const lines = code.split("\n");
1314
+ const result = [];
1315
+ let depth = 0;
1316
+ let fnDepth = 0;
1317
+ let inFunction = false;
1318
+ let braceBalance = 0;
1319
+ let capturedLines = 0;
1320
+ for (const line of lines) {
1321
+ const trimmed = line.trim();
1322
+ for (const ch of trimmed) {
1323
+ if (ch === "{") {
1324
+ braceBalance++;
1325
+ depth++;
1326
+ }
1327
+ if (ch === "}") {
1328
+ braceBalance--;
1329
+ depth = Math.max(0, depth - 1);
1330
+ }
1331
+ }
1332
+ const isFnSignature = /^(export\s+)?(async\s+)?function\s|=>\s*\{|=>\s*$/m.test(trimmed) || /^(const|let|var)\s+\w+\s*=\s*(async\s+)?(\([^)]*\)|[^=])\s*=>/.test(trimmed);
1333
+ if (isFnSignature && !inFunction) {
1334
+ inFunction = true;
1335
+ fnDepth = depth;
1336
+ result.push(line);
1337
+ capturedLines++;
1338
+ continue;
1339
+ }
1340
+ if (inFunction) {
1341
+ const isReturn = trimmed.startsWith("return ");
1342
+ const isBranch = /^(if|else|switch|case|try|catch|finally|for|while)/.test(trimmed);
1343
+ const isClosing = trimmed === "}" && depth <= fnDepth - 1;
1344
+ if (isClosing) {
1345
+ result.push(line);
1346
+ inFunction = false;
1347
+ capturedLines++;
1348
+ } else if (isReturn || isBranch) {
1349
+ result.push(trimmed.length > 120 ? `${trimmed.slice(0, 120)}...` : line);
1350
+ capturedLines++;
1351
+ } else if (trimmed.startsWith("//") || trimmed === "") {
1352
+ } else if (capturedLines < maxLength / 30) {
1353
+ result.push(trimmed.length > 100 ? `${trimmed.slice(0, 100)}...` : line);
1354
+ capturedLines++;
1355
+ }
1356
+ } else {
1357
+ result.push(line);
1358
+ capturedLines++;
1359
+ }
1360
+ if (result.join("\n").length > maxLength) {
1361
+ result.push("// ... (truncated)");
1362
+ break;
1363
+ }
1364
+ }
1365
+ return result.join("\n");
1366
+ }
1249
1367
 
1250
1368
  // src/ai/provider.ts
1251
1369
  var providerRegistry = /* @__PURE__ */ new Map();
@@ -1315,15 +1433,18 @@ import path4 from "path";
1315
1433
  function analyzeFile(filePath) {
1316
1434
  const absolutePath = path4.resolve(process.cwd(), filePath);
1317
1435
  if (!fs4.existsSync(absolutePath)) {
1318
- return { filePath, exports: [], apiCalls: [] };
1436
+ return { filePath, exports: [], imports: [], importSignatures: [], apiCalls: [] };
1319
1437
  }
1320
1438
  const content = fs4.readFileSync(absolutePath, "utf-8");
1321
1439
  const ext = path4.extname(filePath);
1322
1440
  const result = {
1323
1441
  filePath,
1324
1442
  exports: extractExports(content, ext),
1443
+ imports: extractImports(content),
1444
+ importSignatures: [],
1325
1445
  apiCalls: extractAPICalls(content, filePath)
1326
1446
  };
1447
+ result.importSignatures = analyzeImportSignatures(result.imports, path4.dirname(absolutePath));
1327
1448
  if (ext === ".vue") {
1328
1449
  result.vueAnalysis = analyzeVueComponent(content, path4.basename(filePath, ".vue"));
1329
1450
  const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
@@ -1743,6 +1864,90 @@ function generateMockRoutesFromAPICalls(apiCalls) {
1743
1864
  }
1744
1865
  return routes;
1745
1866
  }
1867
+ function extractImports(content) {
1868
+ const imports = [];
1869
+ const namedImportRegex = /import\s*\{([^}]+)\}\s*from\s*['"]([^'"]+)['"]/g;
1870
+ let match;
1871
+ while ((match = namedImportRegex.exec(content)) !== null) {
1872
+ const names = match[1].split(",").map((n) => {
1873
+ const parts = n.trim().split(/\s+as\s+/);
1874
+ return parts[0].trim();
1875
+ }).filter(Boolean);
1876
+ imports.push({ names, source: match[2], isDefault: false, isNamespace: false });
1877
+ }
1878
+ const defaultImportRegex = /import\s+(\w+)\s+from\s*['"]([^'"]+)['"]/g;
1879
+ while ((match = defaultImportRegex.exec(content)) !== null) {
1880
+ const fullLine = content.slice(Math.max(0, match.index - 5), match.index + match[0].length);
1881
+ if (fullLine.includes("{")) continue;
1882
+ imports.push({ names: [match[1]], source: match[2], isDefault: true, isNamespace: false });
1883
+ }
1884
+ const namespaceImportRegex = /import\s*\*\s*as\s+(\w+)\s*from\s*['"]([^'"]+)['"]/g;
1885
+ while ((match = namespaceImportRegex.exec(content)) !== null) {
1886
+ imports.push({ names: [match[1]], source: match[2], isDefault: false, isNamespace: true });
1887
+ }
1888
+ return imports;
1889
+ }
1890
+ function analyzeImportSignatures(imports, baseDir) {
1891
+ const signatures = [];
1892
+ for (const imp of imports) {
1893
+ if (!imp.source.startsWith(".") && !imp.source.startsWith("@/") && !imp.source.startsWith("~")) {
1894
+ continue;
1895
+ }
1896
+ const resolvedPath = resolveImportPath(imp.source, baseDir);
1897
+ if (!resolvedPath || !fs4.existsSync(resolvedPath)) continue;
1898
+ try {
1899
+ const content = fs4.readFileSync(resolvedPath, "utf-8");
1900
+ const ext = path4.extname(resolvedPath);
1901
+ const exports = extractExports(content, ext);
1902
+ const sigMap = {};
1903
+ for (const name of imp.names) {
1904
+ const exp = exports.find((e) => e.name === name);
1905
+ if (exp) {
1906
+ const params = exp.params.length > 0 ? `(${exp.params.join(", ")})` : "";
1907
+ const ret = exp.returnType ? `: ${exp.returnType}` : "";
1908
+ const async = exp.isAsync ? "async " : "";
1909
+ sigMap[name] = `${async}${exp.kind}${params}${ret}`;
1910
+ } else if (imp.isDefault) {
1911
+ const defaultExp = exports.find((e) => e.kind === "default");
1912
+ if (defaultExp) {
1913
+ sigMap[name] = `default[${defaultExp.name || "anonymous"}]`;
1914
+ }
1915
+ } else if (imp.isNamespace) {
1916
+ sigMap[name] = `{${exports.map((e) => e.name).join(", ")}}`;
1917
+ }
1918
+ }
1919
+ if (ext === ".vue") {
1920
+ const vueAnalysis = analyzeVueComponent(content, path4.basename(resolvedPath, ".vue"));
1921
+ if (vueAnalysis.props.length > 0) {
1922
+ sigMap["__props__"] = vueAnalysis.props.map((p) => `${p.name}:${p.type}${p.required ? "!" : "?"}`).join(",");
1923
+ }
1924
+ }
1925
+ if (Object.keys(sigMap).length > 0) {
1926
+ signatures.push({ source: imp.source, signatures: sigMap });
1927
+ }
1928
+ } catch {
1929
+ }
1930
+ }
1931
+ return signatures;
1932
+ }
1933
+ function resolveImportPath(importSource, baseDir) {
1934
+ let resolved;
1935
+ if (importSource.startsWith("@/") || importSource.startsWith("~")) {
1936
+ const relativePath = importSource.replace(/^[@~]\//, "src/");
1937
+ resolved = path4.resolve(process.cwd(), relativePath);
1938
+ } else if (importSource.startsWith(".")) {
1939
+ resolved = path4.resolve(baseDir, importSource);
1940
+ } else {
1941
+ return null;
1942
+ }
1943
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".vue", "/index.ts", "/index.js"];
1944
+ if (fs4.existsSync(resolved)) return resolved;
1945
+ for (const ext of extensions) {
1946
+ const withExt = resolved + ext;
1947
+ if (fs4.existsSync(withExt)) return withExt;
1948
+ }
1949
+ return null;
1950
+ }
1746
1951
 
1747
1952
  // src/services/global-config.ts
1748
1953
  import fs5 from "fs";
@@ -1794,7 +1999,7 @@ function registerInitCommand(program2) {
1794
1999
  try {
1795
2000
  await executeInit(options);
1796
2001
  } catch (error) {
1797
- console.error(chalk2.red(`
2002
+ console.error(chalk3.red(`
1798
2003
  \u2717 ${error instanceof Error ? error.message : String(error)}
1799
2004
  `));
1800
2005
  process.exit(1);
@@ -1816,19 +2021,19 @@ async function executeInit(options) {
1816
2021
  }
1817
2022
  ]);
1818
2023
  if (!proceed) {
1819
- console.log(chalk2.gray("\n \u5DF2\u53D6\u6D88\u521D\u59CB\u5316\n"));
2024
+ console.log(chalk3.gray("\n \u5DF2\u53D6\u6D88\u521D\u59CB\u5316\n"));
1820
2025
  return;
1821
2026
  }
1822
2027
  }
1823
2028
  let globalAI = loadGlobalAIConfig();
1824
2029
  if (!globalAI) {
1825
- console.log(chalk2.cyan(" AI \u6A21\u578B\u914D\u7F6E (\u9996\u6B21\u4F7F\u7528\u9700\u914D\u7F6E\uFF0C\u4E4B\u540E\u53EF\u901A\u8FC7 qat change \u4FEE\u6539)\n"));
2030
+ console.log(chalk3.cyan(" AI \u6A21\u578B\u914D\u7F6E (\u9996\u6B21\u4F7F\u7528\u9700\u914D\u7F6E\uFF0C\u4E4B\u540E\u53EF\u901A\u8FC7 qat change \u4FEE\u6539)\n"));
1826
2031
  globalAI = await promptAIConfig();
1827
2032
  saveGlobalAIConfig(globalAI);
1828
- console.log(chalk2.gray(` \u914D\u7F6E\u5DF2\u4FDD\u5B58\u81F3 ${getAIConfigPath()}
2033
+ console.log(chalk3.gray(` \u914D\u7F6E\u5DF2\u4FDD\u5B58\u81F3 ${getAIConfigPath()}
1829
2034
  `));
1830
2035
  } else {
1831
- console.log(chalk2.green(` \u2713 \u5F53\u524D AI \u6A21\u578B: ${chalk2.white(globalAI.model)} @ ${chalk2.gray(globalAI.baseUrl)} (${maskApiKey(globalAI.apiKey)})
2036
+ console.log(chalk3.green(` \u2713 \u5F53\u524D AI \u6A21\u578B: ${chalk3.white(globalAI.model)} @ ${chalk3.gray(globalAI.baseUrl)} (${maskApiKey(globalAI.apiKey)})
1832
2037
  `));
1833
2038
  }
1834
2039
  const aiConfig = toAIConfig(globalAI);
@@ -1837,51 +2042,23 @@ async function executeInit(options) {
1837
2042
  try {
1838
2043
  const result = await testAIConnection(aiConfig);
1839
2044
  if (result.ok) {
1840
- testSpinner.succeed(`AI \u8FDE\u901A\u6B63\u5E38 ${chalk2.gray(`${globalAI.model} (${result.latencyMs}ms)`)}`);
2045
+ testSpinner.succeed(`AI \u8FDE\u901A\u6B63\u5E38 ${chalk3.gray(`${globalAI.model} (${result.latencyMs}ms)`)}`);
1841
2046
  } else {
1842
2047
  testSpinner.fail(`AI \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
1843
- console.log(chalk2.yellow(" \u53EF\u8FD0\u884C qat change \u4FEE\u6539 AI \u914D\u7F6E\u3002"));
2048
+ console.log(chalk3.yellow(" \u53EF\u8FD0\u884C qat change \u4FEE\u6539 AI \u914D\u7F6E\u3002"));
1844
2049
  }
1845
2050
  } catch (error) {
1846
2051
  testSpinner.fail(`AI \u8FDE\u901A\u6D4B\u8BD5\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
1847
2052
  }
1848
2053
  }
1849
2054
  const config = buildProjectConfig(projectInfo);
1850
- let configPath;
1851
2055
  const existingConfigPath = path6.join(process.cwd(), "qat.config.js");
1852
2056
  const existingTsPath = path6.join(process.cwd(), "qat.config.ts");
1853
2057
  const configExists = fs6.existsSync(existingConfigPath) || fs6.existsSync(existingTsPath);
1854
- if (configExists && !options.force) {
1855
- const { overwrite } = await inquirer.prompt([
1856
- {
1857
- type: "confirm",
1858
- name: "overwrite",
1859
- message: "\u914D\u7F6E\u6587\u4EF6 qat.config.js \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6\uFF1F",
1860
- default: true
1861
- }
1862
- ]);
1863
- if (!overwrite) {
1864
- console.log(chalk2.gray(" \u4FDD\u7559\u73B0\u6709\u914D\u7F6E\u6587\u4EF6\uFF0C\u7EE7\u7EED\u540E\u7EED\u6B65\u9AA4..."));
1865
- configPath = existingConfigPath;
1866
- } else {
1867
- const fileSpinner = ora("\u6B63\u5728\u8986\u76D6\u914D\u7F6E\u6587\u4EF6...").start();
1868
- try {
1869
- configPath = await writeConfigFile(process.cwd(), config, true);
1870
- fileSpinner.succeed("\u914D\u7F6E\u6587\u4EF6\u5DF2\u8986\u76D6");
1871
- } catch (error) {
1872
- fileSpinner.fail("\u914D\u7F6E\u6587\u4EF6\u8986\u76D6\u5931\u8D25");
1873
- throw error;
1874
- }
1875
- }
2058
+ if (!configExists) {
2059
+ console.log(chalk3.gray(" \u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u914D\u7F6E\uFF08\u9700\u8981\u81EA\u5B9A\u4E49\u65F6\u53EF\u521B\u5EFA qat.config.js\uFF09"));
1876
2060
  } else {
1877
- const fileSpinner = ora("\u6B63\u5728\u751F\u6210\u914D\u7F6E\u6587\u4EF6...").start();
1878
- try {
1879
- configPath = await writeConfigFile(process.cwd(), config, options.force);
1880
- fileSpinner.succeed("\u914D\u7F6E\u6587\u4EF6\u5DF2\u751F\u6210");
1881
- } catch (error) {
1882
- fileSpinner.fail("\u914D\u7F6E\u6587\u4EF6\u751F\u6210\u5931\u8D25");
1883
- throw error;
1884
- }
2061
+ console.log(chalk3.green(` \u2713 \u5DF2\u6709\u914D\u7F6E\u6587\u4EF6: ${path6.basename(fs6.existsSync(existingConfigPath) ? existingConfigPath : existingTsPath)}`));
1885
2062
  }
1886
2063
  const dirSpinner = ora("\u6B63\u5728\u521B\u5EFA\u6D4B\u8BD5\u76EE\u5F55...").start();
1887
2064
  const createdDirs = createTestDirectories(config);
@@ -1896,10 +2073,10 @@ async function executeInit(options) {
1896
2073
  const mockFilePath = path6.join(process.cwd(), mockDir, "auto-generated.json");
1897
2074
  if (!fs6.existsSync(mockFilePath)) {
1898
2075
  fs6.writeFileSync(mockFilePath, JSON.stringify(mockRoutes, null, 2), "utf-8");
1899
- console.log(chalk2.green(` \u81EA\u52A8\u53D1\u73B0 ${apiCalls.length} \u4E2A API \u63A5\u53E3\uFF0C\u5DF2\u751F\u6210 Mock \u8DEF\u7531`));
2076
+ console.log(chalk3.green(` \u81EA\u52A8\u53D1\u73B0 ${apiCalls.length} \u4E2A API \u63A5\u53E3\uFF0C\u5DF2\u751F\u6210 Mock \u8DEF\u7531`));
1900
2077
  }
1901
2078
  } else {
1902
- console.log(chalk2.gray(" \u672A\u53D1\u73B0 API \u8C03\u7528\uFF0C\u5DF2\u751F\u6210\u793A\u4F8B Mock \u8DEF\u7531"));
2079
+ console.log(chalk3.gray(" \u672A\u53D1\u73B0 API \u8C03\u7528\uFF0C\u5DF2\u751F\u6210\u793A\u4F8B Mock \u8DEF\u7531"));
1903
2080
  }
1904
2081
  }
1905
2082
  const srcDir = config.project?.srcDir || "src";
@@ -1907,10 +2084,10 @@ async function executeInit(options) {
1907
2084
  const utilities = discoverUtilityFiles(process.cwd(), srcDir);
1908
2085
  const totalFiles = components.length + utilities.length;
1909
2086
  if (totalFiles > 0) {
1910
- console.log(chalk2.cyan(`
2087
+ console.log(chalk3.cyan(`
1911
2088
  \u53D1\u73B0 ${totalFiles} \u4E2A\u53EF\u6D4B\u8BD5\u6587\u4EF6 (${components.length} \u7EC4\u4EF6, ${utilities.length} \u5DE5\u5177/\u670D\u52A1)`));
1912
2089
  }
1913
- displayResult(configPath, createdDirs, totalFiles, projectInfo);
2090
+ displayResult(createdDirs, totalFiles, projectInfo);
1914
2091
  }
1915
2092
  async function promptAIConfig() {
1916
2093
  const answers = await inquirer.prompt([
@@ -1994,8 +2171,8 @@ function buildProjectConfig(projectInfo) {
1994
2171
  };
1995
2172
  }
1996
2173
  function displayProjectInfo(info) {
1997
- console.log(chalk2.cyan("\n \u9879\u76EE\u68C0\u6D4B\u7ED3\u679C:"));
1998
- console.log(chalk2.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2174
+ console.log(chalk3.cyan("\n \u9879\u76EE\u68C0\u6D4B\u7ED3\u679C:"));
2175
+ console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1999
2176
  const items = [
2000
2177
  ["\u9879\u76EE\u540D\u79F0", info.name],
2001
2178
  ["\u6846\u67B6", info.frameworkConfidence > 0.5 ? `${info.frameworkDisplayName} (\u7F6E\u4FE1\u5EA6 ${Math.round(info.frameworkConfidence * 100)}%)` : info.frameworkDisplayName],
@@ -2017,8 +2194,8 @@ function displayProjectInfo(info) {
2017
2194
  items.push(["\u7EC4\u4EF6\u76EE\u5F55", info.componentDirs.join(", ")]);
2018
2195
  }
2019
2196
  for (const [label, value] of items) {
2020
- const displayValue = value === true ? chalk2.green("\u2713") : value === false ? chalk2.red("\u2717") : String(value);
2021
- console.log(` ${chalk2.white(label.padEnd(12))} ${displayValue}`);
2197
+ const displayValue = value === true ? chalk3.green("\u2713") : value === false ? chalk3.red("\u2717") : String(value);
2198
+ console.log(` ${chalk3.white(label.padEnd(12))} ${displayValue}`);
2022
2199
  }
2023
2200
  console.log();
2024
2201
  }
@@ -2047,40 +2224,39 @@ function createTestDirectories(config) {
2047
2224
  }
2048
2225
  return dirs;
2049
2226
  }
2050
- function displayResult(configPath, createdDirs, totalTestableFiles, projectInfo) {
2051
- const relativeConfig = path6.relative(process.cwd(), configPath);
2052
- console.log(chalk2.green("\n \u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210!\n"));
2053
- console.log(chalk2.white(" \u5DF2\u751F\u6210\u914D\u7F6E:"));
2054
- console.log(chalk2.gray(` ${relativeConfig}`));
2055
- console.log();
2227
+ function displayResult(createdDirs, totalTestableFiles, projectInfo) {
2228
+ console.log(chalk3.green("\n \u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210!\n"));
2056
2229
  if (createdDirs.length > 0) {
2057
- console.log(chalk2.white(" \u5DF2\u521B\u5EFA\u76EE\u5F55:"));
2230
+ console.log(chalk3.white(" \u5DF2\u521B\u5EFA\u76EE\u5F55:"));
2058
2231
  for (const dir of createdDirs) {
2059
- console.log(chalk2.gray(` ${dir}/`));
2232
+ console.log(chalk3.gray(` ${dir}/`));
2060
2233
  }
2061
2234
  console.log();
2062
2235
  }
2063
- console.log(chalk2.cyan(" \u4E0B\u4E00\u6B65:"));
2236
+ console.log(chalk3.white(" \u914D\u7F6E:"));
2237
+ console.log(chalk3.gray(" \u9ED8\u8BA4\u914D\u7F6E\u5DF2\u5185\u7F6E\uFF0C\u9700\u8981\u81EA\u5B9A\u4E49\u65F6\u521B\u5EFA qat.config.js"));
2238
+ console.log();
2239
+ console.log(chalk3.cyan(" \u4E0B\u4E00\u6B65:"));
2064
2240
  if (totalTestableFiles > 0) {
2065
- console.log(chalk2.gray(` 1. qat create \u9009\u62E9\u6587\u4EF6\u5E76\u751F\u6210\u6D4B\u8BD5\u7528\u4F8B (\u53D1\u73B0 ${totalTestableFiles} \u4E2A\u53EF\u6D4B\u8BD5\u6587\u4EF6)`));
2241
+ console.log(chalk3.gray(` 1. qat create \u9009\u62E9\u6587\u4EF6\u5E76\u751F\u6210\u6D4B\u8BD5\u7528\u4F8B (\u53D1\u73B0 ${totalTestableFiles} \u4E2A\u53EF\u6D4B\u8BD5\u6587\u4EF6)`));
2066
2242
  } else {
2067
- console.log(chalk2.gray(" 1. qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
2243
+ console.log(chalk3.gray(" 1. qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
2068
2244
  }
2069
- console.log(chalk2.gray(" 2. qat run \u6267\u884C\u6D4B\u8BD5"));
2070
- console.log(chalk2.gray(" 3. qat status \u67E5\u770B AI \u6A21\u578B\u72B6\u6001"));
2245
+ console.log(chalk3.gray(" 2. qat run \u6267\u884C\u6D4B\u8BD5"));
2246
+ console.log(chalk3.gray(" 3. qat status \u67E5\u770B AI \u6A21\u578B\u72B6\u6001"));
2071
2247
  if (projectInfo.testFrameworks.length === 0) {
2072
2248
  console.log();
2073
- console.log(chalk2.yellow(" \u26A0 \u672A\u68C0\u6D4B\u5230\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\uFF0C\u8FD0\u884C qat run \u65F6\u4F1A\u63D0\u793A\u5B89\u88C5"));
2249
+ console.log(chalk3.yellow(" \u26A0 \u672A\u68C0\u6D4B\u5230\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\uFF0C\u8FD0\u884C qat run \u65F6\u4F1A\u63D0\u793A\u5B89\u88C5"));
2074
2250
  }
2075
2251
  console.log();
2076
2252
  }
2077
2253
 
2078
2254
  // src/commands/create.ts
2079
- import chalk4 from "chalk";
2255
+ import chalk5 from "chalk";
2080
2256
  import inquirer2 from "inquirer";
2081
2257
  import ora3 from "ora";
2082
2258
  import fs8 from "fs";
2083
- import path8 from "path";
2259
+ import path9 from "path";
2084
2260
 
2085
2261
  // src/services/template.ts
2086
2262
  import fs7 from "fs";
@@ -2129,7 +2305,7 @@ Handlebars.registerHelper("propTestValue", (prop) => {
2129
2305
  };
2130
2306
  return map[prop.type] || "'test-value'";
2131
2307
  });
2132
- function resolveImportPath(testType, targetPath) {
2308
+ function resolveImportPath2(testType, targetPath) {
2133
2309
  if (!targetPath) return targetPath;
2134
2310
  const testDirMap = {
2135
2311
  unit: "tests/unit",
@@ -2155,7 +2331,7 @@ function resolveImportPath(testType, targetPath) {
2155
2331
  function renderTemplate(type, context) {
2156
2332
  const templateContent = loadTemplate(type);
2157
2333
  const template = Handlebars.compile(templateContent);
2158
- const resolvedTarget = resolveImportPath(type, context.target);
2334
+ const resolvedTarget = resolveImportPath2(type, context.target);
2159
2335
  const fullContext = {
2160
2336
  vueVersion: 3,
2161
2337
  typescript: true,
@@ -2537,12 +2713,13 @@ function toPascalCase(str) {
2537
2713
  }
2538
2714
 
2539
2715
  // src/services/test-reviewer.ts
2540
- import chalk3 from "chalk";
2716
+ import chalk4 from "chalk";
2541
2717
  import ora2 from "ora";
2718
+ import path8 from "path";
2542
2719
  var MAX_RETRIES = 3;
2543
2720
  var REVIEW_THRESHOLD = 0.6;
2544
2721
  async function generateWithReview(params) {
2545
- const { testType, targetPath, sourceCode, analysis, aiConfig, framework, onAttempt } = params;
2722
+ const { testType, targetPath, sourceCode, analysis, aiConfig, framework, onAttempt, onProgress } = params;
2546
2723
  const generatorProvider = createAIProvider(aiConfig);
2547
2724
  const reviewerProvider = createAIProvider(aiConfig);
2548
2725
  if (!generatorProvider.capabilities.generateTest) {
@@ -2554,9 +2731,15 @@ async function generateWithReview(params) {
2554
2731
  let lastReview = null;
2555
2732
  let approved = false;
2556
2733
  let attempts = 0;
2734
+ const targetName = path8.basename(targetPath);
2557
2735
  for (let i = 0; i < MAX_RETRIES; i++) {
2558
2736
  attempts = i + 1;
2559
2737
  onAttempt?.(attempts, MAX_RETRIES);
2738
+ const genLabel = attempts > 1 ? `\u27F3 \u91CD\u65B0\u751F\u6210 (${attempts}/${MAX_RETRIES})` : "\u751F\u6210\u6D4B\u8BD5\u4EE3\u7801";
2739
+ onProgress?.(`${genLabel} \u2190 ${targetName}`);
2740
+ if (process.env.QAT_DEBUG === "true") {
2741
+ console.error(chalk4.gray(`[DEBUG] \u2500\u2500\u2500 \u751F\u6210\u8005 AI \u7B2C${attempts}\u6B21\u5C1D\u8BD5 \u2500\u2500\u2500`));
2742
+ }
2560
2743
  const generationPrompt = i === 0 ? void 0 : `\u4E0A\u6B21\u751F\u6210\u7684\u6D4B\u8BD5\u672A\u901A\u8FC7\u5BA1\u8BA1\uFF0C\u8BF7\u6839\u636E\u4EE5\u4E0B\u53CD\u9988\u91CD\u65B0\u751F\u6210\uFF1A
2561
2744
  \u5BA1\u8BA1\u8BC4\u5206: ${(lastReview.score * 100).toFixed(0)}%
2562
2745
  \u5BA1\u8BA1\u610F\u89C1: ${lastReview.feedback}
@@ -2569,16 +2752,18 @@ ${lastReview.suggestions.map((s) => `- ${s}`).join("\n")}
2569
2752
  const generateResponse = await generatorProvider.generateTest({
2570
2753
  type: testType,
2571
2754
  target: targetPath,
2572
- context: i === 0 ? sourceCode : `${sourceCode}
2573
-
2574
- --- \u5BA1\u8BA1\u53CD\u9988 ---
2575
- ${generationPrompt}`,
2755
+ // 重试时:源码已压缩在分析摘要中,context只传审计反馈以减少token
2756
+ context: i === 0 ? sourceCode : generationPrompt,
2576
2757
  framework: framework || void 0,
2577
2758
  analysis
2578
2759
  });
2579
2760
  currentCode = generateResponse.code;
2580
2761
  currentDescription = generateResponse.description;
2581
2762
  currentConfidence = generateResponse.confidence;
2763
+ onProgress?.(`\u5BA1\u8BA1\u5BA1\u67E5 \u2190 ${targetName}`);
2764
+ if (process.env.QAT_DEBUG === "true") {
2765
+ console.error(chalk4.gray(`[DEBUG] \u2500\u2500\u2500 \u5BA1\u8BA1\u5458 AI \u5BA1\u67E5 \u2500\u2500\u2500`));
2766
+ }
2582
2767
  const reviewRequest = {
2583
2768
  target: targetPath,
2584
2769
  sourceCode,
@@ -2610,20 +2795,20 @@ function printReviewReport(report) {
2610
2795
  const approved = report.filter((r) => r.approved);
2611
2796
  const failed = report.filter((r) => !r.approved);
2612
2797
  console.log();
2613
- console.log(chalk3.cyan(" \u5BA1\u8BA1\u62A5\u544A:"));
2614
- console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2798
+ console.log(chalk4.cyan(" \u5BA1\u8BA1\u62A5\u544A:"));
2799
+ console.log(chalk4.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
2615
2800
  for (const entry of approved) {
2616
- console.log(` ${chalk3.green("\u2713")} ${chalk3.gray(entry.target)} ${chalk3.green(`(${(entry.score * 100).toFixed(0)}%`)}${entry.attempts > 1 ? chalk3.yellow(` ${entry.attempts}\u6B21\u5C1D\u8BD5`) : ""})`);
2801
+ console.log(` ${chalk4.green("\u2713")} ${chalk4.gray(entry.target)} ${chalk4.green(`(${(entry.score * 100).toFixed(0)}%`)}${entry.attempts > 1 ? chalk4.yellow(` ${entry.attempts}\u6B21\u5C1D\u8BD5`) : ""})`);
2617
2802
  }
2618
2803
  for (const entry of failed) {
2619
- console.log(` ${chalk3.red("\u2717")} ${chalk3.gray(entry.target)} ${chalk3.red(`(${(entry.score * 100).toFixed(0)}%)`)}`);
2804
+ console.log(` ${chalk4.red("\u2717")} ${chalk4.gray(entry.target)} ${chalk4.red(`(${(entry.score * 100).toFixed(0)}%)`)}`);
2620
2805
  if (entry.issues.length > 0) {
2621
2806
  for (const issue of entry.issues.slice(0, 3)) {
2622
- console.log(chalk3.gray(` - ${issue}`));
2807
+ console.log(chalk4.gray(` - ${issue}`));
2623
2808
  }
2624
2809
  }
2625
2810
  if (entry.feedback) {
2626
- console.log(chalk3.gray(` \u53CD\u9988: ${entry.feedback}`));
2811
+ console.log(chalk4.gray(` \u53CD\u9988: ${entry.feedback}`));
2627
2812
  }
2628
2813
  }
2629
2814
  console.log();
@@ -2651,7 +2836,7 @@ function registerCreateCommand(program2) {
2651
2836
  try {
2652
2837
  await executeCreate(options);
2653
2838
  } catch (error) {
2654
- console.error(chalk4.red(`
2839
+ console.error(chalk5.red(`
2655
2840
  \u2717 ${error instanceof Error ? error.message : String(error)}
2656
2841
  `));
2657
2842
  process.exit(1);
@@ -2676,7 +2861,7 @@ async function executeCreate(options) {
2676
2861
  name: "types",
2677
2862
  message: "\u9009\u62E9\u6D4B\u8BD5\u7C7B\u578B (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
2678
2863
  choices: Object.entries(TEST_TYPE_LABELS).map(([value, label]) => ({
2679
- name: `${label} - ${chalk4.gray(TEST_TYPE_DESCRIPTIONS[value])}`,
2864
+ name: `${label} - ${chalk5.gray(TEST_TYPE_DESCRIPTIONS[value])}`,
2680
2865
  value,
2681
2866
  short: label,
2682
2867
  checked: value === "unit" || value === "component"
@@ -2734,11 +2919,13 @@ async function executeCreate(options) {
2734
2919
  const reviewReportEntries = [];
2735
2920
  for (const testType of types) {
2736
2921
  for (const testTarget of targets) {
2737
- const spinner = ora3(`\u6B63\u5728\u751F\u6210 ${TEST_TYPE_LABELS[testType]} - ${path8.basename(testTarget)}...`).start();
2922
+ const spinner = ora3(`\u6B63\u5728\u751F\u6210 ${TEST_TYPE_LABELS[testType]} - ${path9.basename(testTarget)}...`).start();
2738
2923
  try {
2739
2924
  let content;
2740
2925
  if (useAI && testTarget) {
2741
- const aiResult = await generateWithAI(testType, name, testTarget, config);
2926
+ const aiResult = await generateWithAI(testType, name, testTarget, config, (text) => {
2927
+ spinner.text = `${text}`;
2928
+ });
2742
2929
  content = aiResult.code;
2743
2930
  if (aiResult.reviewEntry) {
2744
2931
  reviewReportEntries.push(aiResult.reviewEntry);
@@ -2779,9 +2966,9 @@ async function executeCreate(options) {
2779
2966
  });
2780
2967
  }
2781
2968
  const outputDir = getTestOutputDir(testType);
2782
- const filePath = path8.join(outputDir, `${name}.${testType === "e2e" ? "spec" : "test"}.ts`);
2969
+ const filePath = path9.join(outputDir, `${name}.${testType === "e2e" ? "spec" : "test"}.ts`);
2783
2970
  if (fs8.existsSync(filePath)) {
2784
- spinner.warn(`\u6D4B\u8BD5\u6587\u4EF6\u5DF2\u5B58\u5728: ${path8.relative(process.cwd(), filePath)}`);
2971
+ spinner.warn(`\u6D4B\u8BD5\u6587\u4EF6\u5DF2\u5B58\u5728: ${path9.relative(process.cwd(), filePath)}`);
2785
2972
  skippedCount++;
2786
2973
  continue;
2787
2974
  }
@@ -2789,11 +2976,11 @@ async function executeCreate(options) {
2789
2976
  fs8.mkdirSync(outputDir, { recursive: true });
2790
2977
  }
2791
2978
  fs8.writeFileSync(filePath, content, "utf-8");
2792
- spinner.succeed(`${TEST_TYPE_LABELS[testType]} - ${path8.basename(testTarget)}`);
2979
+ spinner.succeed(`${TEST_TYPE_LABELS[testType]} - ${path9.basename(testTarget)}`);
2793
2980
  createdFiles.push({ type: testType, filePath });
2794
2981
  } catch (error) {
2795
- spinner.fail(`${TEST_TYPE_LABELS[testType]} - ${path8.basename(testTarget)} \u521B\u5EFA\u5931\u8D25`);
2796
- console.log(chalk4.gray(` ${error instanceof Error ? error.message : String(error)}`));
2982
+ spinner.fail(`${TEST_TYPE_LABELS[testType]} - ${path9.basename(testTarget)} \u521B\u5EFA\u5931\u8D25`);
2983
+ console.log(chalk5.gray(` ${error instanceof Error ? error.message : String(error)}`));
2797
2984
  }
2798
2985
  }
2799
2986
  }
@@ -2807,13 +2994,13 @@ async function selectTargets(types, projectInfo, srcDir) {
2807
2994
  if (types.includes("unit")) {
2808
2995
  const utils = discoverUtilityFiles(process.cwd(), srcDir);
2809
2996
  for (const f of utils.slice(0, 30)) {
2810
- allTargets.push({ name: `${chalk4.gray("[unit]")} ${f}`, value: f });
2997
+ allTargets.push({ name: `${chalk5.gray("[unit]")} ${f}`, value: f });
2811
2998
  }
2812
2999
  }
2813
3000
  if (types.includes("component")) {
2814
3001
  const components = discoverVueComponents(process.cwd(), srcDir);
2815
3002
  for (const f of components.slice(0, 30)) {
2816
- allTargets.push({ name: `${chalk4.gray("[comp]")} ${f}`, value: f });
3003
+ allTargets.push({ name: `${chalk5.gray("[comp]")} ${f}`, value: f });
2817
3004
  }
2818
3005
  }
2819
3006
  if (allTargets.length === 0) {
@@ -2834,7 +3021,7 @@ async function selectTargets(types, projectInfo, srcDir) {
2834
3021
  message: "\u9009\u62E9\u88AB\u6D4B\u76EE\u6807 (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
2835
3022
  choices: [
2836
3023
  ...allTargets,
2837
- { name: chalk4.gray("\u624B\u52A8\u8F93\u5165\u8DEF\u5F84"), value: "__manual__" }
3024
+ { name: chalk5.gray("\u624B\u52A8\u8F93\u5165\u8DEF\u5F84"), value: "__manual__" }
2838
3025
  ],
2839
3026
  validate: (input) => {
2840
3027
  if (input.length === 0) return "\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u76EE\u6807";
@@ -2859,22 +3046,22 @@ async function selectTargets(types, projectInfo, srcDir) {
2859
3046
  }
2860
3047
  function generateDefaultName(type, target) {
2861
3048
  if (!target) return `${type}-test`;
2862
- const basename = path8.basename(target, path8.extname(target));
3049
+ const basename = path9.basename(target, path9.extname(target));
2863
3050
  const cleaned = basename.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2864
3051
  return cleaned || `${type}-test`;
2865
3052
  }
2866
- async function generateWithAI(type, name, target, config) {
3053
+ async function generateWithAI(type, name, target, config, onProgress) {
2867
3054
  const provider = getAIProvider(config.ai);
2868
3055
  if (!provider.capabilities.generateTest) {
2869
3056
  throw new Error("\u5F53\u524D AI Provider \u4E0D\u652F\u6301\u6D4B\u8BD5\u751F\u6210");
2870
3057
  }
2871
3058
  let sourceCode = "";
2872
- const fullPath = path8.resolve(process.cwd(), target);
3059
+ const fullPath = path9.resolve(process.cwd(), target);
2873
3060
  if (fs8.existsSync(fullPath)) {
2874
3061
  sourceCode = fs8.readFileSync(fullPath, "utf-8");
2875
3062
  }
2876
3063
  const analysis = analyzeFile(target);
2877
- const analysisSummary = analysis.exports.length > 0 || analysis.vueAnalysis ? {
3064
+ const analysisSummary = analysis.exports.length > 0 || analysis.vueAnalysis || analysis.importSignatures.length > 0 ? {
2878
3065
  exports: analysis.exports.map((e) => ({
2879
3066
  name: e.name,
2880
3067
  kind: e.kind,
@@ -2891,7 +3078,8 @@ async function generateWithAI(type, name, target, config) {
2891
3078
  params: e.params
2892
3079
  })),
2893
3080
  methods: analysis.vueAnalysis?.methods,
2894
- computed: analysis.vueAnalysis?.computed
3081
+ computed: analysis.vueAnalysis?.computed,
3082
+ importSignatures: analysis.importSignatures.length > 0 ? analysis.importSignatures : void 0
2895
3083
  } : void 0;
2896
3084
  const reviewResult = await generateWithReview({
2897
3085
  testType: type,
@@ -2899,7 +3087,8 @@ async function generateWithAI(type, name, target, config) {
2899
3087
  sourceCode,
2900
3088
  analysis: analysisSummary,
2901
3089
  aiConfig: config.ai,
2902
- framework: "vue"
3090
+ framework: "vue",
3091
+ onProgress
2903
3092
  });
2904
3093
  const headerComment = [
2905
3094
  `// AI Generated Test - ${name}`,
@@ -2933,46 +3122,46 @@ function getTestOutputDir(type) {
2933
3122
  }
2934
3123
  function displayCreateResult(createdFiles, skippedCount, usedAI) {
2935
3124
  if (createdFiles.length === 0 && skippedCount === 0) {
2936
- console.log(chalk4.yellow("\n \u6CA1\u6709\u521B\u5EFA\u4EFB\u4F55\u6D4B\u8BD5\u6587\u4EF6\n"));
3125
+ console.log(chalk5.yellow("\n \u6CA1\u6709\u521B\u5EFA\u4EFB\u4F55\u6D4B\u8BD5\u6587\u4EF6\n"));
2937
3126
  return;
2938
3127
  }
2939
- console.log(chalk4.green(`
3128
+ console.log(chalk5.green(`
2940
3129
  \u2713 \u5DF2\u521B\u5EFA ${createdFiles.length} \u4E2A\u6D4B\u8BD5\u6587\u4EF6`));
2941
3130
  if (skippedCount > 0) {
2942
- console.log(chalk4.yellow(` \u26A0 \u8DF3\u8FC7 ${skippedCount} \u4E2A\u5DF2\u5B58\u5728\u7684\u6587\u4EF6`));
3131
+ console.log(chalk5.yellow(` \u26A0 \u8DF3\u8FC7 ${skippedCount} \u4E2A\u5DF2\u5B58\u5728\u7684\u6587\u4EF6`));
2943
3132
  }
2944
3133
  console.log();
2945
3134
  for (const { type, filePath } of createdFiles) {
2946
- const relativePath = path8.relative(process.cwd(), filePath);
2947
- console.log(chalk4.white(` ${chalk4.cyan(TEST_TYPE_LABELS[type])} ${chalk4.gray(relativePath)}`));
3135
+ const relativePath = path9.relative(process.cwd(), filePath);
3136
+ console.log(chalk5.white(` ${chalk5.cyan(TEST_TYPE_LABELS[type])} ${chalk5.gray(relativePath)}`));
2948
3137
  }
2949
3138
  if (usedAI) {
2950
3139
  console.log();
2951
- console.log(chalk4.magenta(" \u751F\u6210\u65B9\u5F0F: AI \u8F85\u52A9"));
3140
+ console.log(chalk5.magenta(" \u751F\u6210\u65B9\u5F0F: AI \u8F85\u52A9"));
2952
3141
  }
2953
3142
  console.log();
2954
- console.log(chalk4.gray(" \u8FD0\u884C\u6D4B\u8BD5:"));
3143
+ console.log(chalk5.gray(" \u8FD0\u884C\u6D4B\u8BD5:"));
2955
3144
  const uniqueTypes = [...new Set(createdFiles.map((f) => f.type))];
2956
3145
  if (uniqueTypes.length === 1) {
2957
- console.log(chalk4.cyan(` qat run -t ${uniqueTypes[0]}`));
3146
+ console.log(chalk5.cyan(` qat run -t ${uniqueTypes[0]}`));
2958
3147
  } else {
2959
- console.log(chalk4.cyan(` qat run -t ${uniqueTypes.join(" -t ")}`));
2960
- console.log(chalk4.gray(" # \u6216\u8FD0\u884C\u5168\u90E8"));
2961
- console.log(chalk4.cyan(" qat run"));
3148
+ console.log(chalk5.cyan(` qat run -t ${uniqueTypes.join(" -t ")}`));
3149
+ console.log(chalk5.gray(" # \u6216\u8FD0\u884C\u5168\u90E8"));
3150
+ console.log(chalk5.cyan(" qat run"));
2962
3151
  }
2963
3152
  console.log();
2964
3153
  }
2965
3154
 
2966
3155
  // src/commands/run.ts
2967
- import chalk5 from "chalk";
3156
+ import chalk6 from "chalk";
2968
3157
  import inquirer3 from "inquirer";
2969
3158
  import ora4 from "ora";
2970
3159
  import fs11 from "fs";
2971
- import path12 from "path";
3160
+ import path13 from "path";
2972
3161
 
2973
3162
  // src/runners/vitest-runner.ts
2974
3163
  import { execFile } from "child_process";
2975
- import path9 from "path";
3164
+ import path10 from "path";
2976
3165
  import fs9 from "fs";
2977
3166
  import os2 from "os";
2978
3167
  var isVerbose = () => process.env.QAT_VERBOSE === "true";
@@ -3054,7 +3243,7 @@ function buildVitestArgs(options) {
3054
3243
  return args;
3055
3244
  }
3056
3245
  async function execVitest(args) {
3057
- const tmpFile = path9.join(os2.tmpdir(), `qat-vitest-result-${Date.now()}.json`);
3246
+ const tmpFile = path10.join(os2.tmpdir(), `qat-vitest-result-${Date.now()}.json`);
3058
3247
  const argsWithOutput = [...args, "--outputFile", tmpFile];
3059
3248
  return new Promise((resolve, reject) => {
3060
3249
  const npx = process.platform === "win32" ? "npx.cmd" : "npx";
@@ -3133,7 +3322,7 @@ function parseVitestJSON(jsonStr) {
3133
3322
  for (const fileResult of data.testResults) {
3134
3323
  const suiteTests = parseTestResults(fileResult);
3135
3324
  suites.push({
3136
- name: path9.basename(fileResult.name || "unknown"),
3325
+ name: path10.basename(fileResult.name || "unknown"),
3137
3326
  file: fileResult.name || "unknown",
3138
3327
  type: "unit",
3139
3328
  status: mapVitestStatus(fileResult.status),
@@ -3244,7 +3433,7 @@ function parseFromStdout(output) {
3244
3433
  if (data.testResults && Array.isArray(data.testResults)) {
3245
3434
  for (const fileResult of data.testResults) {
3246
3435
  suites.push({
3247
- name: path9.basename(fileResult.name || "unknown"),
3436
+ name: path10.basename(fileResult.name || "unknown"),
3248
3437
  file: fileResult.name || "unknown",
3249
3438
  type: "unit",
3250
3439
  status: mapVitestStatus(fileResult.status),
@@ -3280,7 +3469,7 @@ function parseVitestTextOutput(output, hasError) {
3280
3469
  if (isPassed) totalPassed += testCount;
3281
3470
  else totalFailed += testCount;
3282
3471
  suites.push({
3283
- name: path9.basename(file),
3472
+ name: path10.basename(file),
3284
3473
  file,
3285
3474
  type: "unit",
3286
3475
  status: isPassed ? "passed" : "failed",
@@ -3381,7 +3570,7 @@ function extractCoverage(coverageMap) {
3381
3570
 
3382
3571
  // src/runners/playwright-runner.ts
3383
3572
  import { execFile as execFile2 } from "child_process";
3384
- import path10 from "path";
3573
+ import path11 from "path";
3385
3574
  async function runPlaywright(options) {
3386
3575
  const startTime = Date.now();
3387
3576
  const args = buildPlaywrightArgs(options);
@@ -3581,7 +3770,7 @@ function parsePlaywrightTextOutput(output, hasError) {
3581
3770
  let existingSuite = suites.find((s) => s.file === file);
3582
3771
  if (!existingSuite) {
3583
3772
  existingSuite = {
3584
- name: path10.basename(file),
3773
+ name: path11.basename(file),
3585
3774
  file,
3586
3775
  type: "e2e",
3587
3776
  status: "passed",
@@ -3816,7 +4005,7 @@ function calculateAverageMetrics(results) {
3816
4005
 
3817
4006
  // src/services/reporter.ts
3818
4007
  import fs10 from "fs";
3819
- import path11 from "path";
4008
+ import path12 from "path";
3820
4009
  function aggregateResults(results) {
3821
4010
  const summary = {
3822
4011
  total: 0,
@@ -4012,13 +4201,13 @@ function generateMDReport(data) {
4012
4201
  }
4013
4202
  function writeReportToDisk(data, outputDir) {
4014
4203
  const md = generateMDReport(data);
4015
- const dir = path11.resolve(outputDir);
4204
+ const dir = path12.resolve(outputDir);
4016
4205
  if (!fs10.existsSync(dir)) {
4017
4206
  fs10.mkdirSync(dir, { recursive: true });
4018
4207
  }
4019
- const mdPath = path11.join(dir, "report.md");
4208
+ const mdPath = path12.join(dir, "report.md");
4020
4209
  fs10.writeFileSync(mdPath, md, "utf-8");
4021
- const jsonPath = path11.join(dir, "report.json");
4210
+ const jsonPath = path12.join(dir, "report.json");
4022
4211
  fs10.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf-8");
4023
4212
  return mdPath;
4024
4213
  }
@@ -4055,7 +4244,7 @@ function registerRunCommand(program2) {
4055
4244
  try {
4056
4245
  await executeRun(options);
4057
4246
  } catch (error) {
4058
- console.error(chalk5.red(`
4247
+ console.error(chalk6.red(`
4059
4248
  \u2717 ${error instanceof Error ? error.message : String(error)}
4060
4249
  `));
4061
4250
  process.exit(1);
@@ -4075,15 +4264,15 @@ async function executeRun(options) {
4075
4264
  }
4076
4265
  let typesToRun = await determineTypesToRun(type, file, config);
4077
4266
  if (typesToRun.length === 0) {
4078
- console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u8BF7\u5728 qat.config.ts \u4E2D\u542F\u7528\uFF09\n"));
4267
+ console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u8BF7\u5728 qat.config.ts \u4E2D\u542F\u7528\uFF09\n"));
4079
4268
  return;
4080
4269
  }
4081
4270
  const missingDeps = checkTestDependencies(typesToRun);
4082
4271
  if (missingDeps.length > 0) {
4083
- console.log(chalk5.yellow("\n \u26A0 \u4EE5\u4E0B\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\u672A\u5B89\u88C5:\n"));
4272
+ console.log(chalk6.yellow("\n \u26A0 \u4EE5\u4E0B\u6D4B\u8BD5\u6846\u67B6\u4F9D\u8D56\u672A\u5B89\u88C5:\n"));
4084
4273
  for (const dep of missingDeps) {
4085
- console.log(chalk5.white(` ${dep.runner} (${dep.pkg})`));
4086
- console.log(chalk5.gray(` \u5B89\u88C5\u547D\u4EE4: ${chalk5.cyan(dep.installCmd)}`));
4274
+ console.log(chalk6.white(` ${dep.runner} (${dep.pkg})`));
4275
+ console.log(chalk6.gray(` \u5B89\u88C5\u547D\u4EE4: ${chalk6.cyan(dep.installCmd)}`));
4087
4276
  }
4088
4277
  console.log();
4089
4278
  const { action } = await inquirer3.prompt([
@@ -4100,13 +4289,13 @@ async function executeRun(options) {
4100
4289
  }
4101
4290
  ]);
4102
4291
  if (action === "cancel") {
4103
- console.log(chalk5.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4292
+ console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4104
4293
  return;
4105
4294
  }
4106
4295
  if (action === "install") {
4107
4296
  const installed = await installTestDependencies(missingDeps);
4108
4297
  if (!installed) {
4109
- console.log(chalk5.yellow(" \u90E8\u5206\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF0C\u5C06\u8DF3\u8FC7\u5BF9\u5E94\u7684\u6D4B\u8BD5\u7C7B\u578B"));
4298
+ console.log(chalk6.yellow(" \u90E8\u5206\u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF0C\u5C06\u8DF3\u8FC7\u5BF9\u5E94\u7684\u6D4B\u8BD5\u7C7B\u578B"));
4110
4299
  }
4111
4300
  const stillMissing = checkTestDependencies(typesToRun);
4112
4301
  if (stillMissing.length > 0) {
@@ -4116,7 +4305,7 @@ async function executeRun(options) {
4116
4305
  return !dep || !missingRunners.has(dep.pkg);
4117
4306
  });
4118
4307
  if (typesToRun.length === 0) {
4119
- console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u4F9D\u8D56\u672A\u5B89\u88C5\uFF09\n"));
4308
+ console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\uFF08\u4F9D\u8D56\u672A\u5B89\u88C5\uFF09\n"));
4120
4309
  return;
4121
4310
  }
4122
4311
  }
@@ -4128,7 +4317,7 @@ async function executeRun(options) {
4128
4317
  return !dep || !missingRunners.has(dep.pkg);
4129
4318
  });
4130
4319
  if (typesToRun.length === 0) {
4131
- console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4320
+ console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4132
4321
  return;
4133
4322
  }
4134
4323
  }
@@ -4151,13 +4340,13 @@ async function executeRun(options) {
4151
4340
  }
4152
4341
  ]);
4153
4342
  if (action === "cancel") {
4154
- console.log(chalk5.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4343
+ console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4155
4344
  return;
4156
4345
  }
4157
4346
  if (action === "skip") {
4158
4347
  typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
4159
4348
  if (typesToRun.length === 0) {
4160
- console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4349
+ console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4161
4350
  return;
4162
4351
  }
4163
4352
  }
@@ -4177,12 +4366,12 @@ async function executeRun(options) {
4177
4366
  }
4178
4367
  ]);
4179
4368
  if (fallback === "cancel") {
4180
- console.log(chalk5.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4369
+ console.log(chalk6.gray("\n \u5DF2\u53D6\u6D88\u8FD0\u884C\n"));
4181
4370
  return;
4182
4371
  }
4183
4372
  typesToRun = typesToRun.filter((t) => !SERVER_REQUIRED_TYPES.includes(t));
4184
4373
  if (typesToRun.length === 0) {
4185
- console.log(chalk5.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4374
+ console.log(chalk6.yellow("\n \u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B\n"));
4186
4375
  return;
4187
4376
  }
4188
4377
  }
@@ -4239,9 +4428,9 @@ async function executeRun(options) {
4239
4428
  const reportData = aggregateResults(results);
4240
4429
  const outputDir = config.report.outputDir || "qat-report";
4241
4430
  const reportPath = writeReportToDisk(reportData, outputDir);
4242
- const relativePath = path12.relative(process.cwd(), reportPath);
4243
- console.log(chalk5.gray(`
4244
- \u62A5\u544A\u5DF2\u751F\u6210: ${chalk5.cyan(relativePath)}`));
4431
+ const relativePath = path13.relative(process.cwd(), reportPath);
4432
+ console.log(chalk6.gray(`
4433
+ \u62A5\u544A\u5DF2\u751F\u6210: ${chalk6.cyan(relativePath)}`));
4245
4434
  console.log();
4246
4435
  const hasFailures = results.some((r) => r.status === "failed");
4247
4436
  if (hasFailures && isAIAvailable(config.ai)) {
@@ -4250,7 +4439,7 @@ async function executeRun(options) {
4250
4439
  }
4251
4440
  function printDryRunCommands(types, options, config) {
4252
4441
  console.log();
4253
- console.log(chalk5.cyan(" \u53EF\u6267\u884C\u7684\u6D4B\u8BD5\u547D\u4EE4:\n"));
4442
+ console.log(chalk6.cyan(" \u53EF\u6267\u884C\u7684\u6D4B\u8BD5\u547D\u4EE4:\n"));
4254
4443
  for (const testType of types) {
4255
4444
  const label = TYPE_LABELS2[testType];
4256
4445
  const runner = TYPE_RUNNERS[testType];
@@ -4287,17 +4476,17 @@ function printDryRunCommands(types, options, config) {
4287
4476
  default:
4288
4477
  cmd = `# ${label}: \u672A\u77E5\u8FD0\u884C\u5668`;
4289
4478
  }
4290
- console.log(chalk5.white(` ${label}:`));
4291
- console.log(chalk5.cyan(` ${cmd}`));
4479
+ console.log(chalk6.white(` ${label}:`));
4480
+ console.log(chalk6.cyan(` ${cmd}`));
4292
4481
  console.log();
4293
4482
  }
4294
- console.log(chalk5.gray(" \u6216\u4F7F\u7528 QAT \u7EDF\u4E00\u8FD0\u884C:"));
4483
+ console.log(chalk6.gray(" \u6216\u4F7F\u7528 QAT \u7EDF\u4E00\u8FD0\u884C:"));
4295
4484
  if (types.length === 1) {
4296
- console.log(chalk5.cyan(` qat run -t ${types[0]}`));
4485
+ console.log(chalk6.cyan(` qat run -t ${types[0]}`));
4297
4486
  } else {
4298
- console.log(chalk5.cyan(` qat run -t ${types.join(" -t ")}`));
4299
- console.log(chalk5.gray(" # \u5E76\u884C\u8FD0\u884C"));
4300
- console.log(chalk5.cyan(" qat run -p"));
4487
+ console.log(chalk6.cyan(` qat run -t ${types.join(" -t ")}`));
4488
+ console.log(chalk6.gray(" # \u5E76\u884C\u8FD0\u884C"));
4489
+ console.log(chalk6.cyan(" qat run -p"));
4301
4490
  }
4302
4491
  console.log();
4303
4492
  }
@@ -4320,7 +4509,7 @@ async function determineTypesToRun(type, file, config) {
4320
4509
  name: "selectedTypes",
4321
4510
  message: "\u9009\u62E9\u8981\u8FD0\u884C\u7684\u6D4B\u8BD5\u7C7B\u578B (\u7A7A\u683C\u9009\u62E9/\u53D6\u6D88\uFF0C\u56DE\u8F66\u786E\u8BA4):",
4322
4511
  choices: enabledTypes.map((t) => ({
4323
- name: `${TYPE_LABELS2[t]} (${chalk5.gray(TYPE_RUNNERS[t])})`,
4512
+ name: `${TYPE_LABELS2[t]} (${chalk6.gray(TYPE_RUNNERS[t])})`,
4324
4513
  value: t,
4325
4514
  checked: true
4326
4515
  })),
@@ -4380,7 +4569,7 @@ async function runTestType(testType, config, options) {
4380
4569
  }
4381
4570
  }
4382
4571
  async function runWatchMode(_config, options) {
4383
- console.log(chalk5.cyan(" \u76D1\u542C\u6A21\u5F0F\u5DF2\u542F\u52A8 (Ctrl+C \u9000\u51FA)\n"));
4572
+ console.log(chalk6.cyan(" \u76D1\u542C\u6A21\u5F0F\u5DF2\u542F\u52A8 (Ctrl+C \u9000\u51FA)\n"));
4384
4573
  const { spawn } = await import("child_process");
4385
4574
  const args = ["vitest", "--watch"];
4386
4575
  if (options.file) {
@@ -4404,7 +4593,7 @@ async function runWatchMode(_config, options) {
4404
4593
  shell: true
4405
4594
  });
4406
4595
  child.on("error", (err) => {
4407
- console.error(chalk5.red(`
4596
+ console.error(chalk6.red(`
4408
4597
  Vitest \u542F\u52A8\u5931\u8D25: ${err.message}
4409
4598
  `));
4410
4599
  });
@@ -4459,17 +4648,17 @@ function displayJestStyleResults(results) {
4459
4648
  for (const result of results) {
4460
4649
  const typeLabel = TYPE_LABELS2[result.type] || result.type;
4461
4650
  if (result.suites.length === 0) {
4462
- console.log(chalk5.gray(` ${typeLabel}: \u65E0\u6D4B\u8BD5\u7ED3\u679C`));
4651
+ console.log(chalk6.gray(` ${typeLabel}: \u65E0\u6D4B\u8BD5\u7ED3\u679C`));
4463
4652
  continue;
4464
4653
  }
4465
4654
  for (const suite of result.suites) {
4466
4655
  if (suite.tests.length === 0) continue;
4467
- const suiteIcon = suite.status === "passed" ? chalk5.green("PASS") : chalk5.red("FAIL");
4468
- console.log(` ${suiteIcon} ${chalk5.white(suite.name)} ${chalk5.gray(`(${formatDuration2(suite.duration)})`)}`);
4656
+ const suiteIcon = suite.status === "passed" ? chalk6.green("PASS") : chalk6.red("FAIL");
4657
+ console.log(` ${suiteIcon} ${chalk6.white(suite.name)} ${chalk6.gray(`(${formatDuration2(suite.duration)})`)}`);
4469
4658
  for (const test of suite.tests) {
4470
- const icon = test.status === "passed" ? chalk5.green(" \u2713") : test.status === "failed" ? chalk5.red(" \u2715") : chalk5.yellow(" \u25CB");
4471
- const name = test.status === "failed" ? chalk5.red(test.name) : test.name;
4472
- console.log(` ${icon} ${name} ${chalk5.gray(formatDuration2(test.duration))}`);
4659
+ const icon = test.status === "passed" ? chalk6.green(" \u2713") : test.status === "failed" ? chalk6.red(" \u2715") : chalk6.yellow(" \u25CB");
4660
+ const name = test.status === "failed" ? chalk6.red(test.name) : test.name;
4661
+ console.log(` ${icon} ${name} ${chalk6.gray(formatDuration2(test.duration))}`);
4473
4662
  }
4474
4663
  }
4475
4664
  console.log();
@@ -4501,28 +4690,28 @@ function displayJestStyleResults(results) {
4501
4690
  }
4502
4691
  }
4503
4692
  const total = totalPassed + totalFailed + totalSkipped;
4504
- console.log(chalk5.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4505
- console.log(chalk5.white(" \u6D4B\u8BD5\u7ED3\u679C\u6C47\u603B"));
4506
- console.log(chalk5.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4693
+ console.log(chalk6.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4694
+ console.log(chalk6.white(" \u6D4B\u8BD5\u7ED3\u679C\u6C47\u603B"));
4695
+ console.log(chalk6.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
4507
4696
  console.log();
4508
- console.log(` ${chalk5.white("\u7C7B\u578B".padEnd(14))} ${chalk5.white("\u901A\u8FC7".padStart(6))} ${chalk5.white("\u5931\u8D25".padStart(6))} ${chalk5.white("\u8DF3\u8FC7".padStart(6))} ${chalk5.white("\u603B\u8BA1".padStart(6))} ${chalk5.white("\u901A\u8FC7\u7387".padStart(8))} ${chalk5.white("\u8017\u65F6".padStart(8))}`);
4697
+ console.log(` ${chalk6.white("\u7C7B\u578B".padEnd(14))} ${chalk6.white("\u901A\u8FC7".padStart(6))} ${chalk6.white("\u5931\u8D25".padStart(6))} ${chalk6.white("\u8DF3\u8FC7".padStart(6))} ${chalk6.white("\u603B\u8BA1".padStart(6))} ${chalk6.white("\u901A\u8FC7\u7387".padStart(8))} ${chalk6.white("\u8017\u65F6".padStart(8))}`);
4509
4698
  console.log(` ${"\u2500".repeat(14)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)}`);
4510
4699
  for (const [type, stats] of Object.entries(typeStats)) {
4511
4700
  const label = (TYPE_LABELS2[type] || type).padEnd(14);
4512
4701
  const typeTotal = stats.passed + stats.failed + stats.skipped;
4513
4702
  const rate = typeTotal > 0 ? (stats.passed / typeTotal * 100).toFixed(0) + "%" : "-";
4514
- const rateColored = typeTotal > 0 && stats.passed === typeTotal ? chalk5.green(rate.padStart(8)) : stats.failed > 0 ? chalk5.red(rate.padStart(8)) : rate.padStart(8);
4703
+ const rateColored = typeTotal > 0 && stats.passed === typeTotal ? chalk6.green(rate.padStart(8)) : stats.failed > 0 ? chalk6.red(rate.padStart(8)) : rate.padStart(8);
4515
4704
  console.log(` ${label} ${String(stats.passed).padStart(6)} ${String(stats.failed).padStart(6)} ${String(stats.skipped).padStart(6)} ${String(typeTotal).padStart(6)} ${rateColored} ${formatDuration2(stats.duration).padStart(8)}`);
4516
4705
  }
4517
4706
  console.log(` ${"\u2500".repeat(14)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(6)} ${"\u2500".repeat(8)} ${"\u2500".repeat(8)}`);
4518
4707
  const totalRate = total > 0 ? (totalPassed / total * 100).toFixed(0) + "%" : "-";
4519
- const totalRateColored = total > 0 && totalFailed === 0 ? chalk5.green(totalRate.padStart(8)) : totalFailed > 0 ? chalk5.red(totalRate.padStart(8)) : totalRate.padStart(8);
4708
+ const totalRateColored = total > 0 && totalFailed === 0 ? chalk6.green(totalRate.padStart(8)) : totalFailed > 0 ? chalk6.red(totalRate.padStart(8)) : totalRate.padStart(8);
4520
4709
  console.log(` ${"\u603B\u8BA1".padEnd(14)} ${String(totalPassed).padStart(6)} ${String(totalFailed).padStart(6)} ${String(totalSkipped).padStart(6)} ${String(total).padStart(6)} ${totalRateColored} ${formatDuration2(totalDuration).padStart(8)}`);
4521
4710
  console.log();
4522
4711
  for (const result of results) {
4523
4712
  if (result.coverage) {
4524
4713
  const c = result.coverage;
4525
- console.log(chalk5.cyan(" \u8986\u76D6\u7387:"));
4714
+ console.log(chalk6.cyan(" \u8986\u76D6\u7387:"));
4526
4715
  console.log(` \u8BED\u53E5: ${coverageColor(c.statements)} \u5206\u652F: ${coverageColor(c.branches)} \u51FD\u6570: ${coverageColor(c.functions)} \u884C: ${coverageColor(c.lines)}`);
4527
4716
  console.log();
4528
4717
  }
@@ -4530,7 +4719,7 @@ function displayJestStyleResults(results) {
4530
4719
  for (const result of results) {
4531
4720
  if (result.type === "performance" && result.performance) {
4532
4721
  const p = result.performance;
4533
- console.log(chalk5.cyan(" \u6027\u80FD\u6307\u6807:"));
4722
+ console.log(chalk6.cyan(" \u6027\u80FD\u6307\u6807:"));
4534
4723
  console.log(` Performance: ${scoreColor(p.performance)} ${p.performance}/100`);
4535
4724
  console.log(` Accessibility: ${scoreColor(p.accessibility)} ${p.accessibility}/100`);
4536
4725
  console.log(` Best Practices: ${scoreColor(p.bestPractices)} ${p.bestPractices}/100`);
@@ -4547,31 +4736,31 @@ function displayJestStyleResults(results) {
4547
4736
  )
4548
4737
  );
4549
4738
  if (failedTests.length > 0) {
4550
- console.log(chalk5.red(" \u5931\u8D25\u8BE6\u60C5:"));
4739
+ console.log(chalk6.red(" \u5931\u8D25\u8BE6\u60C5:"));
4551
4740
  for (const { suite, test } of failedTests) {
4552
- console.log(chalk5.red(` \u2715 ${suite.name} > ${test.name}`));
4741
+ console.log(chalk6.red(` \u2715 ${suite.name} > ${test.name}`));
4553
4742
  if (test.error?.message) {
4554
- console.log(chalk5.gray(` ${test.error.message.split("\n")[0]}`));
4743
+ console.log(chalk6.gray(` ${test.error.message.split("\n")[0]}`));
4555
4744
  }
4556
4745
  }
4557
4746
  console.log();
4558
4747
  }
4559
4748
  if (totalFailed > 0) {
4560
- console.log(chalk5.red(` Tests: ${totalFailed} failed, ${totalPassed} passed, ${total} total`));
4749
+ console.log(chalk6.red(` Tests: ${totalFailed} failed, ${totalPassed} passed, ${total} total`));
4561
4750
  process.exitCode = 1;
4562
4751
  } else if (total === 0) {
4563
- console.log(chalk5.yellow(" \u6CA1\u6709\u53D1\u73B0\u6D4B\u8BD5\u7528\u4F8B"));
4752
+ console.log(chalk6.yellow(" \u6CA1\u6709\u53D1\u73B0\u6D4B\u8BD5\u7528\u4F8B"));
4564
4753
  } else {
4565
- console.log(chalk5.green(` Tests: ${totalPassed} passed, ${total} total`));
4754
+ console.log(chalk6.green(` Tests: ${totalPassed} passed, ${total} total`));
4566
4755
  }
4567
- console.log(chalk5.gray(` Time: ${formatDuration2(totalDuration)}`));
4756
+ console.log(chalk6.gray(` Time: ${formatDuration2(totalDuration)}`));
4568
4757
  console.log();
4569
4758
  }
4570
4759
  function coverageColor(value) {
4571
4760
  const pct3 = `${(value * 100).toFixed(1)}%`;
4572
- if (value >= 0.8) return chalk5.green(pct3);
4573
- if (value >= 0.5) return chalk5.yellow(pct3);
4574
- return chalk5.red(pct3);
4761
+ if (value >= 0.8) return chalk6.green(pct3);
4762
+ if (value >= 0.5) return chalk6.yellow(pct3);
4763
+ return chalk6.red(pct3);
4575
4764
  }
4576
4765
  async function aiAnalyzeFailures(results, aiConfig) {
4577
4766
  const failedTests = results.flatMap(
@@ -4580,23 +4769,23 @@ async function aiAnalyzeFailures(results, aiConfig) {
4580
4769
  )
4581
4770
  );
4582
4771
  if (failedTests.length === 0) return;
4583
- console.log(chalk5.magenta(" \u{1F916} AI \u5206\u6790\u5931\u8D25\u539F\u56E0..."));
4772
+ console.log(chalk6.magenta(" \u{1F916} AI \u5206\u6790\u5931\u8D25\u539F\u56E0..."));
4584
4773
  console.log();
4585
4774
  const provider = getAIProvider(aiConfig);
4586
4775
  if (!provider.capabilities.suggestFix) return;
4587
4776
  for (const { suite, test } of failedTests.slice(0, 5)) {
4588
4777
  try {
4589
4778
  const suggestions = await provider.suggestFix(test.error);
4590
- console.log(chalk5.white(` ${suite.name} > ${test.name}`));
4779
+ console.log(chalk6.white(` ${suite.name} > ${test.name}`));
4591
4780
  if (test.error?.message) {
4592
- console.log(chalk5.gray(` \u9519\u8BEF: ${test.error.message.split("\n")[0]}`));
4781
+ console.log(chalk6.gray(` \u9519\u8BEF: ${test.error.message.split("\n")[0]}`));
4593
4782
  }
4594
4783
  for (const suggestion of suggestions.slice(0, 3)) {
4595
- console.log(chalk5.cyan(` \u2192 ${suggestion}`));
4784
+ console.log(chalk6.cyan(` \u2192 ${suggestion}`));
4596
4785
  }
4597
4786
  console.log();
4598
4787
  } catch (error) {
4599
- console.log(chalk5.gray(` AI \u5206\u6790\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`));
4788
+ console.log(chalk6.gray(` AI \u5206\u6790\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`));
4600
4789
  }
4601
4790
  }
4602
4791
  }
@@ -4608,9 +4797,9 @@ function formatDuration2(ms) {
4608
4797
  return `${min}m ${sec}s`;
4609
4798
  }
4610
4799
  function scoreColor(score) {
4611
- if (score >= 90) return chalk5.green("\u25CF");
4612
- if (score >= 50) return chalk5.yellow("\u25CF");
4613
- return chalk5.red("\u25CF");
4800
+ if (score >= 90) return chalk6.green("\u25CF");
4801
+ if (score >= 50) return chalk6.yellow("\u25CF");
4802
+ return chalk6.red("\u25CF");
4614
4803
  }
4615
4804
  async function checkDevServer(config) {
4616
4805
  const baseUrl = config.playwright.baseURL || "http://localhost:5173";
@@ -4626,10 +4815,10 @@ async function checkDevServer(config) {
4626
4815
  }
4627
4816
  async function startDevServer(config) {
4628
4817
  const { spawn } = await import("child_process");
4629
- const hasPnpm = fs11.existsSync(path12.join(process.cwd(), "pnpm-lock.yaml"));
4630
- const hasYarn = fs11.existsSync(path12.join(process.cwd(), "yarn.lock"));
4818
+ const hasPnpm = fs11.existsSync(path13.join(process.cwd(), "pnpm-lock.yaml"));
4819
+ const hasYarn = fs11.existsSync(path13.join(process.cwd(), "yarn.lock"));
4631
4820
  const pkgCmd = hasPnpm ? "pnpm" : hasYarn ? "yarn" : "npm";
4632
- console.log(chalk5.cyan(` \u6B63\u5728\u542F\u52A8 dev server (${pkgCmd} run dev) ...`));
4821
+ console.log(chalk6.cyan(` \u6B63\u5728\u542F\u52A8 dev server (${pkgCmd} run dev) ...`));
4633
4822
  const child = spawn(pkgCmd, ["run", "dev"], {
4634
4823
  cwd: process.cwd(),
4635
4824
  stdio: "pipe",
@@ -4645,24 +4834,24 @@ async function startDevServer(config) {
4645
4834
  waited += interval;
4646
4835
  const isUp = await checkDevServer(config);
4647
4836
  if (isUp) {
4648
- console.log(chalk5.green(` \u2713 dev server \u5DF2\u542F\u52A8 (${baseUrl})`));
4837
+ console.log(chalk6.green(` \u2713 dev server \u5DF2\u542F\u52A8 (${baseUrl})`));
4649
4838
  child.unref();
4650
4839
  return true;
4651
4840
  }
4652
4841
  }
4653
4842
  child.kill();
4654
- console.log(chalk5.red(" \u2717 dev server \u542F\u52A8\u8D85\u65F6"));
4843
+ console.log(chalk6.red(" \u2717 dev server \u542F\u52A8\u8D85\u65F6"));
4655
4844
  return false;
4656
4845
  }
4657
4846
  function saveRunResults(results) {
4658
4847
  if (results.length === 0) return;
4659
- const resultsPath = path12.join(process.cwd(), RESULTS_DIR);
4848
+ const resultsPath = path13.join(process.cwd(), RESULTS_DIR);
4660
4849
  if (!fs11.existsSync(resultsPath)) {
4661
4850
  fs11.mkdirSync(resultsPath, { recursive: true });
4662
4851
  }
4663
4852
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4664
4853
  const fileName = `result-${timestamp}.json`;
4665
- const filePath = path12.join(resultsPath, fileName);
4854
+ const filePath = path13.join(resultsPath, fileName);
4666
4855
  const data = {
4667
4856
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4668
4857
  results
@@ -4672,13 +4861,13 @@ function saveRunResults(results) {
4672
4861
  const files = fs11.readdirSync(resultsPath).filter((f) => f.startsWith("result-") && f.endsWith(".json")).sort();
4673
4862
  while (files.length > 20) {
4674
4863
  const oldest = files.shift();
4675
- fs11.unlinkSync(path12.join(resultsPath, oldest));
4864
+ fs11.unlinkSync(path13.join(resultsPath, oldest));
4676
4865
  }
4677
4866
  } catch {
4678
4867
  }
4679
4868
  }
4680
4869
  function checkTestDependencies(types) {
4681
- const pkgPath = path12.join(process.cwd(), "package.json");
4870
+ const pkgPath = path13.join(process.cwd(), "package.json");
4682
4871
  let allDeps = {};
4683
4872
  if (fs11.existsSync(pkgPath)) {
4684
4873
  try {
@@ -4704,8 +4893,8 @@ function checkTestDependencies(types) {
4704
4893
  return missingDeps;
4705
4894
  }
4706
4895
  async function installTestDependencies(missingDeps) {
4707
- const hasPnpm = fs11.existsSync(path12.join(process.cwd(), "pnpm-lock.yaml"));
4708
- const hasYarn = fs11.existsSync(path12.join(process.cwd(), "yarn.lock"));
4896
+ const hasPnpm = fs11.existsSync(path13.join(process.cwd(), "pnpm-lock.yaml"));
4897
+ const hasYarn = fs11.existsSync(path13.join(process.cwd(), "yarn.lock"));
4709
4898
  const pkgManager = hasPnpm ? "pnpm" : hasYarn ? "yarn" : "npm";
4710
4899
  const { execFile: execFile5 } = await import("child_process");
4711
4900
  let allSuccess = true;
@@ -4743,7 +4932,7 @@ async function installTestDependencies(missingDeps) {
4743
4932
  spinner.succeed(`${dep.pkg} \u5B89\u88C5\u6210\u529F`);
4744
4933
  } catch (error) {
4745
4934
  spinner.fail(`${dep.pkg} \u5B89\u88C5\u5931\u8D25`);
4746
- console.log(chalk5.gray(` \u53EF\u624B\u52A8\u5B89\u88C5: ${chalk5.cyan(dep.installCmd)}`));
4935
+ console.log(chalk6.gray(` \u53EF\u624B\u52A8\u5B89\u88C5: ${chalk6.cyan(dep.installCmd)}`));
4747
4936
  allSuccess = false;
4748
4937
  }
4749
4938
  }
@@ -4751,7 +4940,7 @@ async function installTestDependencies(missingDeps) {
4751
4940
  }
4752
4941
 
4753
4942
  // src/commands/mock.ts
4754
- import chalk6 from "chalk";
4943
+ import chalk7 from "chalk";
4755
4944
  import ora5 from "ora";
4756
4945
  function registerMockCommand(program2) {
4757
4946
  program2.command("mock").description("Mock\u670D\u52A1\u7BA1\u7406 - \u542F\u52A8/\u505C\u6B62Mock API\u670D\u52A1\u5668").argument("<action>", "\u64CD\u4F5C\u7C7B\u578B (start|stop|status)").option("-p, --port <port>", "\u6307\u5B9A\u7AEF\u53E3\u53F7", "3456").action(async (action, options) => {
@@ -4762,7 +4951,7 @@ function registerMockCommand(program2) {
4762
4951
  config: options.config
4763
4952
  });
4764
4953
  } catch (error) {
4765
- console.error(chalk6.red(`
4954
+ console.error(chalk7.red(`
4766
4955
  \u2717 ${error instanceof Error ? error.message : String(error)}
4767
4956
  `));
4768
4957
  process.exit(1);
@@ -4786,9 +4975,9 @@ async function executeMock(options) {
4786
4975
  break;
4787
4976
  }
4788
4977
  default: {
4789
- console.error(chalk6.red(`
4978
+ console.error(chalk7.red(`
4790
4979
  \u672A\u77E5\u64CD\u4F5C: ${options.action}`));
4791
- console.log(chalk6.gray(" \u53EF\u7528\u64CD\u4F5C: start, stop, status\n"));
4980
+ console.log(chalk7.gray(" \u53EF\u7528\u64CD\u4F5C: start, stop, status\n"));
4792
4981
  process.exit(1);
4793
4982
  }
4794
4983
  }
@@ -4796,7 +4985,7 @@ async function executeMock(options) {
4796
4985
  async function startMock(port, routesDir) {
4797
4986
  const state = getMockServerState();
4798
4987
  if (state.running) {
4799
- console.log(chalk6.yellow(`
4988
+ console.log(chalk7.yellow(`
4800
4989
  Mock\u670D\u52A1\u5668\u5DF2\u5728\u8FD0\u884C (\u7AEF\u53E3: ${state.port})
4801
4990
  `));
4802
4991
  return;
@@ -4810,22 +4999,22 @@ async function startMock(port, routesDir) {
4810
4999
  await startMockServer(port, routes);
4811
5000
  spinner.succeed(`Mock\u670D\u52A1\u5668\u5DF2\u542F\u52A8`);
4812
5001
  console.log();
4813
- console.log(chalk6.white(" \u5730\u5740:"), chalk6.cyan(`http://localhost:${port}`));
4814
- console.log(chalk6.white(" \u8DEF\u7531:"), routes.length > 0 ? `${routes.length} \u4E2A` : chalk6.gray("\u4F7F\u7528\u9ED8\u8BA4\u8DEF\u7531"));
4815
- console.log(chalk6.white(" \u5065\u5EB7\u68C0\u67E5:"), chalk6.cyan(`http://localhost:${port}/api/health`));
5002
+ console.log(chalk7.white(" \u5730\u5740:"), chalk7.cyan(`http://localhost:${port}`));
5003
+ console.log(chalk7.white(" \u8DEF\u7531:"), routes.length > 0 ? `${routes.length} \u4E2A` : chalk7.gray("\u4F7F\u7528\u9ED8\u8BA4\u8DEF\u7531"));
5004
+ console.log(chalk7.white(" \u5065\u5EB7\u68C0\u67E5:"), chalk7.cyan(`http://localhost:${port}/api/health`));
4816
5005
  if (routes.length > 0) {
4817
5006
  console.log();
4818
- console.log(chalk6.white(" \u5DF2\u6CE8\u518C\u8DEF\u7531:"));
5007
+ console.log(chalk7.white(" \u5DF2\u6CE8\u518C\u8DEF\u7531:"));
4819
5008
  for (const route of routes.slice(0, 10)) {
4820
- const method = chalk6.gray(route.method.padEnd(6));
5009
+ const method = chalk7.gray(route.method.padEnd(6));
4821
5010
  console.log(` ${method} ${route.path}`);
4822
5011
  }
4823
5012
  if (routes.length > 10) {
4824
- console.log(chalk6.gray(` ... \u8FD8\u6709 ${routes.length - 10} \u4E2A\u8DEF\u7531`));
5013
+ console.log(chalk7.gray(` ... \u8FD8\u6709 ${routes.length - 10} \u4E2A\u8DEF\u7531`));
4825
5014
  }
4826
5015
  }
4827
5016
  console.log();
4828
- console.log(chalk6.gray(" \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1\u5668"));
5017
+ console.log(chalk7.gray(" \u6309 Ctrl+C \u505C\u6B62\u670D\u52A1\u5668"));
4829
5018
  process.on("SIGINT", async () => {
4830
5019
  const stopSpinner = ora5("\u6B63\u5728\u505C\u6B62Mock\u670D\u52A1\u5668...").start();
4831
5020
  await stopMockServer();
@@ -4842,7 +5031,7 @@ async function startMock(port, routesDir) {
4842
5031
  async function stopMock() {
4843
5032
  const state = getMockServerState();
4844
5033
  if (!state.running) {
4845
- console.log(chalk6.yellow("\n Mock\u670D\u52A1\u5668\u672A\u5728\u8FD0\u884C\n"));
5034
+ console.log(chalk7.yellow("\n Mock\u670D\u52A1\u5668\u672A\u5728\u8FD0\u884C\n"));
4846
5035
  return;
4847
5036
  }
4848
5037
  const spinner = ora5("\u6B63\u5728\u505C\u6B62Mock\u670D\u52A1\u5668...").start();
@@ -4859,28 +5048,28 @@ function showStatus() {
4859
5048
  const state = getMockServerState();
4860
5049
  console.log();
4861
5050
  if (state.running) {
4862
- console.log(chalk6.green(" \u25CF Mock\u670D\u52A1\u5668\u8FD0\u884C\u4E2D"));
4863
- console.log(chalk6.white(" \u7AEF\u53E3:"), state.port);
4864
- console.log(chalk6.white(" \u8DEF\u7531:"), state.routes.length);
5051
+ console.log(chalk7.green(" \u25CF Mock\u670D\u52A1\u5668\u8FD0\u884C\u4E2D"));
5052
+ console.log(chalk7.white(" \u7AEF\u53E3:"), state.port);
5053
+ console.log(chalk7.white(" \u8DEF\u7531:"), state.routes.length);
4865
5054
  } else {
4866
- console.log(chalk6.gray(" \u25CB Mock\u670D\u52A1\u5668\u672A\u8FD0\u884C"));
4867
- console.log(chalk6.gray(" \u4F7F\u7528 qat mock start \u542F\u52A8"));
5055
+ console.log(chalk7.gray(" \u25CB Mock\u670D\u52A1\u5668\u672A\u8FD0\u884C"));
5056
+ console.log(chalk7.gray(" \u4F7F\u7528 qat mock start \u542F\u52A8"));
4868
5057
  }
4869
5058
  console.log();
4870
5059
  }
4871
5060
 
4872
5061
  // src/commands/report.ts
4873
- import chalk7 from "chalk";
5062
+ import chalk8 from "chalk";
4874
5063
  import ora6 from "ora";
4875
5064
  import fs12 from "fs";
4876
- import path13 from "path";
5065
+ import path14 from "path";
4877
5066
  var RESULTS_DIR2 = ".qat-results";
4878
5067
  function registerReportCommand(program2) {
4879
5068
  program2.command("report").description("\u751F\u6210\u6D4B\u8BD5\u62A5\u544A - \u805A\u5408\u6240\u6709\u6D4B\u8BD5\u7ED3\u679C\u5E76\u8F93\u51FAHTML").option("-o, --output <dir>", "\u62A5\u544A\u8F93\u51FA\u76EE\u5F55").option("--open", "\u751F\u6210\u540E\u81EA\u52A8\u6253\u5F00\u62A5\u544A", false).action(async (options) => {
4880
5069
  try {
4881
5070
  await executeReport(options);
4882
5071
  } catch (error) {
4883
- console.error(chalk7.red(`
5072
+ console.error(chalk8.red(`
4884
5073
  \u2717 ${error instanceof Error ? error.message : String(error)}
4885
5074
  `));
4886
5075
  process.exit(1);
@@ -4894,7 +5083,7 @@ async function executeReport(options) {
4894
5083
  const results = collectResults();
4895
5084
  if (results.length === 0) {
4896
5085
  spinner.info("\u6CA1\u6709\u627E\u5230\u6D4B\u8BD5\u7ED3\u679C");
4897
- console.log(chalk7.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run \u751F\u6210\u6D4B\u8BD5\u7ED3\u679C\n"));
5086
+ console.log(chalk8.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run \u751F\u6210\u6D4B\u8BD5\u7ED3\u679C\n"));
4898
5087
  return;
4899
5088
  }
4900
5089
  spinner.text = "\u6B63\u5728\u751F\u6210\u6D4B\u8BD5\u62A5\u544A...";
@@ -4909,13 +5098,13 @@ async function executeReport(options) {
4909
5098
  }
4910
5099
  function collectResults() {
4911
5100
  const results = [];
4912
- const resultsPath = path13.join(process.cwd(), RESULTS_DIR2);
5101
+ const resultsPath = path14.join(process.cwd(), RESULTS_DIR2);
4913
5102
  if (!fs12.existsSync(resultsPath)) {
4914
5103
  return results;
4915
5104
  }
4916
5105
  const files = fs12.readdirSync(resultsPath).filter((f) => f.endsWith(".json")).sort().reverse();
4917
5106
  if (files.length > 0) {
4918
- const latestFile = path13.join(resultsPath, files[0]);
5107
+ const latestFile = path14.join(resultsPath, files[0]);
4919
5108
  try {
4920
5109
  const data = JSON.parse(fs12.readFileSync(latestFile, "utf-8"));
4921
5110
  if (Array.isArray(data)) {
@@ -4929,49 +5118,49 @@ function collectResults() {
4929
5118
  return results;
4930
5119
  }
4931
5120
  function saveResultToHistory(reportData) {
4932
- const resultsPath = path13.join(process.cwd(), RESULTS_DIR2);
5121
+ const resultsPath = path14.join(process.cwd(), RESULTS_DIR2);
4933
5122
  if (!fs12.existsSync(resultsPath)) {
4934
5123
  fs12.mkdirSync(resultsPath, { recursive: true });
4935
5124
  }
4936
5125
  const timestamp = new Date(reportData.timestamp).toISOString().replace(/[:.]/g, "-");
4937
5126
  const fileName = `result-${timestamp}.json`;
4938
- const filePath = path13.join(resultsPath, fileName);
5127
+ const filePath = path14.join(resultsPath, fileName);
4939
5128
  fs12.writeFileSync(filePath, JSON.stringify(reportData, null, 2), "utf-8");
4940
5129
  const files = fs12.readdirSync(resultsPath).filter((f) => f.startsWith("result-") && f.endsWith(".json")).sort();
4941
5130
  while (files.length > 20) {
4942
5131
  const oldest = files.shift();
4943
- fs12.unlinkSync(path13.join(resultsPath, oldest));
5132
+ fs12.unlinkSync(path14.join(resultsPath, oldest));
4944
5133
  }
4945
5134
  }
4946
5135
  function displayReportResult(reportPath, data) {
4947
- const relativePath = path13.relative(process.cwd(), reportPath);
5136
+ const relativePath = path14.relative(process.cwd(), reportPath);
4948
5137
  const passRate = data.summary.total > 0 ? (data.summary.passed / data.summary.total * 100).toFixed(1) : "0";
4949
5138
  console.log();
4950
- console.log(chalk7.green(" \u2713 \u6D4B\u8BD5\u62A5\u544A\u5DF2\u751F\u6210"));
5139
+ console.log(chalk8.green(" \u2713 \u6D4B\u8BD5\u62A5\u544A\u5DF2\u751F\u6210"));
4951
5140
  console.log();
4952
- console.log(chalk7.white(" \u62A5\u544A\u8DEF\u5F84:"), chalk7.cyan(relativePath));
4953
- console.log(chalk7.white(" \u901A\u8FC7\u7387: "), parseFloat(passRate) >= 80 ? chalk7.green(`${passRate}%`) : parseFloat(passRate) >= 50 ? chalk7.yellow(`${passRate}%`) : chalk7.red(`${passRate}%`));
4954
- console.log(chalk7.white(" \u6D4B\u8BD5\u7528\u4F8B:"), `${data.summary.total} total`);
4955
- console.log(chalk7.white(" \u2705 \u901A\u8FC7: "), chalk7.green(String(data.summary.passed)));
5141
+ console.log(chalk8.white(" \u62A5\u544A\u8DEF\u5F84:"), chalk8.cyan(relativePath));
5142
+ console.log(chalk8.white(" \u901A\u8FC7\u7387: "), parseFloat(passRate) >= 80 ? chalk8.green(`${passRate}%`) : parseFloat(passRate) >= 50 ? chalk8.yellow(`${passRate}%`) : chalk8.red(`${passRate}%`));
5143
+ console.log(chalk8.white(" \u6D4B\u8BD5\u7528\u4F8B:"), `${data.summary.total} total`);
5144
+ console.log(chalk8.white(" \u2705 \u901A\u8FC7: "), chalk8.green(String(data.summary.passed)));
4956
5145
  if (data.summary.failed > 0) {
4957
- console.log(chalk7.white(" \u274C \u5931\u8D25: "), chalk7.red(String(data.summary.failed)));
5146
+ console.log(chalk8.white(" \u274C \u5931\u8D25: "), chalk8.red(String(data.summary.failed)));
4958
5147
  }
4959
5148
  if (data.summary.skipped > 0) {
4960
- console.log(chalk7.white(" \u23ED\uFE0F \u8DF3\u8FC7: "), chalk7.yellow(String(data.summary.skipped)));
5149
+ console.log(chalk8.white(" \u23ED\uFE0F \u8DF3\u8FC7: "), chalk8.yellow(String(data.summary.skipped)));
4961
5150
  }
4962
5151
  if (Object.keys(data.byType).length > 0) {
4963
5152
  console.log();
4964
- console.log(chalk7.white(" \u6309\u7C7B\u578B:"));
5153
+ console.log(chalk8.white(" \u6309\u7C7B\u578B:"));
4965
5154
  for (const [type, stats] of Object.entries(data.byType)) {
4966
5155
  const rate = stats.total > 0 ? (stats.passed / stats.total * 100).toFixed(0) : "0";
4967
- const icon = stats.failed > 0 ? chalk7.red("\u274C") : chalk7.green("\u2705");
5156
+ const icon = stats.failed > 0 ? chalk8.red("\u274C") : chalk8.green("\u2705");
4968
5157
  console.log(` ${icon} ${type}: ${stats.passed}/${stats.total} (${rate}%)`);
4969
5158
  }
4970
5159
  }
4971
5160
  if (data.coverage) {
4972
5161
  console.log();
4973
- console.log(chalk7.white(" \u8986\u76D6\u7387:"));
4974
- console.log(` \u8BED\u53E5: ${chalk7.cyan(pct2(data.coverage.statements))} \u5206\u652F: ${chalk7.cyan(pct2(data.coverage.branches))} \u51FD\u6570: ${chalk7.cyan(pct2(data.coverage.functions))} \u884C: ${chalk7.cyan(pct2(data.coverage.lines))}`);
5162
+ console.log(chalk8.white(" \u8986\u76D6\u7387:"));
5163
+ console.log(` \u8BED\u53E5: ${chalk8.cyan(pct2(data.coverage.statements))} \u5206\u652F: ${chalk8.cyan(pct2(data.coverage.branches))} \u51FD\u6570: ${chalk8.cyan(pct2(data.coverage.functions))} \u884C: ${chalk8.cyan(pct2(data.coverage.lines))}`);
4975
5164
  }
4976
5165
  console.log();
4977
5166
  }
@@ -4991,18 +5180,18 @@ async function openReport(reportPath) {
4991
5180
  }
4992
5181
  exec(command, { shell: true }, (error) => {
4993
5182
  if (error) {
4994
- console.log(chalk7.gray(" \u63D0\u793A: \u624B\u52A8\u6253\u5F00\u62A5\u544A\u67E5\u770B"));
5183
+ console.log(chalk8.gray(" \u63D0\u793A: \u624B\u52A8\u6253\u5F00\u62A5\u544A\u67E5\u770B"));
4995
5184
  }
4996
5185
  });
4997
5186
  }
4998
5187
 
4999
5188
  // src/commands/visual.ts
5000
- import chalk8 from "chalk";
5189
+ import chalk9 from "chalk";
5001
5190
  import ora7 from "ora";
5002
5191
 
5003
5192
  // src/services/visual.ts
5004
5193
  import fs13 from "fs";
5005
- import path14 from "path";
5194
+ import path15 from "path";
5006
5195
  import pixelmatch from "pixelmatch";
5007
5196
  import { PNG } from "pngjs";
5008
5197
  function compareImages(baselinePath, currentPath, diffOutputPath, threshold = 0.1) {
@@ -5041,7 +5230,7 @@ function compareImages(baselinePath, currentPath, diffOutputPath, threshold = 0.
5041
5230
  const passed = diffRatio <= threshold;
5042
5231
  let diffPath;
5043
5232
  if (diffPixels > 0) {
5044
- const diffDir = path14.dirname(diffOutputPath);
5233
+ const diffDir = path15.dirname(diffOutputPath);
5045
5234
  if (!fs13.existsSync(diffDir)) {
5046
5235
  fs13.mkdirSync(diffDir, { recursive: true });
5047
5236
  }
@@ -5062,7 +5251,7 @@ function createBaseline(currentPath, baselinePath) {
5062
5251
  if (!fs13.existsSync(currentPath)) {
5063
5252
  throw new Error(`\u5F53\u524D\u622A\u56FE\u4E0D\u5B58\u5728: ${currentPath}`);
5064
5253
  }
5065
- const baselineDir = path14.dirname(baselinePath);
5254
+ const baselineDir = path15.dirname(baselinePath);
5066
5255
  if (!fs13.existsSync(baselineDir)) {
5067
5256
  fs13.mkdirSync(baselineDir, { recursive: true });
5068
5257
  }
@@ -5076,9 +5265,9 @@ function updateAllBaselines(currentDir, baselineDir) {
5076
5265
  }
5077
5266
  const files = fs13.readdirSync(currentDir).filter((f) => f.endsWith(".png"));
5078
5267
  for (const file of files) {
5079
- const currentPath = path14.join(currentDir, file);
5080
- const baselinePath = path14.join(baselineDir, file);
5081
- const baselineDirAbs = path14.dirname(baselinePath);
5268
+ const currentPath = path15.join(currentDir, file);
5269
+ const baselinePath = path15.join(baselineDir, file);
5270
+ const baselineDirAbs = path15.dirname(baselinePath);
5082
5271
  if (!fs13.existsSync(baselineDirAbs)) {
5083
5272
  fs13.mkdirSync(baselineDirAbs, { recursive: true });
5084
5273
  }
@@ -5094,7 +5283,7 @@ function cleanBaselines(baselineDir) {
5094
5283
  const files = fs13.readdirSync(baselineDir).filter((f) => f.endsWith(".png"));
5095
5284
  let count = 0;
5096
5285
  for (const file of files) {
5097
- fs13.unlinkSync(path14.join(baselineDir, file));
5286
+ fs13.unlinkSync(path15.join(baselineDir, file));
5098
5287
  count++;
5099
5288
  }
5100
5289
  return count;
@@ -5106,7 +5295,7 @@ function cleanDiffs(diffDir) {
5106
5295
  const files = fs13.readdirSync(diffDir).filter((f) => f.endsWith(".png"));
5107
5296
  let count = 0;
5108
5297
  for (const file of files) {
5109
- fs13.unlinkSync(path14.join(diffDir, file));
5298
+ fs13.unlinkSync(path15.join(diffDir, file));
5110
5299
  count++;
5111
5300
  }
5112
5301
  return count;
@@ -5118,9 +5307,9 @@ function compareDirectories(baselineDir, currentDir, diffDir, threshold = 0.1) {
5118
5307
  }
5119
5308
  const currentFiles = fs13.readdirSync(currentDir).filter((f) => f.endsWith(".png"));
5120
5309
  for (const file of currentFiles) {
5121
- const currentPath = path14.join(currentDir, file);
5122
- const baselinePath = path14.join(baselineDir, file);
5123
- const diffPath = path14.join(diffDir, file);
5310
+ const currentPath = path15.join(currentDir, file);
5311
+ const baselinePath = path15.join(baselineDir, file);
5312
+ const diffPath = path15.join(diffDir, file);
5124
5313
  if (!fs13.existsSync(baselinePath)) {
5125
5314
  createBaseline(currentPath, baselinePath);
5126
5315
  results.push({
@@ -5154,7 +5343,7 @@ function compareDirectories(baselineDir, currentDir, diffDir, threshold = 0.1) {
5154
5343
 
5155
5344
  // src/commands/visual.ts
5156
5345
  import fs14 from "fs";
5157
- import path15 from "path";
5346
+ import path16 from "path";
5158
5347
  function registerVisualCommand(program2) {
5159
5348
  program2.command("visual").description("\u89C6\u89C9\u56DE\u5F52\u6D4B\u8BD5 - \u622A\u56FE\u6BD4\u5BF9\u4E0E\u57FA\u7EBF\u7BA1\u7406").argument("<action>", "\u64CD\u4F5C\u7C7B\u578B (test|approve|clean)").option("--threshold <number>", "\u50CF\u7D20\u5DEE\u5F02\u9608\u503C (0-1)", "0.1").action(async (action, options) => {
5160
5349
  try {
@@ -5164,7 +5353,7 @@ function registerVisualCommand(program2) {
5164
5353
  config: options.config
5165
5354
  });
5166
5355
  } catch (error) {
5167
- console.error(chalk8.red(`
5356
+ console.error(chalk9.red(`
5168
5357
  \u2717 ${error instanceof Error ? error.message : String(error)}
5169
5358
  `));
5170
5359
  process.exit(1);
@@ -5190,9 +5379,9 @@ async function executeVisual(options) {
5190
5379
  break;
5191
5380
  }
5192
5381
  default: {
5193
- console.error(chalk8.red(`
5382
+ console.error(chalk9.red(`
5194
5383
  \u672A\u77E5\u64CD\u4F5C: ${options.action}`));
5195
- console.log(chalk8.gray(" \u53EF\u7528\u64CD\u4F5C: test, approve, clean\n"));
5384
+ console.log(chalk9.gray(" \u53EF\u7528\u64CD\u4F5C: test, approve, clean\n"));
5196
5385
  process.exit(1);
5197
5386
  }
5198
5387
  }
@@ -5216,7 +5405,7 @@ async function runVisualTest(threshold, baselineDir, diffDir, config) {
5216
5405
  const currentDir = findCurrentScreenshotsDir(baselineDir);
5217
5406
  if (!currentDir) {
5218
5407
  compareSpinner.info("\u6CA1\u6709\u627E\u5230\u622A\u56FE\u6587\u4EF6");
5219
- console.log(chalk8.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run -t visual \u751F\u6210\u622A\u56FE\n"));
5408
+ console.log(chalk9.gray("\n \u63D0\u793A: \u5148\u8FD0\u884C qat run -t visual \u751F\u6210\u622A\u56FE\n"));
5220
5409
  return;
5221
5410
  }
5222
5411
  const results = compareDirectories(baselineDir, currentDir, diffDir, threshold);
@@ -5229,9 +5418,9 @@ async function runVisualTest(threshold, baselineDir, diffDir, config) {
5229
5418
  }
5230
5419
  function findCurrentScreenshotsDir(baselineDir) {
5231
5420
  const possibleDirs = [
5232
- path15.join(process.cwd(), "test-results"),
5233
- path15.join(process.cwd(), "tests", "visual", "current"),
5234
- path15.join(process.cwd(), baselineDir, "..", "current")
5421
+ path16.join(process.cwd(), "test-results"),
5422
+ path16.join(process.cwd(), "tests", "visual", "current"),
5423
+ path16.join(process.cwd(), baselineDir, "..", "current")
5235
5424
  ];
5236
5425
  for (const dir of possibleDirs) {
5237
5426
  if (fs14.existsSync(dir)) {
@@ -5247,7 +5436,7 @@ function findPngFiles(dir) {
5247
5436
  if (!fs14.existsSync(d)) return;
5248
5437
  const entries = fs14.readdirSync(d, { withFileTypes: true });
5249
5438
  for (const entry of entries) {
5250
- const fullPath = path15.join(d, entry.name);
5439
+ const fullPath = path16.join(d, entry.name);
5251
5440
  if (entry.isDirectory() && entry.name !== "node_modules") {
5252
5441
  walk(fullPath);
5253
5442
  } else if (entry.name.endsWith(".png")) {
@@ -5262,48 +5451,48 @@ function displayVisualResults(results, threshold) {
5262
5451
  const passed = results.filter((r) => r.passed);
5263
5452
  const failed = results.filter((r) => !r.passed);
5264
5453
  console.log();
5265
- console.log(chalk8.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5454
+ console.log(chalk9.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5266
5455
  if (failed.length === 0) {
5267
- console.log(chalk8.green(` \u2713 \u5168\u90E8\u901A\u8FC7 (${results.length} \u4E2A\u622A\u56FE\u6BD4\u5BF9)`));
5456
+ console.log(chalk9.green(` \u2713 \u5168\u90E8\u901A\u8FC7 (${results.length} \u4E2A\u622A\u56FE\u6BD4\u5BF9)`));
5268
5457
  } else {
5269
- console.log(chalk8.red(` \u2717 ${failed.length} \u4E2A\u622A\u56FE\u5B58\u5728\u5DEE\u5F02`));
5458
+ console.log(chalk9.red(` \u2717 ${failed.length} \u4E2A\u622A\u56FE\u5B58\u5728\u5DEE\u5F02`));
5270
5459
  }
5271
5460
  console.log();
5272
- console.log(` ${chalk8.white("\u9608\u503C:")} ${(threshold * 100).toFixed(0)}%`);
5273
- console.log(` ${chalk8.green("\u901A\u8FC7:")} ${passed.length}`);
5274
- console.log(` ${chalk8.red("\u5931\u8D25:")} ${failed.length}`);
5461
+ console.log(` ${chalk9.white("\u9608\u503C:")} ${(threshold * 100).toFixed(0)}%`);
5462
+ console.log(` ${chalk9.green("\u901A\u8FC7:")} ${passed.length}`);
5463
+ console.log(` ${chalk9.red("\u5931\u8D25:")} ${failed.length}`);
5275
5464
  if (passed.length > 0) {
5276
5465
  console.log();
5277
- console.log(chalk8.green(" \u901A\u8FC7\u7684\u622A\u56FE:"));
5466
+ console.log(chalk9.green(" \u901A\u8FC7\u7684\u622A\u56FE:"));
5278
5467
  for (const result of passed) {
5279
- const name = path15.basename(result.baselinePath);
5468
+ const name = path16.basename(result.baselinePath);
5280
5469
  if (result.totalPixels > 0) {
5281
5470
  const diffPct = (result.diffRatio * 100).toFixed(2);
5282
- console.log(chalk8.green(` \u2713 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
5471
+ console.log(chalk9.green(` \u2713 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
5283
5472
  } else {
5284
- console.log(chalk8.green(` \u2713 ${name} (\u65B0\u5EFA\u57FA\u7EBF)`));
5473
+ console.log(chalk9.green(` \u2713 ${name} (\u65B0\u5EFA\u57FA\u7EBF)`));
5285
5474
  }
5286
5475
  }
5287
5476
  }
5288
5477
  if (failed.length > 0) {
5289
5478
  console.log();
5290
- console.log(chalk8.red(" \u5931\u8D25\u7684\u622A\u56FE:"));
5479
+ console.log(chalk9.red(" \u5931\u8D25\u7684\u622A\u56FE:"));
5291
5480
  for (const result of failed) {
5292
- const name = path15.basename(result.baselinePath);
5481
+ const name = path16.basename(result.baselinePath);
5293
5482
  if (result.diffPixels === -1) {
5294
- console.log(chalk8.red(` \u2717 ${name} (\u5C3A\u5BF8\u4E0D\u5339\u914D)`));
5483
+ console.log(chalk9.red(` \u2717 ${name} (\u5C3A\u5BF8\u4E0D\u5339\u914D)`));
5295
5484
  } else {
5296
5485
  const diffPct = (result.diffRatio * 100).toFixed(2);
5297
- console.log(chalk8.red(` \u2717 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
5486
+ console.log(chalk9.red(` \u2717 ${name} (\u5DEE\u5F02: ${diffPct}%)`));
5298
5487
  }
5299
5488
  if (result.diffPath) {
5300
- console.log(chalk8.gray(` \u5DEE\u5F02\u56FE: ${path15.relative(process.cwd(), result.diffPath)}`));
5489
+ console.log(chalk9.gray(` \u5DEE\u5F02\u56FE: ${path16.relative(process.cwd(), result.diffPath)}`));
5301
5490
  }
5302
5491
  }
5303
5492
  console.log();
5304
- console.log(chalk8.yellow(" \u63D0\u793A: \u8FD0\u884C qat visual approve \u66F4\u65B0\u57FA\u7EBF"));
5493
+ console.log(chalk9.yellow(" \u63D0\u793A: \u8FD0\u884C qat visual approve \u66F4\u65B0\u57FA\u7EBF"));
5305
5494
  }
5306
- console.log(chalk8.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5495
+ console.log(chalk9.cyan(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5307
5496
  console.log();
5308
5497
  }
5309
5498
  async function approveBaselines(baselineDir, diffDir) {
@@ -5322,7 +5511,7 @@ async function approveBaselines(baselineDir, diffDir) {
5322
5511
  spinner.succeed(`\u5DF2\u66F4\u65B0 ${updated.length} \u4E2A\u57FA\u7EBF\u5FEB\u7167`);
5323
5512
  console.log();
5324
5513
  for (const file of updated) {
5325
- console.log(chalk8.green(` \u2713 ${file}`));
5514
+ console.log(chalk9.green(` \u2713 ${file}`));
5326
5515
  }
5327
5516
  console.log();
5328
5517
  }
@@ -5332,19 +5521,19 @@ async function cleanAll(baselineDir, diffDir) {
5332
5521
  const diffs = cleanDiffs(diffDir);
5333
5522
  spinner.succeed("\u6E05\u7406\u5B8C\u6210");
5334
5523
  console.log();
5335
- console.log(chalk8.white(" \u5DF2\u5220\u9664:"));
5524
+ console.log(chalk9.white(" \u5DF2\u5220\u9664:"));
5336
5525
  console.log(` \u57FA\u7EBF: ${baselines} \u4E2A`);
5337
5526
  console.log(` \u5DEE\u5F02: ${diffs} \u4E2A`);
5338
5527
  console.log();
5339
5528
  }
5340
5529
 
5341
5530
  // src/commands/setup.ts
5342
- import chalk9 from "chalk";
5531
+ import chalk10 from "chalk";
5343
5532
  import inquirer4 from "inquirer";
5344
5533
  import ora8 from "ora";
5345
5534
  import { execFile as execFile4 } from "child_process";
5346
5535
  import fs15 from "fs";
5347
- import path16 from "path";
5536
+ import path17 from "path";
5348
5537
  var DEPENDENCY_GROUPS = [
5349
5538
  {
5350
5539
  name: "Vitest (\u5355\u5143/\u7EC4\u4EF6/API\u6D4B\u8BD5)",
@@ -5368,7 +5557,7 @@ function registerSetupCommand(program2) {
5368
5557
  try {
5369
5558
  await executeSetup(options);
5370
5559
  } catch (error) {
5371
- console.error(chalk9.red(`
5560
+ console.error(chalk10.red(`
5372
5561
  \u2717 ${error instanceof Error ? error.message : String(error)}
5373
5562
  `));
5374
5563
  process.exit(1);
@@ -5376,20 +5565,20 @@ function registerSetupCommand(program2) {
5376
5565
  });
5377
5566
  }
5378
5567
  async function executeSetup(options) {
5379
- console.log(chalk9.cyan("\n QAT \u4F9D\u8D56\u5B89\u88C5\u5668\n"));
5568
+ console.log(chalk10.cyan("\n QAT \u4F9D\u8D56\u5B89\u88C5\u5668\n"));
5380
5569
  const projectInfo = detectProject();
5381
- if (!fs15.existsSync(path16.join(process.cwd(), "package.json"))) {
5570
+ if (!fs15.existsSync(path17.join(process.cwd(), "package.json"))) {
5382
5571
  throw new Error("\u672A\u627E\u5230 package.json\uFF0C\u8BF7\u5728\u9879\u76EE\u6839\u76EE\u5F55\u6267\u884C\u6B64\u547D\u4EE4");
5383
5572
  }
5384
5573
  if (projectInfo.frameworkConfidence > 0) {
5385
- console.log(chalk9.white(` \u68C0\u6D4B\u5230\u6846\u67B6: ${chalk9.cyan(projectInfo.frameworkDisplayName)}`));
5574
+ console.log(chalk10.white(` \u68C0\u6D4B\u5230\u6846\u67B6: ${chalk10.cyan(projectInfo.frameworkDisplayName)}`));
5386
5575
  if (projectInfo.uiLibrary !== "none") {
5387
- console.log(chalk9.white(` UI \u7EC4\u4EF6\u5E93: ${chalk9.cyan(projectInfo.uiLibrary)}`));
5576
+ console.log(chalk10.white(` UI \u7EC4\u4EF6\u5E93: ${chalk10.cyan(projectInfo.uiLibrary)}`));
5388
5577
  }
5389
5578
  if (projectInfo.monorepo !== "none") {
5390
- console.log(chalk9.white(` Monorepo: ${chalk9.cyan(projectInfo.monorepo)}`));
5579
+ console.log(chalk10.white(` Monorepo: ${chalk10.cyan(projectInfo.monorepo)}`));
5391
5580
  if (projectInfo.appDirs.length > 0) {
5392
- console.log(chalk9.white(` \u5B50\u9879\u76EE: ${chalk9.gray(projectInfo.appDirs.join(", "))}`));
5581
+ console.log(chalk10.white(` \u5B50\u9879\u76EE: ${chalk10.gray(projectInfo.appDirs.join(", "))}`));
5393
5582
  }
5394
5583
  }
5395
5584
  console.log();
@@ -5412,8 +5601,8 @@ async function executeSetup(options) {
5412
5601
  }
5413
5602
  ]);
5414
5603
  if (chooseDir !== "root") {
5415
- installDir = path16.join(process.cwd(), chooseDir);
5416
- if (!fs15.existsSync(path16.join(installDir, "package.json"))) {
5604
+ installDir = path17.join(process.cwd(), chooseDir);
5605
+ if (!fs15.existsSync(path17.join(installDir, "package.json"))) {
5417
5606
  throw new Error(`${chooseDir} \u4E0B\u6CA1\u6709 package.json`);
5418
5607
  }
5419
5608
  }
@@ -5425,12 +5614,12 @@ async function executeSetup(options) {
5425
5614
  }
5426
5615
  const groupsToInstall = determineGroups(config, projectInfo, options.force);
5427
5616
  if (groupsToInstall.length === 0) {
5428
- console.log(chalk9.green(" \u2713 \u6240\u6709\u4F9D\u8D56\u5DF2\u5B89\u88C5\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\n"));
5617
+ console.log(chalk10.green(" \u2713 \u6240\u6709\u4F9D\u8D56\u5DF2\u5B89\u88C5\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\n"));
5429
5618
  return;
5430
5619
  }
5431
5620
  const selectedGroups = await selectGroups(groupsToInstall, options.dryRun);
5432
5621
  if (selectedGroups.length === 0) {
5433
- console.log(chalk9.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
5622
+ console.log(chalk10.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
5434
5623
  return;
5435
5624
  }
5436
5625
  const allPackages = selectedGroups.flatMap((g) => g.packages);
@@ -5444,19 +5633,19 @@ ${allPackages.map((p) => ` - ${p}`).join("\n")}`,
5444
5633
  }
5445
5634
  ]);
5446
5635
  if (!confirmed) {
5447
- console.log(chalk9.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
5636
+ console.log(chalk10.gray("\n \u5DF2\u53D6\u6D88\u5B89\u88C5\n"));
5448
5637
  return;
5449
5638
  }
5450
5639
  if (options.dryRun) {
5451
- console.log(chalk9.yellow("\n [Dry Run] \u4EE5\u4E0B\u547D\u4EE4\u5C06\u88AB\u6267\u884C:\n"));
5640
+ console.log(chalk10.yellow("\n [Dry Run] \u4EE5\u4E0B\u547D\u4EE4\u5C06\u88AB\u6267\u884C:\n"));
5452
5641
  const pm = getPackageManager(projectInfo.packageManager);
5453
5642
  const installCmd = pm === "npm" ? "npm install -D" : pm === "yarn" ? "yarn add -D" : pm === "pnpm" ? "pnpm add -D" : "bun add -D";
5454
5643
  for (const group of selectedGroups) {
5455
- const dirHint = installDir !== process.cwd() ? ` (\u5728 ${path16.relative(process.cwd(), installDir) || installDir})` : "";
5456
- console.log(chalk9.white(` ${installCmd} ${group.packages.join(" ")}${dirHint}`));
5644
+ const dirHint = installDir !== process.cwd() ? ` (\u5728 ${path17.relative(process.cwd(), installDir) || installDir})` : "";
5645
+ console.log(chalk10.white(` ${installCmd} ${group.packages.join(" ")}${dirHint}`));
5457
5646
  if (group.postInstall) {
5458
5647
  for (const cmd of group.postInstall) {
5459
- console.log(chalk9.white(` ${cmd}`));
5648
+ console.log(chalk10.white(` ${cmd}`));
5460
5649
  }
5461
5650
  }
5462
5651
  }
@@ -5569,36 +5758,36 @@ ${stderr}` : "")));
5569
5758
  });
5570
5759
  }
5571
5760
  function displaySetupResult(groups) {
5572
- console.log(chalk9.green("\n \u2713 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210!\n"));
5573
- console.log(chalk9.white(" \u5DF2\u5B89\u88C5:"));
5761
+ console.log(chalk10.green("\n \u2713 \u4F9D\u8D56\u5B89\u88C5\u5B8C\u6210!\n"));
5762
+ console.log(chalk10.white(" \u5DF2\u5B89\u88C5:"));
5574
5763
  for (const group of groups) {
5575
- console.log(chalk9.cyan(`
5764
+ console.log(chalk10.cyan(`
5576
5765
  ${group.name}`));
5577
5766
  for (const pkg of group.packages) {
5578
- console.log(chalk9.gray(` \u2713 ${pkg}`));
5767
+ console.log(chalk10.gray(` \u2713 ${pkg}`));
5579
5768
  }
5580
5769
  if (group.postInstall) {
5581
5770
  for (const cmd of group.postInstall) {
5582
- console.log(chalk9.gray(` \u2713 ${cmd}`));
5771
+ console.log(chalk10.gray(` \u2713 ${cmd}`));
5583
5772
  }
5584
5773
  }
5585
5774
  }
5586
5775
  console.log();
5587
- console.log(chalk9.cyan(" \u4E0B\u4E00\u6B65:"));
5588
- console.log(chalk9.gray(" 1. \u8FD0\u884C qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
5589
- console.log(chalk9.gray(" 2. \u8FD0\u884C qat run \u6267\u884C\u6D4B\u8BD5"));
5776
+ console.log(chalk10.cyan(" \u4E0B\u4E00\u6B65:"));
5777
+ console.log(chalk10.gray(" 1. \u8FD0\u884C qat create \u521B\u5EFA\u6D4B\u8BD5\u7528\u4F8B"));
5778
+ console.log(chalk10.gray(" 2. \u8FD0\u884C qat run \u6267\u884C\u6D4B\u8BD5"));
5590
5779
  console.log();
5591
5780
  }
5592
5781
 
5593
5782
  // src/commands/status.ts
5594
- import chalk10 from "chalk";
5783
+ import chalk11 from "chalk";
5595
5784
  import ora9 from "ora";
5596
5785
  function registerStatusCommand(program2) {
5597
5786
  program2.command("status").description("\u67E5\u770B QAT \u72B6\u6001 - AI \u6A21\u578B\u4FE1\u606F\u3001\u9879\u76EE\u914D\u7F6E").action(async (options) => {
5598
5787
  try {
5599
5788
  await executeStatus(options);
5600
5789
  } catch (error) {
5601
- console.error(chalk10.red(`
5790
+ console.error(chalk11.red(`
5602
5791
  \u2717 ${error instanceof Error ? error.message : String(error)}
5603
5792
  `));
5604
5793
  process.exit(1);
@@ -5606,24 +5795,24 @@ function registerStatusCommand(program2) {
5606
5795
  });
5607
5796
  }
5608
5797
  async function executeStatus(_options) {
5609
- console.log(chalk10.cyan("\n AI \u6A21\u578B\u72B6\u6001"));
5610
- console.log(chalk10.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5798
+ console.log(chalk11.cyan("\n AI \u6A21\u578B\u72B6\u6001"));
5799
+ console.log(chalk11.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5611
5800
  const globalAI = loadGlobalAIConfig();
5612
5801
  if (!globalAI) {
5613
- console.log(chalk10.yellow(" \u2717 \u672A\u914D\u7F6E AI \u6A21\u578B"));
5614
- console.log(chalk10.gray(" \u8FD0\u884C qat change \u914D\u7F6E AI \u6A21\u578B"));
5802
+ console.log(chalk11.yellow(" \u2717 \u672A\u914D\u7F6E AI \u6A21\u578B"));
5803
+ console.log(chalk11.gray(" \u8FD0\u884C qat change \u914D\u7F6E AI \u6A21\u578B"));
5615
5804
  } else {
5616
- console.log(` ${chalk10.white("\u6A21\u578B:")} ${chalk10.green(globalAI.model)}`);
5617
- console.log(` ${chalk10.white("API URL:")} ${chalk10.gray(globalAI.baseUrl)}`);
5618
- console.log(` ${chalk10.white("API Key:")} ${chalk10.gray(maskApiKey(globalAI.apiKey))}`);
5619
- console.log(` ${chalk10.white("Provider:")} ${chalk10.gray(globalAI.provider)}`);
5620
- console.log(` ${chalk10.white("\u914D\u7F6E\u6587\u4EF6:")} ${chalk10.gray(getAIConfigPath())}`);
5805
+ console.log(` ${chalk11.white("\u6A21\u578B:")} ${chalk11.green(globalAI.model)}`);
5806
+ console.log(` ${chalk11.white("API URL:")} ${chalk11.gray(globalAI.baseUrl)}`);
5807
+ console.log(` ${chalk11.white("API Key:")} ${chalk11.gray(maskApiKey(globalAI.apiKey))}`);
5808
+ console.log(` ${chalk11.white("Provider:")} ${chalk11.gray(globalAI.provider)}`);
5809
+ console.log(` ${chalk11.white("\u914D\u7F6E\u6587\u4EF6:")} ${chalk11.gray(getAIConfigPath())}`);
5621
5810
  const testSpinner = ora9(" \u6B63\u5728\u6D4B\u8BD5\u8FDE\u901A\u6027...").start();
5622
5811
  try {
5623
5812
  const aiConfig = toAIConfig(globalAI);
5624
5813
  const result = await testAIConnection(aiConfig);
5625
5814
  if (result.ok) {
5626
- testSpinner.succeed(` \u8FDE\u901A\u6B63\u5E38 ${chalk10.gray(`(${result.latencyMs}ms)`)}`);
5815
+ testSpinner.succeed(` \u8FDE\u901A\u6B63\u5E38 ${chalk11.gray(`(${result.latencyMs}ms)`)}`);
5627
5816
  } else {
5628
5817
  testSpinner.fail(` \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
5629
5818
  }
@@ -5632,26 +5821,26 @@ async function executeStatus(_options) {
5632
5821
  }
5633
5822
  }
5634
5823
  console.log();
5635
- console.log(chalk10.cyan(" \u9879\u76EE\u914D\u7F6E"));
5636
- console.log(chalk10.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5824
+ console.log(chalk11.cyan(" \u9879\u76EE\u914D\u7F6E"));
5825
+ console.log(chalk11.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
5637
5826
  try {
5638
5827
  const config = await loadConfig();
5639
5828
  const projectInfo = detectProject();
5640
- console.log(` ${chalk10.white("\u6846\u67B6:")} ${projectInfo.frameworkDisplayName}`);
5641
- console.log(` ${chalk10.white("\u6E90\u7801\u76EE\u5F55:")} ${config.project.srcDir}`);
5642
- console.log(` ${chalk10.white("Vitest:")} ${config.vitest.enabled ? chalk10.green("\u2713") : chalk10.red("\u2717")} (${config.vitest.environment})`);
5643
- console.log(` ${chalk10.white("Playwright:")} ${config.playwright.enabled ? chalk10.green("\u2713") : chalk10.red("\u2717")} (${config.playwright.browsers.join(", ")})`);
5644
- console.log(` ${chalk10.white("Mock:")} ${config.mock.enabled ? chalk10.green("\u2713") : chalk10.red("\u2717")} (port ${config.mock.port})`);
5645
- console.log(` ${chalk10.white("Visual:")} ${config.visual.enabled ? chalk10.green("\u2713") : chalk10.red("\u2717")}`);
5646
- console.log(` ${chalk10.white("Lighthouse:")} ${config.lighthouse.enabled ? chalk10.green("\u2713") : chalk10.red("\u2717")}`);
5829
+ console.log(` ${chalk11.white("\u6846\u67B6:")} ${projectInfo.frameworkDisplayName}`);
5830
+ console.log(` ${chalk11.white("\u6E90\u7801\u76EE\u5F55:")} ${config.project.srcDir}`);
5831
+ console.log(` ${chalk11.white("Vitest:")} ${config.vitest.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (${config.vitest.environment})`);
5832
+ console.log(` ${chalk11.white("Playwright:")} ${config.playwright.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (${config.playwright.browsers.join(", ")})`);
5833
+ console.log(` ${chalk11.white("Mock:")} ${config.mock.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")} (port ${config.mock.port})`);
5834
+ console.log(` ${chalk11.white("Visual:")} ${config.visual.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")}`);
5835
+ console.log(` ${chalk11.white("Lighthouse:")} ${config.lighthouse.enabled ? chalk11.green("\u2713") : chalk11.red("\u2717")}`);
5647
5836
  } catch {
5648
- console.log(chalk10.yellow(" \u2717 \u672A\u627E\u5230\u9879\u76EE\u914D\u7F6E (\u8FD0\u884C qat init \u521D\u59CB\u5316)"));
5837
+ console.log(chalk11.yellow(" \u2717 \u672A\u627E\u5230\u9879\u76EE\u914D\u7F6E (\u8FD0\u884C qat init \u521D\u59CB\u5316)"));
5649
5838
  }
5650
5839
  console.log();
5651
5840
  }
5652
5841
 
5653
5842
  // src/commands/change.ts
5654
- import chalk11 from "chalk";
5843
+ import chalk12 from "chalk";
5655
5844
  import inquirer5 from "inquirer";
5656
5845
  import ora10 from "ora";
5657
5846
  function registerChangeCommand(program2) {
@@ -5659,7 +5848,7 @@ function registerChangeCommand(program2) {
5659
5848
  try {
5660
5849
  await executeChange(options);
5661
5850
  } catch (error) {
5662
- console.error(chalk11.red(`
5851
+ console.error(chalk12.red(`
5663
5852
  \u2717 ${error instanceof Error ? error.message : String(error)}
5664
5853
  `));
5665
5854
  process.exit(1);
@@ -5669,13 +5858,13 @@ function registerChangeCommand(program2) {
5669
5858
  async function executeChange(_options) {
5670
5859
  const current = loadGlobalAIConfig();
5671
5860
  if (current) {
5672
- console.log(chalk11.cyan("\n \u5F53\u524D AI \u914D\u7F6E:"));
5673
- console.log(` ${chalk11.white("\u6A21\u578B:")} ${chalk11.green(current.model)}`);
5674
- console.log(` ${chalk11.white("API URL:")} ${chalk11.gray(current.baseUrl)}`);
5675
- console.log(` ${chalk11.white("API Key:")} ${chalk11.gray(maskApiKey(current.apiKey))}`);
5861
+ console.log(chalk12.cyan("\n \u5F53\u524D AI \u914D\u7F6E:"));
5862
+ console.log(` ${chalk12.white("\u6A21\u578B:")} ${chalk12.green(current.model)}`);
5863
+ console.log(` ${chalk12.white("API URL:")} ${chalk12.gray(current.baseUrl)}`);
5864
+ console.log(` ${chalk12.white("API Key:")} ${chalk12.gray(maskApiKey(current.apiKey))}`);
5676
5865
  console.log();
5677
5866
  } else {
5678
- console.log(chalk11.yellow("\n \u5F53\u524D\u672A\u914D\u7F6E AI \u6A21\u578B\uFF0C\u8BF7\u914D\u7F6E:\n"));
5867
+ console.log(chalk12.yellow("\n \u5F53\u524D\u672A\u914D\u7F6E AI \u6A21\u578B\uFF0C\u8BF7\u914D\u7F6E:\n"));
5679
5868
  }
5680
5869
  const answers = await inquirer5.prompt([
5681
5870
  {
@@ -5713,16 +5902,16 @@ async function executeChange(_options) {
5713
5902
  model: answers.model?.trim() || "deepseek-chat"
5714
5903
  };
5715
5904
  saveGlobalAIConfig(newConfig);
5716
- console.log(chalk11.green(`
5905
+ console.log(chalk12.green(`
5717
5906
  \u2713 AI \u914D\u7F6E\u5DF2\u4FDD\u5B58`));
5718
- console.log(chalk11.gray(` ${getAIConfigPath()}`));
5719
- console.log(` ${chalk11.white("\u6A21\u578B:")} ${chalk11.green(newConfig.model)} @ ${chalk11.gray(newConfig.baseUrl)}`);
5907
+ console.log(chalk12.gray(` ${getAIConfigPath()}`));
5908
+ console.log(` ${chalk12.white("\u6A21\u578B:")} ${chalk12.green(newConfig.model)} @ ${chalk12.gray(newConfig.baseUrl)}`);
5720
5909
  const testSpinner = ora10(" \u6B63\u5728\u6D4B\u8BD5\u8FDE\u901A\u6027...").start();
5721
5910
  try {
5722
5911
  const aiConfig = toAIConfig(newConfig);
5723
5912
  const result = await testAIConnection(aiConfig);
5724
5913
  if (result.ok) {
5725
- testSpinner.succeed(` AI \u8FDE\u901A\u6B63\u5E38 ${chalk11.gray(`(${newConfig.model}, ${result.latencyMs}ms)`)}`);
5914
+ testSpinner.succeed(` AI \u8FDE\u901A\u6B63\u5E38 ${chalk12.gray(`(${newConfig.model}, ${result.latencyMs}ms)`)}`);
5726
5915
  } else {
5727
5916
  testSpinner.fail(` AI \u8FDE\u901A\u5F02\u5E38: ${result.message}`);
5728
5917
  }
@@ -5733,37 +5922,40 @@ async function executeChange(_options) {
5733
5922
  }
5734
5923
 
5735
5924
  // src/cli.ts
5736
- var VERSION = "0.3.03";
5925
+ var VERSION = "0.3.05";
5737
5926
  function printLogo() {
5738
5927
  const logo = `
5739
- ${chalk12.bold.cyan(" ___ _ _ _ _ _____ _ _ ")}
5740
- ${chalk12.bold.cyan(" / _ \\ _ _ (_) ___ | | __ / \\ _ _ | |_ ___ |_ _| ___ ___ | |_ (_) _ __ __ _ ")}
5741
- ${chalk12.bold.cyan(" | | | | | | | | | | / __| | |/ / / _ \\ | | | | | __| / _ \\ | | / _ \\ / __| | __| | | | '_ \\ / _` |")}
5742
- ${chalk12.bold.cyan(" | |_| | | |_| | | | | (__ | < / ___ \\ | |_| | | |_ | (_) | | | | __/ \\__ \\ | |_ | | | | | | | (_| |")}
5743
- ${chalk12.bold.cyan(" \\__\\_\\ \\__,_| |_| \\___| |_|\\_\\ /_/ \\_\\ \\__,_| \\__| \\___/ |_| \\___| |___/ \\__| |_| |_| |_| \\__, |")}
5744
- ${chalk12.bold.cyan(" |___/ ")}
5745
- ${chalk12.gray(" CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 v")}${chalk12.green(VERSION)}
5928
+ ${chalk13.bold.cyan(" ___ _ _ _ _ _____ _ _ ")}
5929
+ ${chalk13.bold.cyan(" / _ \\ _ _ (_) ___ | | __ / \\ _ _ | |_ ___ |_ _| ___ ___ | |_ (_) _ __ __ _ ")}
5930
+ ${chalk13.bold.cyan(" | | | | | | | | | | / __| | |/ / / _ \\ | | | | | __| / _ \\ | | / _ \\ / __| | __| | | | '_ \\ / _` |")}
5931
+ ${chalk13.bold.cyan(" | |_| | | |_| | | | | (__ | < / ___ \\ | |_| | | |_ | (_) | | | | __/ \\__ \\ | |_ | | | | | | | (_| |")}
5932
+ ${chalk13.bold.cyan(" \\__\\_\\ \\__,_| |_| \\___| |_|\\_\\ /_/ \\_\\ \\__,_| \\__| \\___/ |_| \\___| |___/ \\__| |_| |_| |_| \\__, |")}
5933
+ ${chalk13.bold.cyan(" |___/ ")}
5934
+ ${chalk13.gray(" CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 v")}${chalk13.green(VERSION)}
5746
5935
  `;
5747
5936
  console.log(logo);
5748
5937
  }
5749
5938
  var program = new Command();
5750
- program.name("qat").description("CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 - \u9762\u5411Vue\u9879\u76EE\uFF0C\u96C6\u6210Vitest\u3001Playwright\uFF0C\u8986\u76D6\u6D4B\u8BD5\u5168\u6D41\u7A0B").version(VERSION).option("-c, --config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA").hook("preAction", async (thisCommand) => {
5939
+ program.name("qat").description("CLI\u81EA\u52A8\u5316\u6D4B\u8BD5\u5DE5\u5177 - \u9762\u5411Vue\u9879\u76EE\uFF0C\u96C6\u6210Vitest\u3001Playwright\uFF0C\u8986\u76D6\u6D4B\u8BD5\u5168\u6D41\u7A0B").version(VERSION).option("-c, --config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("-v, --verbose", "\u663E\u793A\u8BE6\u7EC6\u8F93\u51FA").option("-d, --debug", "\u8C03\u8BD5\u6A21\u5F0F\uFF1A\u663E\u793AAI\u8BF7\u6C42/\u54CD\u5E94\u8BE6\u60C5").hook("preAction", async (thisCommand) => {
5751
5940
  printLogo();
5752
5941
  const opts = thisCommand.opts();
5753
5942
  if (opts.verbose) {
5754
5943
  process.env.QAT_VERBOSE = "true";
5755
5944
  }
5945
+ if (opts.debug) {
5946
+ process.env.QAT_DEBUG = "true";
5947
+ }
5756
5948
  if (opts.config) {
5757
5949
  process.env.QAT_CONFIG_PATH = opts.config;
5758
5950
  }
5759
5951
  const { loadExternalFrameworks } = await import("./framework-registry-YGZ63RDX.js");
5760
5952
  const loadResult = await loadExternalFrameworks(process.cwd());
5761
5953
  if (loadResult.loaded > 0 && opts.verbose) {
5762
- console.log(chalk12.gray(` [ext] \u5DF2\u52A0\u8F7D ${loadResult.loaded} \u4E2A\u5916\u90E8\u6269\u5C55\u6587\u4EF6: ${loadResult.files.join(", ")}`));
5954
+ console.log(chalk13.gray(` [ext] \u5DF2\u52A0\u8F7D ${loadResult.loaded} \u4E2A\u5916\u90E8\u6269\u5C55\u6587\u4EF6: ${loadResult.files.join(", ")}`));
5763
5955
  }
5764
5956
  if (loadResult.errors.length > 0) {
5765
5957
  for (const err of loadResult.errors) {
5766
- console.log(chalk12.yellow(` [ext] \u8B66\u544A: ${err.file} - ${err.error}`));
5958
+ console.log(chalk13.yellow(` [ext] \u8B66\u544A: ${err.file} - ${err.error}`));
5767
5959
  }
5768
5960
  }
5769
5961
  });
@@ -5777,9 +5969,9 @@ registerSetupCommand(program);
5777
5969
  registerStatusCommand(program);
5778
5970
  registerChangeCommand(program);
5779
5971
  program.on("command:*", (operands) => {
5780
- console.error(chalk12.red(`
5972
+ console.error(chalk13.red(`
5781
5973
  \u672A\u77E5\u547D\u4EE4: ${operands[0]}`));
5782
- console.log(chalk12.gray(` \u4F7F\u7528 qat --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4
5974
+ console.log(chalk13.gray(` \u4F7F\u7528 qat --help \u67E5\u770B\u53EF\u7528\u547D\u4EE4
5783
5975
  `));
5784
5976
  process.exit(1);
5785
5977
  });