@nick848/sf-cli 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var 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');
@@ -34,7 +34,7 @@ function _interopNamespace(e) {
34
34
  }
35
35
 
36
36
  var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
37
- var fsSync__namespace = /*#__PURE__*/_interopNamespace(fsSync);
37
+ var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
38
38
  var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
39
39
  var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
40
40
  var os__namespace = /*#__PURE__*/_interopNamespace(os);
@@ -55,8 +55,8 @@ var KEY_FILE = ".key";
55
55
  function getOrCreateEncryptionKey() {
56
56
  const keyPath = path4__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
57
57
  try {
58
- if (fsSync__namespace.existsSync(keyPath)) {
59
- 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();
60
60
  return Buffer.from(keyBase64, "base64");
61
61
  }
62
62
  } catch {
@@ -64,10 +64,10 @@ function getOrCreateEncryptionKey() {
64
64
  const key = crypto__namespace.randomBytes(32);
65
65
  try {
66
66
  const keyDir = path4__namespace.dirname(keyPath);
67
- if (!fsSync__namespace.existsSync(keyDir)) {
68
- fsSync__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
67
+ if (!fs10__namespace.existsSync(keyDir)) {
68
+ fs10__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
69
69
  }
70
- fsSync__namespace.writeFileSync(keyPath, key.toString("base64"), {
70
+ fs10__namespace.writeFileSync(keyPath, key.toString("base64"), {
71
71
  mode: 384,
72
72
  // 仅所有者可读写
73
73
  encoding: "utf-8"
@@ -2544,10 +2544,10 @@ var FRONTEND_DEV_AGENT = {
2544
2544
  var CODE_REVIEWER_AGENT = {
2545
2545
  id: "code-reviewer",
2546
2546
  name: "\u4EE3\u7801\u5BA1\u6838",
2547
- 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",
2548
2548
  icon: "\u{1F50D}",
2549
- version: "1.0.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\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",
2551
2551
  capabilities: [
2552
2552
  {
2553
2553
  id: "quality-review",
@@ -2568,13 +2568,24 @@ var CODE_REVIEWER_AGENT = {
2568
2568
  id: "performance-review",
2569
2569
  name: "\u6027\u80FD\u5BA1\u67E5",
2570
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"
2571
2581
  }
2572
2582
  ],
2573
2583
  tools: [
2574
2584
  { name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
2575
2585
  { name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
2576
2586
  { name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
2577
- { 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" }
2578
2589
  ],
2579
2590
  triggers: [
2580
2591
  { type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
@@ -2585,6 +2596,7 @@ var CODE_REVIEWER_AGENT = {
2585
2596
  protectedPaths: ["node_modules", ".git"]
2586
2597
  },
2587
2598
  behavior: {
2599
+ requireConfirmation: ["run_shell_command"],
2588
2600
  autoCommit: false
2589
2601
  }
2590
2602
  },
@@ -2593,6 +2605,7 @@ var CODE_REVIEWER_AGENT = {
2593
2605
  ## \u4F60\u7684\u804C\u8D23
2594
2606
  - \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
2595
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
2596
2609
  - \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
2597
2610
  - \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
2598
2611
 
@@ -2601,8 +2614,20 @@ var CODE_REVIEWER_AGENT = {
2601
2614
  2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
2602
2615
  3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
2603
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
2604
2624
 
2605
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
+
2606
2631
  ### \u5BA1\u67E5\u7ED3\u679C
2607
2632
  - \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
2608
2633
 
@@ -2628,7 +2653,7 @@ var CODE_REVIEWER_AGENT = {
2628
2653
  ## \u4E0A\u4E0B\u6587
2629
2654
  {{context}}
2630
2655
 
2631
- \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`,
2632
2657
  outputFormat: {
2633
2658
  type: "markdown"
2634
2659
  }
@@ -3604,12 +3629,20 @@ function getScheduleRuleDescription(step) {
3604
3629
  var DEFAULT_CONFIRMATION_POINTS = [
3605
3630
  {
3606
3631
  type: "spec-review",
3607
- name: "\u89C4\u8303\u62C6\u89E3\u786E\u8BA4",
3608
- description: "\u89C4\u8303\u62C6\u89E3\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u8FDB\u5165\u8BBE\u8BA1\u9636\u6BB5",
3632
+ name: "\u89C4\u683C\u786E\u8BA4",
3633
+ description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
3609
3634
  triggerStep: "explore",
3610
3635
  targetStep: "new",
3611
3636
  required: true
3612
3637
  },
3638
+ {
3639
+ type: "spec-review",
3640
+ name: "\u89C4\u683C\u786E\u8BA4",
3641
+ description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
3642
+ triggerStep: "propose",
3643
+ targetStep: "apply",
3644
+ required: true
3645
+ },
3613
3646
  {
3614
3647
  type: "architecture",
3615
3648
  name: "\u67B6\u6784\u8C03\u6574\u786E\u8BA4",
@@ -3645,19 +3678,15 @@ var ConfirmationManager = class {
3645
3678
  confirmationPoints;
3646
3679
  confirmations = /* @__PURE__ */ new Map();
3647
3680
  constructor(customPoints) {
3648
- const points = customPoints || DEFAULT_CONFIRMATION_POINTS;
3649
- this.confirmationPoints = new Map(points.map((p) => [p.type, p]));
3681
+ this.confirmationPoints = customPoints || DEFAULT_CONFIRMATION_POINTS;
3650
3682
  }
3651
3683
  /**
3652
3684
  * 获取指定阶段的确认点
3653
3685
  */
3654
3686
  getConfirmationPointForTransition(from, to) {
3655
- for (const point of this.confirmationPoints.values()) {
3656
- if (point.triggerStep === from && point.targetStep === to) {
3657
- return point;
3658
- }
3659
- }
3660
- return void 0;
3687
+ return this.confirmationPoints.find(
3688
+ (point) => point.triggerStep === from && point.targetStep === to
3689
+ );
3661
3690
  }
3662
3691
  /**
3663
3692
  * 检查是否需要确认
@@ -3670,7 +3699,7 @@ var ConfirmationManager = class {
3670
3699
  * 获取确认点详情
3671
3700
  */
3672
3701
  getConfirmationPoint(type) {
3673
- return this.confirmationPoints.get(type);
3702
+ return this.confirmationPoints.find((point) => point.type === type);
3674
3703
  }
3675
3704
  /**
3676
3705
  * 记录确认
@@ -3714,14 +3743,14 @@ var ConfirmationManager = class {
3714
3743
  * 获取所有确认点
3715
3744
  */
3716
3745
  getAllConfirmationPoints() {
3717
- return Array.from(this.confirmationPoints.values());
3746
+ return [...this.confirmationPoints];
3718
3747
  }
3719
3748
  /**
3720
3749
  * 获取指定阶段需要清除的确认点
3721
3750
  */
3722
3751
  getConfirmationsToClear(targetStep) {
3723
3752
  const types = [];
3724
- for (const point of this.confirmationPoints.values()) {
3753
+ for (const point of this.confirmationPoints) {
3725
3754
  const stepOrder = ["explore", "new", "continue", "propose", "apply", "archive"];
3726
3755
  const targetIndex = stepOrder.indexOf(targetStep);
3727
3756
  const triggerIndex = stepOrder.indexOf(point.triggerStep);
@@ -3844,6 +3873,26 @@ var WorkflowEngine = class {
3844
3873
  devStandards: this.devStandards
3845
3874
  };
3846
3875
  }
3876
+ /**
3877
+ * 获取规格文件路径
3878
+ */
3879
+ getSpecFilePath() {
3880
+ if (!this.state) return null;
3881
+ return path4__namespace.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
3882
+ }
3883
+ /**
3884
+ * 检查规格文件是否存在
3885
+ */
3886
+ async hasSpecFile() {
3887
+ const specPath = this.getSpecFilePath();
3888
+ if (!specPath) return false;
3889
+ try {
3890
+ await fs4__namespace.access(specPath);
3891
+ return true;
3892
+ } catch {
3893
+ return false;
3894
+ }
3895
+ }
3847
3896
  /**
3848
3897
  * 启动新工作流
3849
3898
  */
@@ -5618,8 +5667,8 @@ var CommandParser = class {
5618
5667
  };
5619
5668
  }
5620
5669
  parseAtPath(input) {
5621
- const path13 = input.slice(1).trim();
5622
- if (!path13) {
5670
+ const path14 = input.slice(1).trim();
5671
+ if (!path14) {
5623
5672
  return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
5624
5673
  }
5625
5674
  return {
@@ -5627,7 +5676,7 @@ var CommandParser = class {
5627
5676
  command: {
5628
5677
  type: "at" /* AT */,
5629
5678
  raw: input,
5630
- path: path13
5679
+ path: path14
5631
5680
  }
5632
5681
  };
5633
5682
  }
@@ -6471,10 +6520,26 @@ function createSpinner(message) {
6471
6520
  stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
6472
6521
  };
6473
6522
  }
6474
- var packageJsonPath = path4__namespace.resolve(__dirname, "../../package.json");
6475
- var packageJson = JSON.parse(fsSync__namespace.readFileSync(packageJsonPath, "utf-8"));
6476
- var CURRENT_VERSION = packageJson.version;
6477
- var PACKAGE_NAME = packageJson.name;
6523
+ function getPackageInfo() {
6524
+ const possiblePaths = [
6525
+ path4__namespace.resolve(__dirname, "..", "..", "package.json"),
6526
+ path4__namespace.resolve(__dirname, "..", "..", "..", "package.json")
6527
+ ];
6528
+ for (const pkgPath of possiblePaths) {
6529
+ try {
6530
+ if (fs10__namespace.existsSync(pkgPath)) {
6531
+ const content = fs10__namespace.readFileSync(pkgPath, "utf-8");
6532
+ return JSON.parse(content);
6533
+ }
6534
+ } catch {
6535
+ continue;
6536
+ }
6537
+ }
6538
+ return { version: "1.0.0", name: "@nick848/sf-cli" };
6539
+ }
6540
+ var packageInfo = getPackageInfo();
6541
+ var CURRENT_VERSION = packageInfo.version;
6542
+ var PACKAGE_NAME = packageInfo.name;
6478
6543
  async function handleUpdate(args, ctx) {
6479
6544
  const options = {
6480
6545
  check: args.includes("--check") || args.includes("-c"),
@@ -6691,16 +6756,23 @@ async function handleNew(args, ctx) {
6691
6756
  if (workflowEngine) {
6692
6757
  const existingState = workflowEngine.getState();
6693
6758
  if (existingState && existingState.status === "running") {
6759
+ if (existingState.currentStep === "explore" || existingState.currentStep === "propose") {
6760
+ const specPath = path4__namespace.join(workingDir, "openspec", "changes", `${existingState.id}-spec.md`);
6761
+ if (fs10__namespace.existsSync(specPath)) {
6762
+ return {
6763
+ output: chalk9__default.default.yellow("\u5F53\u524D\u5DE5\u4F5C\u6D41\u6B63\u5728\u7B49\u5F85\u89C4\u683C\u786E\u8BA4") + chalk9__default.default.gray(`
6764
+
6765
+ \u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9__default.default.gray(`
6766
+ \u53D8\u66F4ID: ${existingState.id}`) + chalk9__default.default.cyan("\n\n\u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210:") + chalk9__default.default.white(`
6767
+ ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4\u89C4\u683C\u540E\u7EE7\u7EED:") + chalk9__default.default.gray("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.gray("\n /opsx:status - \u67E5\u770B\u8BE6\u60C5")
6768
+ };
6769
+ }
6770
+ }
6694
6771
  return {
6695
6772
  output: chalk9__default.default.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray(`
6696
6773
 
6697
6774
  \u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9__default.default.gray(`
6698
- \u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9__default.default.gray(`
6699
-
6700
- \u9009\u9879:`) + chalk9__default.default.gray(`
6701
- 1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:${existingState.currentStep}`) + chalk9__default.default.gray(`
6702
- 2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel`) + chalk9__default.default.gray(`
6703
- 3. \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001: /opsx:status`)
6775
+ \u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9__default.default.gray("\n\n\u9009\u9879:") + chalk9__default.default.gray("\n 1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:status") + chalk9__default.default.gray("\n 2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel")
6704
6776
  };
6705
6777
  }
6706
6778
  }
@@ -6715,6 +6787,7 @@ async function handleNew(args, ctx) {
6715
6787
  async function newFeature(options, workingDir, workflowEngine) {
6716
6788
  const cwd = workingDir || process.cwd();
6717
6789
  const { requirement, forceComplexity } = options;
6790
+ const lines = [];
6718
6791
  try {
6719
6792
  const stats = await fs4__namespace.stat(cwd);
6720
6793
  if (!stats.isDirectory()) {
@@ -6722,51 +6795,283 @@ async function newFeature(options, workingDir, workflowEngine) {
6722
6795
  output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
6723
6796
  };
6724
6797
  }
6725
- } catch (e) {
6798
+ } catch {
6726
6799
  return {
6727
6800
  output: chalk9__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
6728
6801
  };
6729
6802
  }
6730
- try {
6731
- const context = await readProjectContext(cwd);
6732
- const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
6733
- const workflow = workflowEngine || new WorkflowEngine();
6734
- if (!workflowEngine) {
6735
- await workflow.initialize(cwd);
6736
- }
6737
- const state = await workflow.start(requirement, analysis.score, {
6738
- title: extractTitle(requirement)
6803
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u5206\u6790\u9879\u76EE..."));
6804
+ const context = await readProjectContext(cwd);
6805
+ lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${context.name}`));
6806
+ lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${context.type}`));
6807
+ lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`));
6808
+ lines.push("");
6809
+ lines.push(chalk9__default.default.cyan("\u{1F4CA} \u5206\u6790\u9700\u6C42\u590D\u6742\u5EA6..."));
6810
+ const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
6811
+ lines.push(chalk9__default.default.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`));
6812
+ lines.push(chalk9__default.default.gray(` \u6D41\u7A0B\u7C7B\u578B: ${analysis.recommendation === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`));
6813
+ for (const factor of analysis.factors) {
6814
+ lines.push(chalk9__default.default.gray(` - ${factor}`));
6815
+ }
6816
+ lines.push("");
6817
+ lines.push(chalk9__default.default.cyan("\u{1F4CB} \u521D\u59CB\u5316\u5DE5\u4F5C\u6D41..."));
6818
+ const workflow = workflowEngine || new WorkflowEngine();
6819
+ if (!workflowEngine) {
6820
+ await workflow.initialize(cwd);
6821
+ }
6822
+ const state = await workflow.start(requirement, analysis.score, {
6823
+ title: extractTitle(requirement)
6824
+ });
6825
+ lines.push(chalk9__default.default.gray(` \u53D8\u66F4ID: ${state.id}`));
6826
+ lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u6D41: ${state.type}`));
6827
+ lines.push("");
6828
+ lines.push(chalk9__default.default.cyan("\u{1F4DD} \u751F\u6210\u89C4\u683C\u62C6\u5206..."));
6829
+ const spec = await generateSpec(requirement, context, analysis, state.id);
6830
+ const specPath = await saveSpecFile(cwd, spec);
6831
+ lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
6832
+ lines.push(chalk9__default.default.gray(` \u8DEF\u5F84: ${specPath}`));
6833
+ lines.push("");
6834
+ lines.push(chalk9__default.default.cyan.bold("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
6835
+ lines.push(chalk9__default.default.white(`
6836
+ ${spec.summary}`));
6837
+ if (spec.items.length > 0) {
6838
+ lines.push("");
6839
+ lines.push(chalk9__default.default.cyan(" \u4EFB\u52A1\u62C6\u5206:"));
6840
+ for (const item of spec.items) {
6841
+ const priorityIcon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
6842
+ lines.push(chalk9__default.default.gray(` ${priorityIcon} [${item.id}] ${item.title}`));
6843
+ }
6844
+ }
6845
+ if (spec.risks.length > 0) {
6846
+ lines.push("");
6847
+ lines.push(chalk9__default.default.yellow(" \u26A0\uFE0F \u98CE\u9669\u63D0\u793A:"));
6848
+ for (const risk of spec.risks) {
6849
+ lines.push(chalk9__default.default.gray(` - ${risk}`));
6850
+ }
6851
+ }
6852
+ lines.push("");
6853
+ lines.push(chalk9__default.default.yellow.bold("\u23F3 \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
6854
+ lines.push(chalk9__default.default.gray("\n\u8BF7\u68C0\u67E5\u751F\u6210\u7684\u89C4\u683C\u6587\u4EF6\uFF0C\u786E\u8BA4\u540E\u7EE7\u7EED:"));
6855
+ lines.push(chalk9__default.default.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
6856
+ lines.push(chalk9__default.default.white(" /opsx:rollback explore - \u89C4\u683C\u4E0D\u7B26\uFF0C\u91CD\u65B0\u62C6\u5206"));
6857
+ lines.push(chalk9__default.default.white(" /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001"));
6858
+ return { output: lines.join("\n") };
6859
+ }
6860
+ async function generateSpec(requirement, context, analysis, changeId) {
6861
+ const spec = {
6862
+ changeId,
6863
+ requirement,
6864
+ summary: "",
6865
+ items: [],
6866
+ architectureNotes: [],
6867
+ risks: [],
6868
+ suggestions: []
6869
+ };
6870
+ spec.summary = generateSummary(requirement);
6871
+ if (analysis.recommendation === "complex") {
6872
+ spec.items = generateComplexTasks(requirement, context, analysis);
6873
+ spec.architectureNotes = generateArchitectureNotes(requirement, context);
6874
+ } else {
6875
+ spec.items = generateSimpleTasks(requirement);
6876
+ }
6877
+ spec.risks = generateRisks(requirement, context, analysis);
6878
+ spec.suggestions = generateSuggestions(requirement, context, analysis);
6879
+ return spec;
6880
+ }
6881
+ function generateSummary(requirement) {
6882
+ const firstSentence = requirement.split(/[。!?\n]/)[0];
6883
+ return firstSentence.length > 100 ? firstSentence.slice(0, 97) + "..." : firstSentence;
6884
+ }
6885
+ function generateComplexTasks(requirement, context, analysis) {
6886
+ const items = [];
6887
+ let itemId = 1;
6888
+ const featurePatterns = [
6889
+ { pattern: /用户|登录|注册|认证|权限/, title: "\u7528\u6237\u8BA4\u8BC1\u6A21\u5757", priority: "high" },
6890
+ { pattern: /数据|存储|缓存|数据库/, title: "\u6570\u636E\u5C42\u5B9E\u73B0", priority: "high" },
6891
+ { pattern: /接口|API|请求|响应/, title: "API \u63A5\u53E3\u5F00\u53D1", priority: "high" },
6892
+ { pattern: /界面|页面|组件|UI/, title: "\u754C\u9762\u5F00\u53D1", priority: "medium" },
6893
+ { pattern: /测试|单测|覆盖/, title: "\u6D4B\u8BD5\u7528\u4F8B\u7F16\u5199", priority: "medium" },
6894
+ { pattern: /文档|说明/, title: "\u6587\u6863\u7F16\u5199", priority: "low" },
6895
+ { pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", priority: "low" },
6896
+ { pattern: /优化|性能/, title: "\u6027\u80FD\u4F18\u5316", priority: "medium" },
6897
+ { pattern: /安全|加密/, title: "\u5B89\u5168\u5B9E\u73B0", priority: "high" },
6898
+ { pattern: /日志|监控/, title: "\u65E5\u5FD7\u76D1\u63A7", priority: "low" }
6899
+ ];
6900
+ for (const { pattern, title, priority } of featurePatterns) {
6901
+ if (pattern.test(requirement)) {
6902
+ items.push({
6903
+ id: `T${itemId.toString().padStart(3, "0")}`,
6904
+ title,
6905
+ description: `${title}\u76F8\u5173\u7684\u529F\u80FD\u5B9E\u73B0`,
6906
+ priority,
6907
+ dependencies: itemId > 1 ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
6908
+ estimatedComplexity: priority === "high" ? 3 : priority === "medium" ? 2 : 1
6909
+ });
6910
+ itemId++;
6911
+ }
6912
+ }
6913
+ if (items.length === 0) {
6914
+ items.push({
6915
+ id: "T001",
6916
+ title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
6917
+ description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
6918
+ priority: "high",
6919
+ dependencies: [],
6920
+ estimatedComplexity: 2
6739
6921
  });
6740
- const lines = [
6741
- chalk9__default.default.green("\u2713 \u9700\u6C42\u5206\u6790\u5B8C\u6210\n"),
6742
- chalk9__default.default.cyan("\u9879\u76EE\u4FE1\u606F:"),
6743
- chalk9__default.default.gray(` \u540D\u79F0: ${context.name}`),
6744
- chalk9__default.default.gray(` \u7C7B\u578B: ${context.type}`),
6745
- chalk9__default.default.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`),
6746
- "",
6747
- chalk9__default.default.cyan("\u9700\u6C42\u5206\u6790:"),
6748
- chalk9__default.default.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`),
6749
- chalk9__default.default.gray(` \u5DE5\u4F5C\u6D41: ${state.type === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`),
6750
- chalk9__default.default.gray(` \u53D8\u66F4ID: ${state.id}`),
6751
- "",
6752
- chalk9__default.default.cyan("\u590D\u6742\u5EA6\u56E0\u7D20:"),
6753
- ...analysis.factors.map((f) => chalk9__default.default.gray(` - ${f}`)),
6754
- "",
6755
- chalk9__default.default.yellow("\u4E0B\u4E00\u6B65:"),
6756
- state.type === "complex" ? chalk9__default.default.gray(" \u6267\u884C /opsx:explore \u5F00\u59CB\u63A2\u7D22\u9636\u6BB5") : chalk9__default.default.gray(" \u6267\u884C /opsx:propose \u63D0\u4EA4\u53D8\u66F4\u63D0\u6848")
6757
- ];
6758
- return { output: lines.join("\n") };
6759
- } catch (error) {
6760
- const err = error;
6761
- if (err.code === "EACCES") {
6762
- return {
6763
- output: chalk9__default.default.red(`\u9519\u8BEF: \u65E0\u6743\u9650\u8BBF\u95EE\u76EE\u5F55 ${cwd}`)
6764
- };
6922
+ items.push({
6923
+ id: "T002",
6924
+ title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
6925
+ description: requirement,
6926
+ priority: "high",
6927
+ dependencies: ["T001"],
6928
+ estimatedComplexity: analysis.score
6929
+ });
6930
+ items.push({
6931
+ id: "T003",
6932
+ title: "\u6D4B\u8BD5\u4E0E\u9A8C\u8BC1",
6933
+ description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027",
6934
+ priority: "medium",
6935
+ dependencies: ["T002"],
6936
+ estimatedComplexity: 2
6937
+ });
6938
+ }
6939
+ return items;
6940
+ }
6941
+ function generateSimpleTasks(requirement, context) {
6942
+ return [
6943
+ {
6944
+ id: "T001",
6945
+ title: "\u5B9E\u73B0\u53D8\u66F4",
6946
+ description: requirement,
6947
+ priority: "high",
6948
+ dependencies: [],
6949
+ estimatedComplexity: 3
6950
+ },
6951
+ {
6952
+ id: "T002",
6953
+ title: "\u6D4B\u8BD5\u9A8C\u8BC1",
6954
+ description: "\u9A8C\u8BC1\u53D8\u66F4\u6B63\u786E\u6027",
6955
+ priority: "medium",
6956
+ dependencies: ["T001"],
6957
+ estimatedComplexity: 1
6765
6958
  }
6766
- return {
6767
- output: chalk9__default.default.red(`\u542F\u52A8\u9700\u6C42\u5931\u8D25: ${error.message}`)
6768
- };
6959
+ ];
6960
+ }
6961
+ function generateArchitectureNotes(requirement, context) {
6962
+ const notes = [];
6963
+ if (context.framework) {
6964
+ notes.push(`\u9879\u76EE\u4F7F\u7528 ${context.framework} \u6846\u67B6\uFF0C\u9700\u9075\u5FAA\u5176\u6700\u4F73\u5B9E\u8DF5`);
6965
+ }
6966
+ if (requirement.includes("\u6A21\u5757") || requirement.includes("\u7EC4\u4EF6")) {
6967
+ notes.push("\u5EFA\u8BAE\u91C7\u7528\u6A21\u5757\u5316\u8BBE\u8BA1\uFF0C\u4FDD\u6301\u7EC4\u4EF6\u804C\u8D23\u5355\u4E00");
6968
+ }
6969
+ if (requirement.includes("API") || requirement.includes("\u63A5\u53E3")) {
6970
+ notes.push("API \u8BBE\u8BA1\u9700\u8003\u8651\u7248\u672C\u63A7\u5236\u548C\u5411\u540E\u517C\u5BB9");
6971
+ }
6972
+ if (context.structure.srcStructure) {
6973
+ notes.push(`\u73B0\u6709\u6E90\u7801\u7ED3\u6784: ${context.structure.srcStructure}`);
6974
+ }
6975
+ return notes;
6976
+ }
6977
+ function generateRisks(requirement, context, analysis) {
6978
+ const risks = [];
6979
+ if (!context.framework) {
6980
+ risks.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B\uFF0C\u53EF\u80FD\u5F71\u54CD\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u6027");
6981
+ }
6982
+ if (analysis.score >= 7) {
6983
+ risks.push("\u9700\u6C42\u590D\u6742\u5EA6\u8F83\u9AD8\uFF0C\u5EFA\u8BAE\u5206\u9636\u6BB5\u5B9E\u73B0");
6769
6984
  }
6985
+ if (requirement.includes("\u8FC1\u79FB") || requirement.includes("\u91CD\u6784")) {
6986
+ risks.push("\u6D89\u53CA\u73B0\u6709\u4EE3\u7801\u4FEE\u6539\uFF0C\u9700\u6CE8\u610F\u56DE\u5F52\u6D4B\u8BD5");
6987
+ }
6988
+ if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168")) {
6989
+ risks.push("\u6D89\u53CA\u5B89\u5168\u654F\u611F\u529F\u80FD\uFF0C\u9700\u8981\u989D\u5916\u5BA1\u67E5");
6990
+ }
6991
+ return risks;
6992
+ }
6993
+ function generateSuggestions(requirement, context, analysis) {
6994
+ const suggestions = [];
6995
+ if (context.norms.devStandards) {
6996
+ suggestions.push("\u9879\u76EE\u5DF2\u6709\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u9075\u5FAA\u73B0\u6709\u89C4\u8303");
6997
+ }
6998
+ if (analysis.recommendation === "complex") {
6999
+ suggestions.push("\u590D\u6742\u9700\u6C42\u5EFA\u8BAE\u5148\u8FDB\u884C\u6280\u672F\u8BC4\u5BA1");
7000
+ suggestions.push("\u5EFA\u8BAE\u8C03\u7528 $architect \u83B7\u53D6\u67B6\u6784\u5EFA\u8BAE");
7001
+ }
7002
+ if (context.techStack.length > 0) {
7003
+ suggestions.push(`\u6280\u672F\u6808: ${context.techStack.join(", ")}`);
7004
+ }
7005
+ return suggestions;
7006
+ }
7007
+ async function saveSpecFile(cwd, spec) {
7008
+ const changesDir = path4__namespace.join(cwd, "openspec", "changes");
7009
+ await fs4__namespace.mkdir(changesDir, { recursive: true });
7010
+ const specPath = path4__namespace.join(changesDir, `${spec.changeId}-spec.md`);
7011
+ const content = formatSpecFile(spec);
7012
+ await fs4__namespace.writeFile(specPath, content, "utf-8");
7013
+ return specPath;
7014
+ }
7015
+ function formatSpecFile(spec) {
7016
+ const lines = [];
7017
+ lines.push(`# Spec: ${spec.summary}`);
7018
+ lines.push("");
7019
+ lines.push(`> \u53D8\u66F4ID: ${spec.changeId}`);
7020
+ lines.push(`> \u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}`);
7021
+ lines.push("");
7022
+ lines.push("---");
7023
+ lines.push("");
7024
+ lines.push("## \u9700\u6C42\u6982\u8FF0");
7025
+ lines.push("");
7026
+ lines.push(spec.requirement);
7027
+ lines.push("");
7028
+ lines.push("## \u4EFB\u52A1\u62C6\u5206");
7029
+ lines.push("");
7030
+ for (const item of spec.items) {
7031
+ const priorityLabel = item.priority === "high" ? "\u{1F534} \u9AD8" : item.priority === "medium" ? "\u{1F7E1} \u4E2D" : "\u{1F7E2} \u4F4E";
7032
+ lines.push(`### ${item.id}: ${item.title}`);
7033
+ lines.push("");
7034
+ lines.push(`- **\u4F18\u5148\u7EA7**: ${priorityLabel}`);
7035
+ lines.push(`- **\u63CF\u8FF0**: ${item.description}`);
7036
+ lines.push(`- **\u9884\u4F30\u590D\u6742\u5EA6**: ${item.estimatedComplexity}/5`);
7037
+ if (item.dependencies.length > 0) {
7038
+ lines.push(`- **\u4F9D\u8D56**: ${item.dependencies.join(", ")}`);
7039
+ }
7040
+ lines.push("");
7041
+ }
7042
+ if (spec.architectureNotes.length > 0) {
7043
+ lines.push("## \u67B6\u6784\u8BF4\u660E");
7044
+ lines.push("");
7045
+ for (const note of spec.architectureNotes) {
7046
+ lines.push(`- ${note}`);
7047
+ }
7048
+ lines.push("");
7049
+ }
7050
+ if (spec.risks.length > 0) {
7051
+ lines.push("## \u26A0\uFE0F \u98CE\u9669\u8BC4\u4F30");
7052
+ lines.push("");
7053
+ for (const risk of spec.risks) {
7054
+ lines.push(`- ${risk}`);
7055
+ }
7056
+ lines.push("");
7057
+ }
7058
+ if (spec.suggestions.length > 0) {
7059
+ lines.push("## \u{1F4A1} \u5EFA\u8BAE");
7060
+ lines.push("");
7061
+ for (const suggestion of spec.suggestions) {
7062
+ lines.push(`- ${suggestion}`);
7063
+ }
7064
+ lines.push("");
7065
+ }
7066
+ lines.push("---");
7067
+ lines.push("");
7068
+ lines.push("## \u786E\u8BA4\u72B6\u6001");
7069
+ lines.push("");
7070
+ lines.push("- [ ] \u89C4\u683C\u5DF2\u5BA1\u9605");
7071
+ lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
7072
+ lines.push("");
7073
+ lines.push("**\u786E\u8BA4\u540E\u6267\u884C**: `/opsx:confirm spec-review`");
7074
+ return lines.join("\n");
6770
7075
  }
6771
7076
  function parseArgs(args) {
6772
7077
  let forceComplexity;
@@ -6791,42 +7096,52 @@ async function readProjectContext(cwd) {
6791
7096
  type: "unknown",
6792
7097
  framework: null,
6793
7098
  techStack: [],
6794
- description: ""
7099
+ description: "",
7100
+ structure: {
7101
+ directories: [],
7102
+ keyFiles: [],
7103
+ srcStructure: ""
7104
+ },
7105
+ norms: {
7106
+ devStandards: "",
7107
+ patterns: "",
7108
+ weights: ""
7109
+ }
7110
+ };
7111
+ const [agentsContext, configContext, normsContext, structureContext] = await Promise.all([
7112
+ readAgentsMd(cwd),
7113
+ readConfigYaml(cwd),
7114
+ readNorms(cwd),
7115
+ analyzeStructure(cwd)
7116
+ ]);
7117
+ return {
7118
+ ...defaultContext,
7119
+ ...agentsContext,
7120
+ ...configContext,
7121
+ norms: normsContext,
7122
+ structure: structureContext
6795
7123
  };
7124
+ }
7125
+ async function readAgentsMd(cwd) {
6796
7126
  const agentsPath = path4__namespace.join(cwd, "AGENTS.md");
6797
7127
  try {
6798
7128
  const stats = await fs4__namespace.stat(agentsPath);
6799
7129
  if (stats.size > MAX_FILE_SIZE2) {
6800
7130
  console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
6801
- return defaultContext;
7131
+ return {};
6802
7132
  }
6803
7133
  const content = await fs4__namespace.readFile(agentsPath, "utf-8");
6804
- return parseAgentsMd(content, defaultContext);
7134
+ return parseAgentsMd(content);
6805
7135
  } catch (e) {
6806
7136
  const err = e;
6807
7137
  if (err.code !== "ENOENT") {
6808
7138
  console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
6809
7139
  }
7140
+ return {};
6810
7141
  }
6811
- const configPath = path4__namespace.join(cwd, "openspec", "config.yaml");
6812
- try {
6813
- const stats = await fs4__namespace.stat(configPath);
6814
- if (stats.size > MAX_FILE_SIZE2) {
6815
- console.warn(`\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
6816
- return defaultContext;
6817
- }
6818
- const content = await fs4__namespace.readFile(configPath, "utf-8");
6819
- return parseConfigYaml(content, defaultContext);
6820
- } catch (e) {
6821
- const err = e;
6822
- if (err.code !== "ENOENT") {
6823
- console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
6824
- }
6825
- }
6826
- return defaultContext;
6827
7142
  }
6828
- function parseAgentsMd(content, defaults) {
6829
- const context = { ...defaults };
7143
+ function parseAgentsMd(content) {
7144
+ const context = {};
6830
7145
  const nameMatch = content.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
6831
7146
  if (nameMatch) {
6832
7147
  context.name = nameMatch[1];
@@ -6843,10 +7158,32 @@ function parseAgentsMd(content, defaults) {
6843
7158
  if (descMatch) {
6844
7159
  context.description = descMatch[1].trim();
6845
7160
  }
7161
+ const techStackMatch = content.match(/技术栈[::]\s*([^\n]+)/);
7162
+ if (techStackMatch) {
7163
+ context.techStack = techStackMatch[1].split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
7164
+ }
6846
7165
  return context;
6847
7166
  }
6848
- function parseConfigYaml(content, defaults) {
6849
- const context = { ...defaults };
7167
+ async function readConfigYaml(cwd) {
7168
+ const configPath = path4__namespace.join(cwd, "openspec", "config.yaml");
7169
+ try {
7170
+ const stats = await fs4__namespace.stat(configPath);
7171
+ if (stats.size > MAX_FILE_SIZE2) {
7172
+ console.warn("\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6");
7173
+ return {};
7174
+ }
7175
+ const content = await fs4__namespace.readFile(configPath, "utf-8");
7176
+ return parseConfigYaml(content);
7177
+ } catch (e) {
7178
+ const err = e;
7179
+ if (err.code !== "ENOENT") {
7180
+ console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
7181
+ }
7182
+ return {};
7183
+ }
7184
+ }
7185
+ function parseConfigYaml(content) {
7186
+ const context = {};
6850
7187
  const nameMatch = content.match(/name:\s*(.+)/);
6851
7188
  if (nameMatch) {
6852
7189
  context.name = nameMatch[1].trim();
@@ -6861,6 +7198,67 @@ function parseConfigYaml(content, defaults) {
6861
7198
  }
6862
7199
  return context;
6863
7200
  }
7201
+ async function readNorms(cwd) {
7202
+ const normsDir = path4__namespace.join(cwd, ".sf-cli", "norms");
7203
+ const norms = {
7204
+ devStandards: "",
7205
+ patterns: "",
7206
+ weights: ""
7207
+ };
7208
+ try {
7209
+ const devStandardsPath = path4__namespace.join(normsDir, "devstanded.md");
7210
+ norms.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8").catch(() => "");
7211
+ } catch {
7212
+ }
7213
+ try {
7214
+ const patternsPath = path4__namespace.join(normsDir, "patterns.json");
7215
+ norms.patterns = await fs4__namespace.readFile(patternsPath, "utf-8").catch(() => "");
7216
+ } catch {
7217
+ }
7218
+ try {
7219
+ const weightsPath = path4__namespace.join(normsDir, "weights.json");
7220
+ norms.weights = await fs4__namespace.readFile(weightsPath, "utf-8").catch(() => "");
7221
+ } catch {
7222
+ }
7223
+ return norms;
7224
+ }
7225
+ async function analyzeStructure(cwd) {
7226
+ const structure = {
7227
+ directories: [],
7228
+ keyFiles: [],
7229
+ srcStructure: ""
7230
+ };
7231
+ try {
7232
+ const entries = await fs4__namespace.readdir(cwd, { withFileTypes: true });
7233
+ for (const entry of entries) {
7234
+ if (entry.isDirectory() && !["node_modules", "dist", ".git", "build"].includes(entry.name)) {
7235
+ structure.directories.push(entry.name);
7236
+ }
7237
+ }
7238
+ const keyFiles = [
7239
+ "package.json",
7240
+ "tsconfig.json",
7241
+ "AGENTS.md",
7242
+ "README.md"
7243
+ ];
7244
+ for (const file of keyFiles) {
7245
+ const filePath = path4__namespace.join(cwd, file);
7246
+ try {
7247
+ await fs4__namespace.access(filePath);
7248
+ structure.keyFiles.push(file);
7249
+ } catch {
7250
+ }
7251
+ }
7252
+ const srcDir = path4__namespace.join(cwd, "src");
7253
+ try {
7254
+ const srcEntries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
7255
+ structure.srcStructure = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name).join("/");
7256
+ } catch {
7257
+ }
7258
+ } catch (e) {
7259
+ }
7260
+ return { structure };
7261
+ }
6864
7262
  function analyzeComplexity(requirement, context) {
6865
7263
  let score = 3;
6866
7264
  const factors = [];
@@ -6928,6 +7326,78 @@ function extractTitle(requirement) {
6928
7326
  return requirement.slice(0, 47) + "...";
6929
7327
  }
6930
7328
  var autoScheduleEnabled = true;
7329
+ var DEFAULT_REGRESSION_CONFIG = {
7330
+ enabled: true,
7331
+ command: "npm test -- --run",
7332
+ timeout: 12e4,
7333
+ // 2分钟
7334
+ coverageThreshold: 80
7335
+ };
7336
+ async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_CONFIG) {
7337
+ const startTime = Date.now();
7338
+ const result = {
7339
+ success: false,
7340
+ passed: 0,
7341
+ failed: 0,
7342
+ total: 0,
7343
+ duration: 0,
7344
+ output: "",
7345
+ errors: []
7346
+ };
7347
+ return new Promise((resolve4) => {
7348
+ const proc = child_process.spawn(config.command, [], {
7349
+ cwd: workingDirectory,
7350
+ shell: true,
7351
+ stdio: "pipe"
7352
+ });
7353
+ let stdout = "";
7354
+ let stderr = "";
7355
+ proc.stdout?.on("data", (data) => {
7356
+ stdout += data.toString();
7357
+ });
7358
+ proc.stderr?.on("data", (data) => {
7359
+ stderr += data.toString();
7360
+ });
7361
+ const timeout = setTimeout(() => {
7362
+ proc.kill();
7363
+ result.errors.push("\u6D4B\u8BD5\u8D85\u65F6");
7364
+ result.output = stdout + stderr;
7365
+ result.duration = Date.now() - startTime;
7366
+ resolve4(result);
7367
+ }, config.timeout);
7368
+ proc.on("close", (code) => {
7369
+ clearTimeout(timeout);
7370
+ result.output = stdout + stderr;
7371
+ result.duration = Date.now() - startTime;
7372
+ const passMatch = stdout.match(/(\d+)\s+(?:passed|tests?\s+passed)/i);
7373
+ const failMatch = stdout.match(/(\d+)\s+(?:failed|tests?\s+failed)/i);
7374
+ const totalMatch = stdout.match(/Tests?:\s*(\d+)/i);
7375
+ if (passMatch) {
7376
+ result.passed = parseInt(passMatch[1], 10);
7377
+ }
7378
+ if (failMatch) {
7379
+ result.failed = parseInt(failMatch[1], 10);
7380
+ }
7381
+ if (totalMatch) {
7382
+ result.total = parseInt(totalMatch[1], 10);
7383
+ } else {
7384
+ result.total = result.passed + result.failed;
7385
+ }
7386
+ const coverageMatch = stdout.match(/All files[^\d]*(\d+(?:\.\d+)?)/);
7387
+ if (coverageMatch) {
7388
+ result.coverage = parseFloat(coverageMatch[1]);
7389
+ }
7390
+ result.success = code === 0 && result.failed === 0;
7391
+ resolve4(result);
7392
+ });
7393
+ proc.on("error", (err) => {
7394
+ clearTimeout(timeout);
7395
+ result.errors.push(err.message);
7396
+ result.duration = Date.now() - startTime;
7397
+ resolve4(result);
7398
+ });
7399
+ });
7400
+ }
6931
7401
  async function handleOpsx(command, args, ctx) {
6932
7402
  const step = command.replace("opsx:", "");
6933
7403
  const workflow = new WorkflowEngine();
@@ -6942,7 +7412,7 @@ async function handleOpsx(command, args, ctx) {
6942
7412
  case "apply":
6943
7413
  return handleApply(workflow);
6944
7414
  case "archive":
6945
- return handleArchive(workflow, args);
7415
+ return handleArchive(workflow, args, ctx);
6946
7416
  case "propose":
6947
7417
  return handlePropose(workflow, args);
6948
7418
  case "status":
@@ -6952,17 +7422,55 @@ async function handleOpsx(command, args, ctx) {
6952
7422
  case "rollback":
6953
7423
  return handleRollback(workflow, args);
6954
7424
  case "confirm":
6955
- return handleConfirm(workflow, args);
7425
+ return handleConfirm(workflow, args, ctx);
6956
7426
  case "next":
6957
7427
  return handleNext(workflow);
6958
7428
  case "auto":
6959
7429
  return handleAutoSchedule(args);
7430
+ case "test":
7431
+ return handleRegressionTest(ctx);
6960
7432
  default:
6961
7433
  return {
6962
7434
  output: chalk9__default.default.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
6963
7435
  };
6964
7436
  }
6965
7437
  }
7438
+ async function handleRegressionTest(ctx) {
7439
+ const lines = [];
7440
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5..."));
7441
+ lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`));
7442
+ lines.push("");
7443
+ const result = await runRegressionTest(ctx.options.workingDirectory);
7444
+ lines.push(chalk9__default.default.gray("\u2500".repeat(50)));
7445
+ if (result.success) {
7446
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
7447
+ } else {
7448
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
7449
+ }
7450
+ lines.push("");
7451
+ lines.push(chalk9__default.default.cyan("\u6D4B\u8BD5\u7ED3\u679C:"));
7452
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${result.passed}`));
7453
+ lines.push(chalk9__default.default.gray(` \u5931\u8D25: ${result.failed}`));
7454
+ lines.push(chalk9__default.default.gray(` \u603B\u8BA1: ${result.total}`));
7455
+ lines.push(chalk9__default.default.gray(` \u8017\u65F6: ${(result.duration / 1e3).toFixed(2)}s`));
7456
+ if (result.coverage !== void 0) {
7457
+ const coverageColor = result.coverage >= 80 ? chalk9__default.default.green : result.coverage >= 60 ? chalk9__default.default.yellow : chalk9__default.default.red;
7458
+ lines.push(coverageColor(` \u8986\u76D6\u7387: ${result.coverage}%`));
7459
+ }
7460
+ if (result.errors.length > 0) {
7461
+ lines.push("");
7462
+ lines.push(chalk9__default.default.red("\u9519\u8BEF\u4FE1\u606F:"));
7463
+ for (const error of result.errors) {
7464
+ lines.push(chalk9__default.default.gray(` - ${error}`));
7465
+ }
7466
+ }
7467
+ if (result.failed > 0) {
7468
+ lines.push("");
7469
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u8BF7\u68C0\u67E5\u5E76\u4FEE\u590D"));
7470
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
7471
+ }
7472
+ return { output: lines.join("\n") };
7473
+ }
6966
7474
  function handleAutoSchedule(args) {
6967
7475
  const action = args[0]?.toLowerCase();
6968
7476
  if (!action) {
@@ -7092,13 +7600,70 @@ async function handleArchive(workflow, args, ctx) {
7092
7600
  ${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
7093
7601
  };
7094
7602
  }
7603
+ const lines = [];
7604
+ lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u5F52\u6863\u524D\u56DE\u5F52\u6D4B\u8BD5..."));
7605
+ lines.push("");
7606
+ const testResult = await runRegressionTest(ctx.options.workingDirectory);
7607
+ if (!testResult.success) {
7608
+ lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
7609
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u5931\u8D25: ${testResult.failed} | \u603B\u8BA1: ${testResult.total}`));
7610
+ lines.push("");
7611
+ lines.push(chalk9__default.default.yellow("\u26A0 \u5F52\u6863\u88AB\u963B\u6B62"));
7612
+ lines.push(chalk9__default.default.gray("\n\u8BF7\u4FEE\u590D\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\u540E\u91CD\u8BD5"));
7613
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
7614
+ lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:test \u53EF\u5355\u72EC\u8FD0\u884C\u56DE\u5F52\u6D4B\u8BD5"));
7615
+ return { output: lines.join("\n") };
7616
+ }
7617
+ lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
7618
+ lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u8017\u65F6: ${(testResult.duration / 1e3).toFixed(2)}s`));
7619
+ lines.push("");
7095
7620
  const changeId = state.id;
7096
7621
  const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
7097
7622
  await workflow.archive(summary);
7098
- return {
7099
- output: chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
7100
- \u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
7101
- };
7623
+ await updateChangelog(ctx.options.workingDirectory, summary, changeId);
7624
+ lines.push(chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
7625
+ \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"));
7626
+ return { output: lines.join("\n") };
7627
+ }
7628
+ async function updateChangelog(workingDirectory, summary, changeId) {
7629
+ try {
7630
+ const changelogPath = path4__namespace.join(workingDirectory, "CHANGELOG.md");
7631
+ const pkgPath = path4__namespace.join(workingDirectory, "package.json");
7632
+ let version = "1.0.0";
7633
+ try {
7634
+ const pkgContent = fs10__namespace.readFileSync(pkgPath, "utf-8");
7635
+ const pkg = JSON.parse(pkgContent);
7636
+ version = pkg.version || "1.0.0";
7637
+ } catch {
7638
+ }
7639
+ const today = /* @__PURE__ */ new Date();
7640
+ const dateStr = today.toISOString().split("T")[0];
7641
+ const entry = `
7642
+ ## v${version} (${dateStr})
7643
+
7644
+ **\u53D8\u66F4\u5185\u5BB9**
7645
+
7646
+ - ${summary} (${changeId})
7647
+ `;
7648
+ if (fs10__namespace.existsSync(changelogPath)) {
7649
+ const content = fs10__namespace.readFileSync(changelogPath, "utf-8");
7650
+ const versionPattern = /^## v\d+\.\d+\.\d+/m;
7651
+ const match = content.match(versionPattern);
7652
+ if (match && match.index !== void 0) {
7653
+ const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
7654
+ fs10__namespace.writeFileSync(changelogPath, newContent, "utf-8");
7655
+ } else {
7656
+ fs10__namespace.appendFileSync(changelogPath, entry, "utf-8");
7657
+ }
7658
+ } else {
7659
+ const header = `# Changelog
7660
+
7661
+ All notable changes to this project will be documented in this file.
7662
+ `;
7663
+ fs10__namespace.writeFileSync(changelogPath, header + entry, "utf-8");
7664
+ }
7665
+ } catch (error) {
7666
+ }
7102
7667
  }
7103
7668
  async function handlePropose(workflow, args, ctx) {
7104
7669
  const state = workflow.getState();
@@ -7225,6 +7790,16 @@ async function handleConfirm(workflow, args, ctx) {
7225
7790
  const type = args[0];
7226
7791
  if (!type) {
7227
7792
  const pendingPoint = workflow.getCurrentConfirmationPoint();
7793
+ const specPath = await checkPendingSpec(ctx.options.workingDirectory, state.id);
7794
+ if (specPath) {
7795
+ return {
7796
+ output: chalk9__default.default.cyan("\u{1F4CB} \u89C4\u683C\u6587\u4EF6\u5F85\u786E\u8BA4") + chalk9__default.default.gray(`
7797
+
7798
+ \u89C4\u683C\u6587\u4EF6: ${specPath}`) + chalk9__default.default.gray(`
7799
+
7800
+ \u8BF7\u68C0\u67E5\u89C4\u683C\u6587\u4EF6\u540E\u786E\u8BA4:`) + chalk9__default.default.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.white("\n /opsx:rollback explore - \u91CD\u65B0\u62C6\u5206")
7801
+ };
7802
+ }
7228
7803
  if (pendingPoint) {
7229
7804
  return {
7230
7805
  output: chalk9__default.default.cyan("\u5F85\u786E\u8BA4\u7684\u68C0\u67E5\u70B9:") + chalk9__default.default.white(`
@@ -7246,12 +7821,54 @@ ${generateConfirmationPrompt(pendingPoint)}`) + chalk9__default.default.gray(`
7246
7821
  }
7247
7822
  const comment = args.slice(1).join(" ");
7248
7823
  workflow.confirm(type, comment);
7249
- return {
7250
- output: chalk9__default.default.green("\u2713 \u5DF2\u786E\u8BA4") + chalk9__default.default.white(`
7824
+ const lines = [];
7825
+ lines.push(chalk9__default.default.green("\u2713 \u5DF2\u786E\u8BA4"));
7826
+ lines.push(chalk9__default.default.white(`
7827
+ ${point.name}`));
7828
+ if (comment) {
7829
+ lines.push(chalk9__default.default.gray(`
7830
+ \u5907\u6CE8: ${comment}`));
7831
+ }
7832
+ if (type === "spec-review") {
7833
+ const allowed = workflow.getAllowedTransitions();
7834
+ if (allowed.length > 0) {
7835
+ const nextStep = allowed[0];
7836
+ try {
7837
+ const transition = await workflow.transition(nextStep);
7838
+ lines.push("");
7839
+ lines.push(chalk9__default.default.cyan(`\u2713 \u5DF2\u81EA\u52A8\u8FDB\u5165 ${nextStep} \u9636\u6BB5`));
7840
+ lines.push(chalk9__default.default.gray(`\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`));
7841
+ if (nextStep === "new") {
7842
+ lines.push(chalk9__default.default.yellow("\n\u4E0B\u4E00\u6B65: \u8BBE\u8BA1\u65B9\u6848"));
7843
+ lines.push(chalk9__default.default.gray(" \u53EF\u8C03\u7528 $architect \u6216 $frontend-dev \u8FDB\u884C\u8BBE\u8BA1"));
7844
+ } else if (nextStep === "apply") {
7845
+ lines.push(chalk9__default.default.yellow("\n\u4E0B\u4E00\u6B65: \u6267\u884C\u53D8\u66F4"));
7846
+ lines.push(chalk9__default.default.gray(" \u53EF\u8C03\u7528 $frontend-dev \u6267\u884C\u4EE3\u7801\u4FEE\u6539"));
7847
+ }
7848
+ return { output: lines.join("\n") };
7849
+ } catch (e) {
7850
+ if (e instanceof ConfirmationRequiredError) {
7851
+ lines.push(chalk9__default.default.yellow("\n\u26A0 \u8FD8\u9700\u8981\u786E\u8BA4:") + chalk9__default.default.white(`
7852
+ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
7251
7853
 
7252
- ${point.name}`) + (comment ? chalk9__default.default.gray(`
7253
- \u5907\u6CE8: ${comment}`) : "") + chalk9__default.default.yellow("\n\n\u4F7F\u7528 /opsx:next \u7EE7\u7EED\u4E0B\u4E00\u9636\u6BB5")
7254
- };
7854
+ \u4F7F\u7528 /opsx:confirm ${e.point.type}`));
7855
+ return { output: lines.join("\n") };
7856
+ }
7857
+ throw e;
7858
+ }
7859
+ }
7860
+ }
7861
+ lines.push(chalk9__default.default.yellow("\n\u4F7F\u7528 /opsx:next \u7EE7\u7EED\u4E0B\u4E00\u9636\u6BB5"));
7862
+ return { output: lines.join("\n") };
7863
+ }
7864
+ async function checkPendingSpec(workingDirectory, changeId) {
7865
+ const specPath = path4__namespace.join(workingDirectory, "openspec", "changes", `${changeId}-spec.md`);
7866
+ try {
7867
+ await fs10__namespace.promises.access(specPath);
7868
+ return specPath;
7869
+ } catch {
7870
+ return null;
7871
+ }
7255
7872
  }
7256
7873
  async function handleNext(workflow, args, ctx) {
7257
7874
  const state = workflow.getState();
@@ -7289,9 +7906,25 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
7289
7906
  }
7290
7907
 
7291
7908
  // src/commands/runner.ts
7292
- var packageJsonPath2 = path4__namespace.resolve(__dirname, "../../package.json");
7293
- var packageJson2 = JSON.parse(fsSync__namespace.readFileSync(packageJsonPath2, "utf-8"));
7294
- var VERSION2 = packageJson2.version;
7909
+ function getVersion() {
7910
+ const possiblePaths = [
7911
+ path4__namespace.resolve(__dirname, "..", "..", "package.json"),
7912
+ path4__namespace.resolve(__dirname, "..", "..", "..", "package.json")
7913
+ ];
7914
+ for (const pkgPath of possiblePaths) {
7915
+ try {
7916
+ if (fs10__namespace.existsSync(pkgPath)) {
7917
+ const content = fs10__namespace.readFileSync(pkgPath, "utf-8");
7918
+ const pkg = JSON.parse(content);
7919
+ return pkg.version;
7920
+ }
7921
+ } catch {
7922
+ continue;
7923
+ }
7924
+ }
7925
+ return "1.0.0";
7926
+ }
7927
+ var VERSION2 = getVersion();
7295
7928
  async function runSlashCommand(command, args, ctx) {
7296
7929
  const normalizedCommand = normalizeCommand(command);
7297
7930
  switch (normalizedCommand) {