@nick848/sf-cli 1.0.2 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## v1.0.3 (2026-03-22)
9
+
10
+ **新增功能**
11
+
12
+ - 📝 CHANGELOG.md - 版本更新历史独立文件
13
+ - 🔄 归档自动更新 - 工作流归档时自动追加变更条目
14
+ - 📦 发布包含 - npm 包包含 CHANGELOG.md
15
+
16
+ ## v1.0.2 (2026-03-22)
17
+
18
+ **新增功能**
19
+
20
+ - ✅ 回归测试机制 - `/opsx:test` 命令执行测试
21
+ - 🔄 归档前自动测试 - 失败则阻止归档
22
+ - 📊 测试覆盖率检查 - 可配置阈值
23
+ - 🔧 code-reviewer Agent 增强 - 新增测试能力
24
+
25
+ ## v1.0.1 (2026-03-21)
26
+
27
+ **新增功能**
28
+
29
+ - 🔒 强制工作流机制 - 所有代码修改必须走工作流流程
30
+ - 📊 状态栏显示工作流阶段
31
+ - 🔐 各阶段权限控制 - 读/写/Shell/Agent 受限
32
+
33
+ ## v1.0.0 (2026-03-21)
34
+
35
+ **首次发布**
36
+
37
+ - 🤖 AI驱动 - 支持 GLM-5、GPT-4o、Claude 等多种AI模型
38
+ - 📋 标准化流程 - BDD+TDD+OpenSpec 规范支持
39
+ - 📚 规范学习 - 自动从代码和对话中学习开发规范
40
+ - 🔧 Sub Agent - 专业化 Agent 协作(前端开发、代码审核、架构师、测试)
41
+ - 🔄 工作流控制 - 三阶段人工确认、回滚机制
42
+ - 🔌 MCP集成 - 支持蓝湖、Figma 设计稿
43
+ - 💻 双命令入口 - `sf-cli` 和 `spfe` 均可启动
44
+ - 📝 交互式命令 - 支持 `/` 斜杠命令和命令行参数模式
package/README.md CHANGED
@@ -184,29 +184,10 @@ await norms.scanProject(projectPath);
184
184
  const result = await runAgent('frontend-dev', ['创建登录组件'], ctx);
185
185
  ```
186
186
 
187
- ## Changelog
188
-
189
- ### v1.0.1 (2026-03-21)
190
-
191
- **新增功能**
192
-
193
- - 🔒 强制工作流机制 - 所有代码修改必须走工作流流程
194
- - 📊 状态栏显示工作流阶段
195
- - 🔐 各阶段权限控制 - 读/写/Shell/Agent 受限
196
-
197
- ### v1.0.0 (2026-03-21)
198
-
199
- **首次发布**
200
-
201
- - 🤖 AI驱动 - 支持 GLM-5、GPT-4o、Claude 等多种AI模型
202
- - 📋 标准化流程 - BDD+TDD+OpenSpec 规范支持
203
- - 📚 规范学习 - 自动从代码和对话中学习开发规范
204
- - 🔧 Sub Agent - 专业化 Agent 协作(前端开发、代码审核、架构师、测试)
205
- - 🔄 工作流控制 - 三阶段人工确认、回滚机制
206
- - 🔌 MCP集成 - 支持蓝湖、Figma 设计稿
207
- - 💻 双命令入口 - `sf-cli` 和 `spfe` 均可启动
208
- - 📝 交互式命令 - 支持 `/` 斜杠命令和命令行参数模式
209
-
210
187
  ## License
211
188
 
212
189
  [MIT](LICENSE)
190
+
191
+ ## Changelog
192
+
193
+ 查看 [CHANGELOG.md](./CHANGELOG.md) 了解版本更新历史。
package/dist/cli/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  var commander = require('commander');
5
5
  var chalk9 = require('chalk');
6
- var fsSync = require('fs');
6
+ var fs9 = require('fs');
7
7
  var path6 = require('path');
8
8
  var readline = require('readline');
9
9
  var fs6 = require('fs/promises');
@@ -37,7 +37,7 @@ function _interopNamespace(e) {
37
37
  }
38
38
 
39
39
  var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
40
- var fsSync__namespace = /*#__PURE__*/_interopNamespace(fsSync);
40
+ var fs9__namespace = /*#__PURE__*/_interopNamespace(fs9);
41
41
  var path6__namespace = /*#__PURE__*/_interopNamespace(path6);
42
42
  var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
43
43
  var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
@@ -125,8 +125,8 @@ var CommandParser = class {
125
125
  };
126
126
  }
127
127
  parseAtPath(input) {
128
- const path15 = input.slice(1).trim();
129
- if (!path15) {
128
+ const path16 = input.slice(1).trim();
129
+ if (!path16) {
130
130
  return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
131
131
  }
132
132
  return {
@@ -134,7 +134,7 @@ var CommandParser = class {
134
134
  command: {
135
135
  type: "at" /* AT */,
136
136
  raw: input,
137
- path: path15
137
+ path: path16
138
138
  }
139
139
  };
140
140
  }
@@ -1705,8 +1705,8 @@ var KEY_FILE = ".key";
1705
1705
  function getOrCreateEncryptionKey() {
1706
1706
  const keyPath = path6__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
1707
1707
  try {
1708
- if (fsSync__namespace.existsSync(keyPath)) {
1709
- const keyBase64 = fsSync__namespace.readFileSync(keyPath, "utf-8").trim();
1708
+ if (fs9__namespace.existsSync(keyPath)) {
1709
+ const keyBase64 = fs9__namespace.readFileSync(keyPath, "utf-8").trim();
1710
1710
  return Buffer.from(keyBase64, "base64");
1711
1711
  }
1712
1712
  } catch {
@@ -1714,10 +1714,10 @@ function getOrCreateEncryptionKey() {
1714
1714
  const key = crypto__namespace.randomBytes(32);
1715
1715
  try {
1716
1716
  const keyDir = path6__namespace.dirname(keyPath);
1717
- if (!fsSync__namespace.existsSync(keyDir)) {
1718
- fsSync__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
1717
+ if (!fs9__namespace.existsSync(keyDir)) {
1718
+ fs9__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
1719
1719
  }
1720
- fsSync__namespace.writeFileSync(keyPath, key.toString("base64"), {
1720
+ fs9__namespace.writeFileSync(keyPath, key.toString("base64"), {
1721
1721
  mode: 384,
1722
1722
  // 仅所有者可读写
1723
1723
  encoding: "utf-8"
@@ -3081,10 +3081,26 @@ function createSpinner(message) {
3081
3081
  stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
3082
3082
  };
3083
3083
  }
3084
- var packageJsonPath = path6__namespace.resolve(__dirname, "../../package.json");
3085
- var packageJson = JSON.parse(fsSync__namespace.readFileSync(packageJsonPath, "utf-8"));
3086
- var CURRENT_VERSION = packageJson.version;
3087
- var PACKAGE_NAME = packageJson.name;
3084
+ function getPackageInfo() {
3085
+ const possiblePaths = [
3086
+ path6__namespace.resolve(__dirname, "..", "..", "package.json"),
3087
+ path6__namespace.resolve(__dirname, "..", "..", "..", "package.json")
3088
+ ];
3089
+ for (const pkgPath of possiblePaths) {
3090
+ try {
3091
+ if (fs9__namespace.existsSync(pkgPath)) {
3092
+ const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
3093
+ return JSON.parse(content);
3094
+ }
3095
+ } catch {
3096
+ continue;
3097
+ }
3098
+ }
3099
+ return { version: "1.0.0", name: "@nick848/sf-cli" };
3100
+ }
3101
+ var packageInfo = getPackageInfo();
3102
+ var CURRENT_VERSION = packageInfo.version;
3103
+ var PACKAGE_NAME = packageInfo.name;
3088
3104
  async function handleUpdate(args, ctx) {
3089
3105
  const options = {
3090
3106
  check: args.includes("--check") || args.includes("-c"),
@@ -4343,10 +4359,10 @@ var FRONTEND_DEV_AGENT = {
4343
4359
  var CODE_REVIEWER_AGENT = {
4344
4360
  id: "code-reviewer",
4345
4361
  name: "\u4EE3\u7801\u5BA1\u6838",
4346
- description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
4362
+ 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",
4347
4363
  icon: "\u{1F50D}",
4348
- version: "1.0.0",
4349
- 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",
4364
+ version: "1.1.0",
4365
+ 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",
4350
4366
  capabilities: [
4351
4367
  {
4352
4368
  id: "quality-review",
@@ -4367,13 +4383,24 @@ var CODE_REVIEWER_AGENT = {
4367
4383
  id: "performance-review",
4368
4384
  name: "\u6027\u80FD\u5BA1\u67E5",
4369
4385
  description: "\u68C0\u67E5\u6027\u80FD\u95EE\u9898\u548C\u4F18\u5316\u5EFA\u8BAE"
4386
+ },
4387
+ {
4388
+ id: "regression-test",
4389
+ name: "\u56DE\u5F52\u6D4B\u8BD5",
4390
+ description: "\u6267\u884C\u6D4B\u8BD5\u5957\u4EF6\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD"
4391
+ },
4392
+ {
4393
+ id: "coverage-analysis",
4394
+ name: "\u8986\u76D6\u7387\u5206\u6790",
4395
+ description: "\u5206\u6790\u6D4B\u8BD5\u8986\u76D6\u7387\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE"
4370
4396
  }
4371
4397
  ],
4372
4398
  tools: [
4373
4399
  { name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
4374
4400
  { name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
4375
4401
  { name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
4376
- { name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" }
4402
+ { name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" },
4403
+ { name: "run_shell_command", description: "\u6267\u884C\u6D4B\u8BD5\u547D\u4EE4", permission: "confirm" }
4377
4404
  ],
4378
4405
  triggers: [
4379
4406
  { type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
@@ -4384,6 +4411,7 @@ var CODE_REVIEWER_AGENT = {
4384
4411
  protectedPaths: ["node_modules", ".git"]
4385
4412
  },
4386
4413
  behavior: {
4414
+ requireConfirmation: ["run_shell_command"],
4387
4415
  autoCommit: false
4388
4416
  }
4389
4417
  },
@@ -4392,6 +4420,7 @@ var CODE_REVIEWER_AGENT = {
4392
4420
  ## \u4F60\u7684\u804C\u8D23
4393
4421
  - \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
4394
4422
  - \u68C0\u67E5\u5B89\u5168\u6F0F\u6D1E\u548C\u98CE\u9669
4423
+ - \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD
4395
4424
  - \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
4396
4425
  - \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
4397
4426
 
@@ -4400,8 +4429,20 @@ var CODE_REVIEWER_AGENT = {
4400
4429
  2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
4401
4430
  3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
4402
4431
  4. **\u89C4\u8303\u6027**: \u547D\u540D\u3001\u683C\u5F0F\u3001\u6CE8\u91CA
4432
+ 5. **\u6D4B\u8BD5\u8986\u76D6**: \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C\u3001\u8986\u76D6\u7387\u5206\u6790
4433
+
4434
+ ## \u56DE\u5F52\u6D4B\u8BD5\u6D41\u7A0B
4435
+ 1. \u6267\u884C \`npm test -- --run\` \u8FD0\u884C\u6D4B\u8BD5\u5957\u4EF6
4436
+ 2. \u5206\u6790\u6D4B\u8BD5\u7ED3\u679C\uFF0C\u8BC6\u522B\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B
4437
+ 3. \u5982\u679C\u6D4B\u8BD5\u5931\u8D25\uFF0C\u963B\u6B62\u5F52\u6863\u5E76\u63D0\u793A\u4FEE\u590D
4438
+ 4. \u63D0\u4F9B\u8986\u76D6\u7387\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE
4403
4439
 
4404
4440
  ## \u8F93\u51FA\u683C\u5F0F
4441
+ ### \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C
4442
+ - \u6D4B\u8BD5\u901A\u8FC7/\u5931\u8D25\u72B6\u6001
4443
+ - \u901A\u8FC7/\u5931\u8D25\u6570\u91CF
4444
+ - \u8986\u76D6\u7387\u767E\u5206\u6BD4
4445
+
4405
4446
  ### \u5BA1\u67E5\u7ED3\u679C
4406
4447
  - \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
4407
4448
 
@@ -4427,7 +4468,7 @@ var CODE_REVIEWER_AGENT = {
4427
4468
  ## \u4E0A\u4E0B\u6587
4428
4469
  {{context}}
4429
4470
 
4430
- \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`,
4471
+ \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`,
4431
4472
  outputFormat: {
4432
4473
  type: "markdown"
4433
4474
  }
@@ -4853,6 +4894,78 @@ ${content}`;
4853
4894
 
4854
4895
  // src/commands/opsx.ts
4855
4896
  var autoScheduleEnabled = true;
4897
+ var DEFAULT_REGRESSION_CONFIG = {
4898
+ enabled: true,
4899
+ command: "npm test -- --run",
4900
+ timeout: 12e4,
4901
+ // 2分钟
4902
+ coverageThreshold: 80
4903
+ };
4904
+ async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_CONFIG) {
4905
+ const startTime = Date.now();
4906
+ const result = {
4907
+ success: false,
4908
+ passed: 0,
4909
+ failed: 0,
4910
+ total: 0,
4911
+ duration: 0,
4912
+ output: "",
4913
+ errors: []
4914
+ };
4915
+ return new Promise((resolve5) => {
4916
+ const proc = child_process.spawn(config.command, [], {
4917
+ cwd: workingDirectory,
4918
+ shell: true,
4919
+ stdio: "pipe"
4920
+ });
4921
+ let stdout = "";
4922
+ let stderr = "";
4923
+ proc.stdout?.on("data", (data) => {
4924
+ stdout += data.toString();
4925
+ });
4926
+ proc.stderr?.on("data", (data) => {
4927
+ stderr += data.toString();
4928
+ });
4929
+ const timeout = setTimeout(() => {
4930
+ proc.kill();
4931
+ result.errors.push("\u6D4B\u8BD5\u8D85\u65F6");
4932
+ result.output = stdout + stderr;
4933
+ result.duration = Date.now() - startTime;
4934
+ resolve5(result);
4935
+ }, config.timeout);
4936
+ proc.on("close", (code) => {
4937
+ clearTimeout(timeout);
4938
+ result.output = stdout + stderr;
4939
+ result.duration = Date.now() - startTime;
4940
+ const passMatch = stdout.match(/(\d+)\s+(?:passed|tests?\s+passed)/i);
4941
+ const failMatch = stdout.match(/(\d+)\s+(?:failed|tests?\s+failed)/i);
4942
+ const totalMatch = stdout.match(/Tests?:\s*(\d+)/i);
4943
+ if (passMatch) {
4944
+ result.passed = parseInt(passMatch[1], 10);
4945
+ }
4946
+ if (failMatch) {
4947
+ result.failed = parseInt(failMatch[1], 10);
4948
+ }
4949
+ if (totalMatch) {
4950
+ result.total = parseInt(totalMatch[1], 10);
4951
+ } else {
4952
+ result.total = result.passed + result.failed;
4953
+ }
4954
+ const coverageMatch = stdout.match(/All files[^\d]*(\d+(?:\.\d+)?)/);
4955
+ if (coverageMatch) {
4956
+ result.coverage = parseFloat(coverageMatch[1]);
4957
+ }
4958
+ result.success = code === 0 && result.failed === 0;
4959
+ resolve5(result);
4960
+ });
4961
+ proc.on("error", (err) => {
4962
+ clearTimeout(timeout);
4963
+ result.errors.push(err.message);
4964
+ result.duration = Date.now() - startTime;
4965
+ resolve5(result);
4966
+ });
4967
+ });
4968
+ }
4856
4969
  async function handleOpsx(command, args, ctx) {
4857
4970
  const step = command.replace("opsx:", "");
4858
4971
  const workflow = new WorkflowEngine();
@@ -4867,7 +4980,7 @@ async function handleOpsx(command, args, ctx) {
4867
4980
  case "apply":
4868
4981
  return handleApply(workflow);
4869
4982
  case "archive":
4870
- return handleArchive(workflow, args);
4983
+ return handleArchive(workflow, args, ctx);
4871
4984
  case "propose":
4872
4985
  return handlePropose(workflow, args);
4873
4986
  case "status":
@@ -4882,12 +4995,50 @@ async function handleOpsx(command, args, ctx) {
4882
4995
  return handleNext(workflow);
4883
4996
  case "auto":
4884
4997
  return handleAutoSchedule(args);
4998
+ case "test":
4999
+ return handleRegressionTest(ctx);
4885
5000
  default:
4886
5001
  return {
4887
5002
  output: chalk9__default.default.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
4888
5003
  };
4889
5004
  }
4890
5005
  }
5006
+ async function handleRegressionTest(ctx) {
5007
+ const lines = [];
5008
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5..."));
5009
+ lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`));
5010
+ lines.push("");
5011
+ const result = await runRegressionTest(ctx.options.workingDirectory);
5012
+ lines.push(chalk9__default.default.gray("\u2500".repeat(50)));
5013
+ if (result.success) {
5014
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
5015
+ } else {
5016
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
5017
+ }
5018
+ lines.push("");
5019
+ lines.push(chalk9__default.default.cyan("\u6D4B\u8BD5\u7ED3\u679C:"));
5020
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${result.passed}`));
5021
+ lines.push(chalk9__default.default.gray(` \u5931\u8D25: ${result.failed}`));
5022
+ lines.push(chalk9__default.default.gray(` \u603B\u8BA1: ${result.total}`));
5023
+ lines.push(chalk9__default.default.gray(` \u8017\u65F6: ${(result.duration / 1e3).toFixed(2)}s`));
5024
+ if (result.coverage !== void 0) {
5025
+ const coverageColor = result.coverage >= 80 ? chalk9__default.default.green : result.coverage >= 60 ? chalk9__default.default.yellow : chalk9__default.default.red;
5026
+ lines.push(coverageColor(` \u8986\u76D6\u7387: ${result.coverage}%`));
5027
+ }
5028
+ if (result.errors.length > 0) {
5029
+ lines.push("");
5030
+ lines.push(chalk9__default.default.red("\u9519\u8BEF\u4FE1\u606F:"));
5031
+ for (const error of result.errors) {
5032
+ lines.push(chalk9__default.default.gray(` - ${error}`));
5033
+ }
5034
+ }
5035
+ if (result.failed > 0) {
5036
+ lines.push("");
5037
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u8BF7\u68C0\u67E5\u5E76\u4FEE\u590D"));
5038
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
5039
+ }
5040
+ return { output: lines.join("\n") };
5041
+ }
4891
5042
  function handleAutoSchedule(args) {
4892
5043
  const action = args[0]?.toLowerCase();
4893
5044
  if (!action) {
@@ -5017,13 +5168,70 @@ async function handleArchive(workflow, args, ctx) {
5017
5168
  ${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
5018
5169
  };
5019
5170
  }
5171
+ const lines = [];
5172
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u5F52\u6863\u524D\u56DE\u5F52\u6D4B\u8BD5..."));
5173
+ lines.push("");
5174
+ const testResult = await runRegressionTest(ctx.options.workingDirectory);
5175
+ if (!testResult.success) {
5176
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
5177
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u5931\u8D25: ${testResult.failed} | \u603B\u8BA1: ${testResult.total}`));
5178
+ lines.push("");
5179
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5F52\u6863\u88AB\u963B\u6B62"));
5180
+ lines.push(chalk9__default.default.gray("\n\u8BF7\u4FEE\u590D\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\u540E\u91CD\u8BD5"));
5181
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
5182
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:test \u53EF\u5355\u72EC\u8FD0\u884C\u56DE\u5F52\u6D4B\u8BD5"));
5183
+ return { output: lines.join("\n") };
5184
+ }
5185
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
5186
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u8017\u65F6: ${(testResult.duration / 1e3).toFixed(2)}s`));
5187
+ lines.push("");
5020
5188
  const changeId = state.id;
5021
5189
  const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
5022
5190
  await workflow.archive(summary);
5023
- return {
5024
- output: chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
5025
- \u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
5026
- };
5191
+ await updateChangelog(ctx.options.workingDirectory, summary, changeId);
5192
+ lines.push(chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
5193
+ \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"));
5194
+ return { output: lines.join("\n") };
5195
+ }
5196
+ async function updateChangelog(workingDirectory, summary, changeId) {
5197
+ try {
5198
+ const changelogPath = path6__namespace.join(workingDirectory, "CHANGELOG.md");
5199
+ const pkgPath = path6__namespace.join(workingDirectory, "package.json");
5200
+ let version = "1.0.0";
5201
+ try {
5202
+ const pkgContent = fs9__namespace.readFileSync(pkgPath, "utf-8");
5203
+ const pkg = JSON.parse(pkgContent);
5204
+ version = pkg.version || "1.0.0";
5205
+ } catch {
5206
+ }
5207
+ const today = /* @__PURE__ */ new Date();
5208
+ const dateStr = today.toISOString().split("T")[0];
5209
+ const entry = `
5210
+ ## v${version} (${dateStr})
5211
+
5212
+ **\u53D8\u66F4\u5185\u5BB9**
5213
+
5214
+ - ${summary} (${changeId})
5215
+ `;
5216
+ if (fs9__namespace.existsSync(changelogPath)) {
5217
+ const content = fs9__namespace.readFileSync(changelogPath, "utf-8");
5218
+ const versionPattern = /^## v\d+\.\d+\.\d+/m;
5219
+ const match = content.match(versionPattern);
5220
+ if (match && match.index !== void 0) {
5221
+ const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
5222
+ fs9__namespace.writeFileSync(changelogPath, newContent, "utf-8");
5223
+ } else {
5224
+ fs9__namespace.appendFileSync(changelogPath, entry, "utf-8");
5225
+ }
5226
+ } else {
5227
+ const header = `# Changelog
5228
+
5229
+ All notable changes to this project will be documented in this file.
5230
+ `;
5231
+ fs9__namespace.writeFileSync(changelogPath, header + entry, "utf-8");
5232
+ }
5233
+ } catch (error) {
5234
+ }
5027
5235
  }
5028
5236
  async function handlePropose(workflow, args, ctx) {
5029
5237
  const state = workflow.getState();
@@ -5214,9 +5422,25 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
5214
5422
  }
5215
5423
 
5216
5424
  // src/commands/runner.ts
5217
- var packageJsonPath2 = path6__namespace.resolve(__dirname, "../../package.json");
5218
- var packageJson2 = JSON.parse(fsSync__namespace.readFileSync(packageJsonPath2, "utf-8"));
5219
- var VERSION2 = packageJson2.version;
5425
+ function getVersion() {
5426
+ const possiblePaths = [
5427
+ path6__namespace.resolve(__dirname, "..", "..", "package.json"),
5428
+ path6__namespace.resolve(__dirname, "..", "..", "..", "package.json")
5429
+ ];
5430
+ for (const pkgPath of possiblePaths) {
5431
+ try {
5432
+ if (fs9__namespace.existsSync(pkgPath)) {
5433
+ const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
5434
+ const pkg = JSON.parse(content);
5435
+ return pkg.version;
5436
+ }
5437
+ } catch {
5438
+ continue;
5439
+ }
5440
+ }
5441
+ return "1.0.0";
5442
+ }
5443
+ var VERSION2 = getVersion();
5220
5444
  async function runSlashCommand(command, args, ctx) {
5221
5445
  const normalizedCommand = normalizeCommand(command);
5222
5446
  switch (normalizedCommand) {
@@ -6222,9 +6446,27 @@ async function startInteractiveMode(options) {
6222
6446
  }
6223
6447
 
6224
6448
  // src/cli/index.ts
6225
- var packageJsonPath3 = path6__namespace.resolve(__dirname, "../package.json");
6226
- var packageJson3 = JSON.parse(fsSync__namespace.readFileSync(packageJsonPath3, "utf-8"));
6227
- var VERSION3 = packageJson3.version;
6449
+ function getPackageJson() {
6450
+ const possiblePaths = [
6451
+ // 开发环境: dist/cli/index.js -> ../../package.json (项目根目录)
6452
+ path6__namespace.resolve(__dirname, "..", "..", "package.json"),
6453
+ // 生产环境: node_modules/@nick848/sf-cli/dist/cli/index.js -> ../../package.json
6454
+ path6__namespace.resolve(__dirname, "..", "..", "package.json")
6455
+ ];
6456
+ for (const pkgPath of possiblePaths) {
6457
+ try {
6458
+ if (fs9__namespace.existsSync(pkgPath)) {
6459
+ const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
6460
+ return JSON.parse(content);
6461
+ }
6462
+ } catch {
6463
+ continue;
6464
+ }
6465
+ }
6466
+ return { version: "1.0.0", name: "@nick848/sf-cli" };
6467
+ }
6468
+ var packageJson = getPackageJson();
6469
+ var VERSION3 = packageJson.version;
6228
6470
  var NAME = "sf-cli";
6229
6471
  commander.program.name(NAME).description("\u4E13\u4E3A\u524D\u7AEF\u5F00\u53D1\u8BBE\u8BA1\u7684AI\u9A71\u52A8CLI\u5DE5\u5177").version(VERSION3, "-v, --version", "\u663E\u793A\u7248\u672C\u53F7");
6230
6472
  commander.program.argument("[directory]", "\u5DE5\u4F5C\u76EE\u5F55", process.cwd()).option("-m, --model <model>", "\u6307\u5B9AAI\u6A21\u578B").option("-y, --yolo", "\u81EA\u52A8\u786E\u8BA4\u6240\u6709\u64CD\u4F5C").action(async (directory, options) => {