@nick848/sf-cli 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1772,7 +1772,7 @@ declare function createAgentExecutor(modelService: ModelService, normsManager: N
1772
1772
  declare const FRONTEND_DEV_AGENT: AgentDefinition;
1773
1773
  /**
1774
1774
  * 代码审核 Agent
1775
- * 负责代码质量检查、安全审查、最佳实践建议
1775
+ * 负责代码质量检查、安全审查、最佳实践建议、回归测试
1776
1776
  */
1777
1777
  declare const CODE_REVIEWER_AGENT: AgentDefinition;
1778
1778
  /**
package/dist/index.d.ts CHANGED
@@ -1772,7 +1772,7 @@ declare function createAgentExecutor(modelService: ModelService, normsManager: N
1772
1772
  declare const FRONTEND_DEV_AGENT: AgentDefinition;
1773
1773
  /**
1774
1774
  * 代码审核 Agent
1775
- * 负责代码质量检查、安全审查、最佳实践建议
1775
+ * 负责代码质量检查、安全审查、最佳实践建议、回归测试
1776
1776
  */
1777
1777
  declare const CODE_REVIEWER_AGENT: AgentDefinition;
1778
1778
  /**
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var fs4 = require('fs/promises');
4
- var fsSync = require('fs');
4
+ var fs10 = require('fs');
5
5
  var path4 = require('path');
6
6
  var crypto = require('crypto');
7
7
  var os = require('os');
@@ -12,7 +12,6 @@ var chalk9 = require('chalk');
12
12
  var uuid = require('uuid');
13
13
  var enquirer = require('enquirer');
14
14
  var child_process = require('child_process');
15
- var module$1 = require('module');
16
15
 
17
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
17
 
@@ -35,15 +34,13 @@ function _interopNamespace(e) {
35
34
  }
36
35
 
37
36
  var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
38
- var fsSync__namespace = /*#__PURE__*/_interopNamespace(fsSync);
37
+ var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
39
38
  var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
40
39
  var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
41
40
  var os__namespace = /*#__PURE__*/_interopNamespace(os);
42
41
  var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
43
42
 
44
- // node_modules/tsup/assets/cjs_shims.js
45
- var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
46
- var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
43
+ // src/services/config.ts
47
44
  var DEFAULT_CONFIG = {
48
45
  model: "GLM-5",
49
46
  apiKey: "",
@@ -58,8 +55,8 @@ var KEY_FILE = ".key";
58
55
  function getOrCreateEncryptionKey() {
59
56
  const keyPath = path4__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
60
57
  try {
61
- if (fsSync__namespace.existsSync(keyPath)) {
62
- const keyBase64 = fsSync__namespace.readFileSync(keyPath, "utf-8").trim();
58
+ if (fs10__namespace.existsSync(keyPath)) {
59
+ const keyBase64 = fs10__namespace.readFileSync(keyPath, "utf-8").trim();
63
60
  return Buffer.from(keyBase64, "base64");
64
61
  }
65
62
  } catch {
@@ -67,10 +64,10 @@ function getOrCreateEncryptionKey() {
67
64
  const key = crypto__namespace.randomBytes(32);
68
65
  try {
69
66
  const keyDir = path4__namespace.dirname(keyPath);
70
- if (!fsSync__namespace.existsSync(keyDir)) {
71
- fsSync__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
67
+ if (!fs10__namespace.existsSync(keyDir)) {
68
+ fs10__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
72
69
  }
73
- fsSync__namespace.writeFileSync(keyPath, key.toString("base64"), {
70
+ fs10__namespace.writeFileSync(keyPath, key.toString("base64"), {
74
71
  mode: 384,
75
72
  // 仅所有者可读写
76
73
  encoding: "utf-8"
@@ -377,7 +374,7 @@ var BaseAdapter = class {
377
374
  * 延迟工具函数
378
375
  */
379
376
  delay(ms) {
380
- return new Promise((resolve2) => setTimeout(resolve2, ms));
377
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
381
378
  }
382
379
  };
383
380
 
@@ -1822,17 +1819,17 @@ var FigmaMCPAdapter = class extends MCPAdapterBase {
1822
1819
  * 解析设计规格
1823
1820
  */
1824
1821
  parseDesignSpec(data, url, fileKey, nodeId) {
1825
- const document2 = data.document || data;
1822
+ const document = data.document || data;
1826
1823
  return {
1827
1824
  id: nodeId || fileKey,
1828
- name: document2.name || "\u672A\u547D\u540D\u8BBE\u8BA1\u7A3F",
1825
+ name: document.name || "\u672A\u547D\u540D\u8BBE\u8BA1\u7A3F",
1829
1826
  platform: "figma",
1830
1827
  url,
1831
- width: document2.absoluteBoundingBox?.width || 0,
1832
- height: document2.absoluteBoundingBox?.height || 0,
1828
+ width: document.absoluteBoundingBox?.width || 0,
1829
+ height: document.absoluteBoundingBox?.height || 0,
1833
1830
  scale: 1,
1834
- layers: this.parseFigmaLayers(document2.children || []),
1835
- styles: this.extractStylesFromDocument(document2),
1831
+ layers: this.parseFigmaLayers(document.children || []),
1832
+ styles: this.extractStylesFromDocument(document),
1836
1833
  assets: this.parseAssets(data.assets || []),
1837
1834
  extractedAt: /* @__PURE__ */ new Date()
1838
1835
  };
@@ -1928,7 +1925,7 @@ var FigmaMCPAdapter = class extends MCPAdapterBase {
1928
1925
  /**
1929
1926
  * 从文档提取全局样式
1930
1927
  */
1931
- extractStylesFromDocument(document2) {
1928
+ extractStylesFromDocument(document) {
1932
1929
  const styles = {
1933
1930
  colors: [],
1934
1931
  typography: [],
@@ -1954,7 +1951,7 @@ var FigmaMCPAdapter = class extends MCPAdapterBase {
1954
1951
  node.children.forEach(traverse);
1955
1952
  }
1956
1953
  };
1957
- traverse(document2);
1954
+ traverse(document);
1958
1955
  styles.colors = this.deduplicateColors(styles.colors);
1959
1956
  styles.typography = this.deduplicateTypography(styles.typography);
1960
1957
  return styles;
@@ -2547,10 +2544,10 @@ var FRONTEND_DEV_AGENT = {
2547
2544
  var CODE_REVIEWER_AGENT = {
2548
2545
  id: "code-reviewer",
2549
2546
  name: "\u4EE3\u7801\u5BA1\u6838",
2550
- description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
2547
+ description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
2551
2548
  icon: "\u{1F50D}",
2552
- version: "1.0.0",
2553
- role: "\u4F60\u662F\u4E00\u540D\u8D44\u6DF1\u4EE3\u7801\u5BA1\u6838\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u6700\u4F73\u5B9E\u8DF5\u3002\u4F60\u8D1F\u8D23\u5BA1\u67E5\u4EE3\u7801\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE\u3002",
2549
+ version: "1.1.0",
2550
+ role: "\u4F60\u662F\u4E00\u540D\u8D44\u6DF1\u4EE3\u7801\u5BA1\u6838\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u6700\u4F73\u5B9E\u8DF5\u3002\u4F60\u8D1F\u8D23\u5BA1\u67E5\u4EE3\u7801\u3001\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE\u3002",
2554
2551
  capabilities: [
2555
2552
  {
2556
2553
  id: "quality-review",
@@ -2571,13 +2568,24 @@ var CODE_REVIEWER_AGENT = {
2571
2568
  id: "performance-review",
2572
2569
  name: "\u6027\u80FD\u5BA1\u67E5",
2573
2570
  description: "\u68C0\u67E5\u6027\u80FD\u95EE\u9898\u548C\u4F18\u5316\u5EFA\u8BAE"
2571
+ },
2572
+ {
2573
+ id: "regression-test",
2574
+ name: "\u56DE\u5F52\u6D4B\u8BD5",
2575
+ description: "\u6267\u884C\u6D4B\u8BD5\u5957\u4EF6\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD"
2576
+ },
2577
+ {
2578
+ id: "coverage-analysis",
2579
+ name: "\u8986\u76D6\u7387\u5206\u6790",
2580
+ description: "\u5206\u6790\u6D4B\u8BD5\u8986\u76D6\u7387\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE"
2574
2581
  }
2575
2582
  ],
2576
2583
  tools: [
2577
2584
  { name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
2578
2585
  { name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
2579
2586
  { name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
2580
- { name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" }
2587
+ { name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" },
2588
+ { name: "run_shell_command", description: "\u6267\u884C\u6D4B\u8BD5\u547D\u4EE4", permission: "confirm" }
2581
2589
  ],
2582
2590
  triggers: [
2583
2591
  { type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
@@ -2588,6 +2596,7 @@ var CODE_REVIEWER_AGENT = {
2588
2596
  protectedPaths: ["node_modules", ".git"]
2589
2597
  },
2590
2598
  behavior: {
2599
+ requireConfirmation: ["run_shell_command"],
2591
2600
  autoCommit: false
2592
2601
  }
2593
2602
  },
@@ -2596,6 +2605,7 @@ var CODE_REVIEWER_AGENT = {
2596
2605
  ## \u4F60\u7684\u804C\u8D23
2597
2606
  - \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
2598
2607
  - \u68C0\u67E5\u5B89\u5168\u6F0F\u6D1E\u548C\u98CE\u9669
2608
+ - \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD
2599
2609
  - \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
2600
2610
  - \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
2601
2611
 
@@ -2604,8 +2614,20 @@ var CODE_REVIEWER_AGENT = {
2604
2614
  2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
2605
2615
  3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
2606
2616
  4. **\u89C4\u8303\u6027**: \u547D\u540D\u3001\u683C\u5F0F\u3001\u6CE8\u91CA
2617
+ 5. **\u6D4B\u8BD5\u8986\u76D6**: \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C\u3001\u8986\u76D6\u7387\u5206\u6790
2618
+
2619
+ ## \u56DE\u5F52\u6D4B\u8BD5\u6D41\u7A0B
2620
+ 1. \u6267\u884C \`npm test -- --run\` \u8FD0\u884C\u6D4B\u8BD5\u5957\u4EF6
2621
+ 2. \u5206\u6790\u6D4B\u8BD5\u7ED3\u679C\uFF0C\u8BC6\u522B\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B
2622
+ 3. \u5982\u679C\u6D4B\u8BD5\u5931\u8D25\uFF0C\u963B\u6B62\u5F52\u6863\u5E76\u63D0\u793A\u4FEE\u590D
2623
+ 4. \u63D0\u4F9B\u8986\u76D6\u7387\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE
2607
2624
 
2608
2625
  ## \u8F93\u51FA\u683C\u5F0F
2626
+ ### \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C
2627
+ - \u6D4B\u8BD5\u901A\u8FC7/\u5931\u8D25\u72B6\u6001
2628
+ - \u901A\u8FC7/\u5931\u8D25\u6570\u91CF
2629
+ - \u8986\u76D6\u7387\u767E\u5206\u6BD4
2630
+
2609
2631
  ### \u5BA1\u67E5\u7ED3\u679C
2610
2632
  - \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
2611
2633
 
@@ -2631,7 +2653,7 @@ var CODE_REVIEWER_AGENT = {
2631
2653
  ## \u4E0A\u4E0B\u6587
2632
2654
  {{context}}
2633
2655
 
2634
- \u8BF7\u5BF9\u4EE5\u4E0A\u6587\u4EF6\u8FDB\u884C\u5168\u9762\u5BA1\u67E5\uFF0C\u63D0\u4F9B\u8BE6\u7EC6\u7684\u5BA1\u67E5\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE\u3002`,
2656
+ \u8BF7\u5BF9\u4EE5\u4E0A\u6587\u4EF6\u8FDB\u884C\u5168\u9762\u5BA1\u67E5\uFF0C\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u5E76\u63D0\u4F9B\u8BE6\u7EC6\u7684\u5BA1\u67E5\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE\u3002`,
2635
2657
  outputFormat: {
2636
2658
  type: "markdown"
2637
2659
  }
@@ -5621,8 +5643,8 @@ var CommandParser = class {
5621
5643
  };
5622
5644
  }
5623
5645
  parseAtPath(input) {
5624
- const path11 = input.slice(1).trim();
5625
- if (!path11) {
5646
+ const path14 = input.slice(1).trim();
5647
+ if (!path14) {
5626
5648
  return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
5627
5649
  }
5628
5650
  return {
@@ -5630,7 +5652,7 @@ var CommandParser = class {
5630
5652
  command: {
5631
5653
  type: "at" /* AT */,
5632
5654
  raw: input,
5633
- path: path11
5655
+ path: path14
5634
5656
  }
5635
5657
  };
5636
5658
  }
@@ -6474,8 +6496,26 @@ function createSpinner(message) {
6474
6496
  stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
6475
6497
  };
6476
6498
  }
6477
- var CURRENT_VERSION = "1.0.0";
6478
- var PACKAGE_NAME = "sf-cli";
6499
+ function getPackageInfo() {
6500
+ const possiblePaths = [
6501
+ path4__namespace.resolve(__dirname, "..", "..", "package.json"),
6502
+ path4__namespace.resolve(__dirname, "..", "..", "..", "package.json")
6503
+ ];
6504
+ for (const pkgPath of possiblePaths) {
6505
+ try {
6506
+ if (fs10__namespace.existsSync(pkgPath)) {
6507
+ const content = fs10__namespace.readFileSync(pkgPath, "utf-8");
6508
+ return JSON.parse(content);
6509
+ }
6510
+ } catch {
6511
+ continue;
6512
+ }
6513
+ }
6514
+ return { version: "1.0.0", name: "@nick848/sf-cli" };
6515
+ }
6516
+ var packageInfo = getPackageInfo();
6517
+ var CURRENT_VERSION = packageInfo.version;
6518
+ var PACKAGE_NAME = packageInfo.name;
6479
6519
  async function handleUpdate(args, ctx) {
6480
6520
  const options = {
6481
6521
  check: args.includes("--check") || args.includes("-c"),
@@ -6502,6 +6542,9 @@ async function checkForUpdates() {
6502
6542
  const latestVersion = await getLatestVersion();
6503
6543
  if (!latestVersion) {
6504
6544
  lines.push(chalk9__default.default.yellow("\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
6545
+ lines.push(chalk9__default.default.gray("\u53EF\u80FD\u539F\u56E0:"));
6546
+ lines.push(chalk9__default.default.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
6547
+ lines.push(chalk9__default.default.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
6505
6548
  return { output: lines.join("\n") };
6506
6549
  }
6507
6550
  lines.push(chalk9__default.default.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
@@ -6520,43 +6563,70 @@ async function checkForUpdates() {
6520
6563
  async function performUpdate(targetVersion) {
6521
6564
  const lines = [];
6522
6565
  lines.push(chalk9__default.default.cyan("\u6B63\u5728\u66F4\u65B0 sf-cli..."));
6566
+ lines.push(chalk9__default.default.gray(` \u5305\u540D: ${PACKAGE_NAME}`));
6567
+ lines.push(chalk9__default.default.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
6523
6568
  try {
6524
6569
  const latestVersion = await getLatestVersion();
6570
+ if (!latestVersion && !targetVersion) {
6571
+ lines.push(chalk9__default.default.yellow("\n\u26A0 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
6572
+ lines.push(chalk9__default.default.gray("\n\u53EF\u80FD\u539F\u56E0:"));
6573
+ lines.push(chalk9__default.default.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
6574
+ lines.push(chalk9__default.default.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
6575
+ lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
6576
+ lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
6577
+ return { output: lines.join("\n") };
6578
+ }
6525
6579
  if (latestVersion === CURRENT_VERSION && !targetVersion) {
6526
- lines.push(chalk9__default.default.green("\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
6580
+ lines.push(chalk9__default.default.green("\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
6527
6581
  return { output: lines.join("\n") };
6528
6582
  }
6529
6583
  const packageSpec = targetVersion ? `${PACKAGE_NAME}@${targetVersion}` : `${PACKAGE_NAME}@latest`;
6530
6584
  lines.push(chalk9__default.default.gray(` \u5B89\u88C5: ${packageSpec}`));
6531
- await new Promise((resolve2, reject) => {
6585
+ const installResult = await new Promise((resolve4) => {
6532
6586
  const proc = child_process.spawn("npm", ["install", "-g", packageSpec], {
6533
6587
  stdio: "pipe",
6534
6588
  shell: true
6535
6589
  });
6590
+ let output = "";
6591
+ proc.stdout?.on("data", (data) => {
6592
+ output += data.toString();
6593
+ });
6594
+ proc.stderr?.on("data", (data) => {
6595
+ output += data.toString();
6596
+ });
6536
6597
  proc.on("close", (code) => {
6537
- if (code === 0) {
6538
- resolve2();
6539
- } else {
6540
- reject(new Error(`npm install exited with code ${code}`));
6541
- }
6598
+ resolve4({
6599
+ success: code === 0,
6600
+ output
6601
+ });
6542
6602
  });
6543
6603
  proc.on("error", (err) => {
6544
- reject(err);
6604
+ resolve4({
6605
+ success: false,
6606
+ output: err.message
6607
+ });
6545
6608
  });
6546
6609
  });
6547
- lines.push(chalk9__default.default.green("\u2713 \u66F4\u65B0\u5B8C\u6210!"));
6610
+ if (!installResult.success) {
6611
+ lines.push(chalk9__default.default.red("\n\u2717 \u66F4\u65B0\u5931\u8D25"));
6612
+ lines.push(chalk9__default.default.gray(installResult.output));
6613
+ lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
6614
+ lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
6615
+ return { output: lines.join("\n") };
6616
+ }
6617
+ lines.push(chalk9__default.default.green("\n\u2713 \u66F4\u65B0\u5B8C\u6210!"));
6548
6618
  lines.push(chalk9__default.default.gray(` \u65B0\u7248\u672C: v${targetVersion || latestVersion}`));
6549
6619
  lines.push(chalk9__default.default.gray("\n\u8BF7\u91CD\u542F CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"));
6550
6620
  } catch (error) {
6551
6621
  lines.push(chalk9__default.default.red("\u66F4\u65B0\u5931\u8D25: " + error.message));
6552
6622
  lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
6553
- lines.push(chalk9__default.default.gray(" npm install -g sf-cli@latest"));
6623
+ lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
6554
6624
  }
6555
6625
  return { output: lines.join("\n") };
6556
6626
  }
6557
6627
  async function getLatestVersion() {
6558
6628
  try {
6559
- const result = await new Promise((resolve2, reject) => {
6629
+ const result = await new Promise((resolve4, reject) => {
6560
6630
  const proc = child_process.spawn("npm", ["view", PACKAGE_NAME, "version"], {
6561
6631
  stdio: "pipe",
6562
6632
  shell: true
@@ -6566,13 +6636,17 @@ async function getLatestVersion() {
6566
6636
  output += data.toString();
6567
6637
  });
6568
6638
  proc.on("close", (code) => {
6569
- if (code === 0) {
6570
- resolve2(output.trim());
6639
+ if (code === 0 && output.trim()) {
6640
+ resolve4(output.trim());
6571
6641
  } else {
6572
6642
  reject(new Error("Failed to get version"));
6573
6643
  }
6574
6644
  });
6575
6645
  proc.on("error", reject);
6646
+ setTimeout(() => {
6647
+ proc.kill();
6648
+ reject(new Error("Timeout"));
6649
+ }, 1e4);
6576
6650
  });
6577
6651
  return result || null;
6578
6652
  } catch {
@@ -6895,6 +6969,78 @@ function extractTitle(requirement) {
6895
6969
  return requirement.slice(0, 47) + "...";
6896
6970
  }
6897
6971
  var autoScheduleEnabled = true;
6972
+ var DEFAULT_REGRESSION_CONFIG = {
6973
+ enabled: true,
6974
+ command: "npm test -- --run",
6975
+ timeout: 12e4,
6976
+ // 2分钟
6977
+ coverageThreshold: 80
6978
+ };
6979
+ async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_CONFIG) {
6980
+ const startTime = Date.now();
6981
+ const result = {
6982
+ success: false,
6983
+ passed: 0,
6984
+ failed: 0,
6985
+ total: 0,
6986
+ duration: 0,
6987
+ output: "",
6988
+ errors: []
6989
+ };
6990
+ return new Promise((resolve4) => {
6991
+ const proc = child_process.spawn(config.command, [], {
6992
+ cwd: workingDirectory,
6993
+ shell: true,
6994
+ stdio: "pipe"
6995
+ });
6996
+ let stdout = "";
6997
+ let stderr = "";
6998
+ proc.stdout?.on("data", (data) => {
6999
+ stdout += data.toString();
7000
+ });
7001
+ proc.stderr?.on("data", (data) => {
7002
+ stderr += data.toString();
7003
+ });
7004
+ const timeout = setTimeout(() => {
7005
+ proc.kill();
7006
+ result.errors.push("\u6D4B\u8BD5\u8D85\u65F6");
7007
+ result.output = stdout + stderr;
7008
+ result.duration = Date.now() - startTime;
7009
+ resolve4(result);
7010
+ }, config.timeout);
7011
+ proc.on("close", (code) => {
7012
+ clearTimeout(timeout);
7013
+ result.output = stdout + stderr;
7014
+ result.duration = Date.now() - startTime;
7015
+ const passMatch = stdout.match(/(\d+)\s+(?:passed|tests?\s+passed)/i);
7016
+ const failMatch = stdout.match(/(\d+)\s+(?:failed|tests?\s+failed)/i);
7017
+ const totalMatch = stdout.match(/Tests?:\s*(\d+)/i);
7018
+ if (passMatch) {
7019
+ result.passed = parseInt(passMatch[1], 10);
7020
+ }
7021
+ if (failMatch) {
7022
+ result.failed = parseInt(failMatch[1], 10);
7023
+ }
7024
+ if (totalMatch) {
7025
+ result.total = parseInt(totalMatch[1], 10);
7026
+ } else {
7027
+ result.total = result.passed + result.failed;
7028
+ }
7029
+ const coverageMatch = stdout.match(/All files[^\d]*(\d+(?:\.\d+)?)/);
7030
+ if (coverageMatch) {
7031
+ result.coverage = parseFloat(coverageMatch[1]);
7032
+ }
7033
+ result.success = code === 0 && result.failed === 0;
7034
+ resolve4(result);
7035
+ });
7036
+ proc.on("error", (err) => {
7037
+ clearTimeout(timeout);
7038
+ result.errors.push(err.message);
7039
+ result.duration = Date.now() - startTime;
7040
+ resolve4(result);
7041
+ });
7042
+ });
7043
+ }
6898
7044
  async function handleOpsx(command, args, ctx) {
6899
7045
  const step = command.replace("opsx:", "");
6900
7046
  const workflow = new WorkflowEngine();
@@ -6909,7 +7055,7 @@ async function handleOpsx(command, args, ctx) {
6909
7055
  case "apply":
6910
7056
  return handleApply(workflow);
6911
7057
  case "archive":
6912
- return handleArchive(workflow, args);
7058
+ return handleArchive(workflow, args, ctx);
6913
7059
  case "propose":
6914
7060
  return handlePropose(workflow, args);
6915
7061
  case "status":
@@ -6924,12 +7070,50 @@ async function handleOpsx(command, args, ctx) {
6924
7070
  return handleNext(workflow);
6925
7071
  case "auto":
6926
7072
  return handleAutoSchedule(args);
7073
+ case "test":
7074
+ return handleRegressionTest(ctx);
6927
7075
  default:
6928
7076
  return {
6929
7077
  output: chalk9__default.default.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
6930
7078
  };
6931
7079
  }
6932
7080
  }
7081
+ async function handleRegressionTest(ctx) {
7082
+ const lines = [];
7083
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5..."));
7084
+ lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`));
7085
+ lines.push("");
7086
+ const result = await runRegressionTest(ctx.options.workingDirectory);
7087
+ lines.push(chalk9__default.default.gray("\u2500".repeat(50)));
7088
+ if (result.success) {
7089
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
7090
+ } else {
7091
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
7092
+ }
7093
+ lines.push("");
7094
+ lines.push(chalk9__default.default.cyan("\u6D4B\u8BD5\u7ED3\u679C:"));
7095
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${result.passed}`));
7096
+ lines.push(chalk9__default.default.gray(` \u5931\u8D25: ${result.failed}`));
7097
+ lines.push(chalk9__default.default.gray(` \u603B\u8BA1: ${result.total}`));
7098
+ lines.push(chalk9__default.default.gray(` \u8017\u65F6: ${(result.duration / 1e3).toFixed(2)}s`));
7099
+ if (result.coverage !== void 0) {
7100
+ const coverageColor = result.coverage >= 80 ? chalk9__default.default.green : result.coverage >= 60 ? chalk9__default.default.yellow : chalk9__default.default.red;
7101
+ lines.push(coverageColor(` \u8986\u76D6\u7387: ${result.coverage}%`));
7102
+ }
7103
+ if (result.errors.length > 0) {
7104
+ lines.push("");
7105
+ lines.push(chalk9__default.default.red("\u9519\u8BEF\u4FE1\u606F:"));
7106
+ for (const error of result.errors) {
7107
+ lines.push(chalk9__default.default.gray(` - ${error}`));
7108
+ }
7109
+ }
7110
+ if (result.failed > 0) {
7111
+ lines.push("");
7112
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u8BF7\u68C0\u67E5\u5E76\u4FEE\u590D"));
7113
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
7114
+ }
7115
+ return { output: lines.join("\n") };
7116
+ }
6933
7117
  function handleAutoSchedule(args) {
6934
7118
  const action = args[0]?.toLowerCase();
6935
7119
  if (!action) {
@@ -7059,13 +7243,70 @@ async function handleArchive(workflow, args, ctx) {
7059
7243
  ${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
7060
7244
  };
7061
7245
  }
7246
+ const lines = [];
7247
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u5F52\u6863\u524D\u56DE\u5F52\u6D4B\u8BD5..."));
7248
+ lines.push("");
7249
+ const testResult = await runRegressionTest(ctx.options.workingDirectory);
7250
+ if (!testResult.success) {
7251
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
7252
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u5931\u8D25: ${testResult.failed} | \u603B\u8BA1: ${testResult.total}`));
7253
+ lines.push("");
7254
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5F52\u6863\u88AB\u963B\u6B62"));
7255
+ lines.push(chalk9__default.default.gray("\n\u8BF7\u4FEE\u590D\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\u540E\u91CD\u8BD5"));
7256
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
7257
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:test \u53EF\u5355\u72EC\u8FD0\u884C\u56DE\u5F52\u6D4B\u8BD5"));
7258
+ return { output: lines.join("\n") };
7259
+ }
7260
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
7261
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u8017\u65F6: ${(testResult.duration / 1e3).toFixed(2)}s`));
7262
+ lines.push("");
7062
7263
  const changeId = state.id;
7063
7264
  const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
7064
7265
  await workflow.archive(summary);
7065
- return {
7066
- output: chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
7067
- \u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
7068
- };
7266
+ await updateChangelog(ctx.options.workingDirectory, summary, changeId);
7267
+ lines.push(chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
7268
+ \u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55") + chalk9__default.default.gray("\nCHANGELOG.md \u5DF2\u66F4\u65B0"));
7269
+ return { output: lines.join("\n") };
7270
+ }
7271
+ async function updateChangelog(workingDirectory, summary, changeId) {
7272
+ try {
7273
+ const changelogPath = path4__namespace.join(workingDirectory, "CHANGELOG.md");
7274
+ const pkgPath = path4__namespace.join(workingDirectory, "package.json");
7275
+ let version = "1.0.0";
7276
+ try {
7277
+ const pkgContent = fs10__namespace.readFileSync(pkgPath, "utf-8");
7278
+ const pkg = JSON.parse(pkgContent);
7279
+ version = pkg.version || "1.0.0";
7280
+ } catch {
7281
+ }
7282
+ const today = /* @__PURE__ */ new Date();
7283
+ const dateStr = today.toISOString().split("T")[0];
7284
+ const entry = `
7285
+ ## v${version} (${dateStr})
7286
+
7287
+ **\u53D8\u66F4\u5185\u5BB9**
7288
+
7289
+ - ${summary} (${changeId})
7290
+ `;
7291
+ if (fs10__namespace.existsSync(changelogPath)) {
7292
+ const content = fs10__namespace.readFileSync(changelogPath, "utf-8");
7293
+ const versionPattern = /^## v\d+\.\d+\.\d+/m;
7294
+ const match = content.match(versionPattern);
7295
+ if (match && match.index !== void 0) {
7296
+ const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
7297
+ fs10__namespace.writeFileSync(changelogPath, newContent, "utf-8");
7298
+ } else {
7299
+ fs10__namespace.appendFileSync(changelogPath, entry, "utf-8");
7300
+ }
7301
+ } else {
7302
+ const header = `# Changelog
7303
+
7304
+ All notable changes to this project will be documented in this file.
7305
+ `;
7306
+ fs10__namespace.writeFileSync(changelogPath, header + entry, "utf-8");
7307
+ }
7308
+ } catch (error) {
7309
+ }
7069
7310
  }
7070
7311
  async function handlePropose(workflow, args, ctx) {
7071
7312
  const state = workflow.getState();
@@ -7254,9 +7495,27 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
7254
7495
  throw e;
7255
7496
  }
7256
7497
  }
7257
- var require2 = module$1.createRequire(importMetaUrl);
7258
- var packageJson = require2("../../package.json");
7259
- var VERSION2 = packageJson.version;
7498
+
7499
+ // src/commands/runner.ts
7500
+ function getVersion() {
7501
+ const possiblePaths = [
7502
+ path4__namespace.resolve(__dirname, "..", "..", "package.json"),
7503
+ path4__namespace.resolve(__dirname, "..", "..", "..", "package.json")
7504
+ ];
7505
+ for (const pkgPath of possiblePaths) {
7506
+ try {
7507
+ if (fs10__namespace.existsSync(pkgPath)) {
7508
+ const content = fs10__namespace.readFileSync(pkgPath, "utf-8");
7509
+ const pkg = JSON.parse(content);
7510
+ return pkg.version;
7511
+ }
7512
+ } catch {
7513
+ continue;
7514
+ }
7515
+ }
7516
+ return "1.0.0";
7517
+ }
7518
+ var VERSION2 = getVersion();
7260
7519
  async function runSlashCommand(command, args, ctx) {
7261
7520
  const normalizedCommand = normalizeCommand(command);
7262
7521
  switch (normalizedCommand) {
@@ -7351,7 +7610,7 @@ async function executeShell(command, ctx) {
7351
7610
  \u547D\u4EE4 "${command}" \u53EF\u80FD\u4F1A\u5220\u9664\u91CD\u8981\u6587\u4EF6`) + chalk9__default.default.gray("\n\u4F7F\u7528 yolo \u6A21\u5F0F\u5F3A\u5236\u6267\u884C")
7352
7611
  };
7353
7612
  }
7354
- return new Promise((resolve2) => {
7613
+ return new Promise((resolve4) => {
7355
7614
  const shell = child_process.spawn(command, [], {
7356
7615
  shell: true,
7357
7616
  cwd: ctx.options.workingDirectory
@@ -7376,11 +7635,11 @@ async function executeShell(command, ctx) {
7376
7635
  output += chalk9__default.default.red(`
7377
7636
  \u9000\u51FA\u7801: ${code}`);
7378
7637
  }
7379
- resolve2({ output: output || chalk9__default.default.gray("(\u65E0\u8F93\u51FA)") });
7638
+ resolve4({ output: output || chalk9__default.default.gray("(\u65E0\u8F93\u51FA)") });
7380
7639
  });
7381
7640
  setTimeout(() => {
7382
7641
  shell.kill();
7383
- resolve2({
7642
+ resolve4({
7384
7643
  output: chalk9__default.default.yellow("\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0C\u5DF2\u7EC8\u6B62")
7385
7644
  });
7386
7645
  }, 6e4);