@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/CHANGELOG.md +59 -0
- package/README.md +4 -23
- package/dist/cli/index.js +767 -116
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +746 -113
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +745 -112
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import * as path5 from 'path';
|
|
|
2
2
|
import path5__default from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import * as fs4 from 'fs/promises';
|
|
5
|
-
import * as
|
|
5
|
+
import * as fs10 from 'fs';
|
|
6
6
|
import * as crypto from 'crypto';
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
import { encoding_for_model } from 'tiktoken';
|
|
@@ -31,8 +31,8 @@ var KEY_FILE = ".key";
|
|
|
31
31
|
function getOrCreateEncryptionKey() {
|
|
32
32
|
const keyPath = path5.join(os.homedir(), KEY_DIR, KEY_FILE);
|
|
33
33
|
try {
|
|
34
|
-
if (
|
|
35
|
-
const keyBase64 =
|
|
34
|
+
if (fs10.existsSync(keyPath)) {
|
|
35
|
+
const keyBase64 = fs10.readFileSync(keyPath, "utf-8").trim();
|
|
36
36
|
return Buffer.from(keyBase64, "base64");
|
|
37
37
|
}
|
|
38
38
|
} catch {
|
|
@@ -40,10 +40,10 @@ function getOrCreateEncryptionKey() {
|
|
|
40
40
|
const key = crypto.randomBytes(32);
|
|
41
41
|
try {
|
|
42
42
|
const keyDir = path5.dirname(keyPath);
|
|
43
|
-
if (!
|
|
44
|
-
|
|
43
|
+
if (!fs10.existsSync(keyDir)) {
|
|
44
|
+
fs10.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
fs10.writeFileSync(keyPath, key.toString("base64"), {
|
|
47
47
|
mode: 384,
|
|
48
48
|
// 仅所有者可读写
|
|
49
49
|
encoding: "utf-8"
|
|
@@ -2520,10 +2520,10 @@ var FRONTEND_DEV_AGENT = {
|
|
|
2520
2520
|
var CODE_REVIEWER_AGENT = {
|
|
2521
2521
|
id: "code-reviewer",
|
|
2522
2522
|
name: "\u4EE3\u7801\u5BA1\u6838",
|
|
2523
|
-
description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
|
|
2523
|
+
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",
|
|
2524
2524
|
icon: "\u{1F50D}",
|
|
2525
|
-
version: "1.
|
|
2526
|
-
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",
|
|
2525
|
+
version: "1.1.0",
|
|
2526
|
+
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",
|
|
2527
2527
|
capabilities: [
|
|
2528
2528
|
{
|
|
2529
2529
|
id: "quality-review",
|
|
@@ -2544,13 +2544,24 @@ var CODE_REVIEWER_AGENT = {
|
|
|
2544
2544
|
id: "performance-review",
|
|
2545
2545
|
name: "\u6027\u80FD\u5BA1\u67E5",
|
|
2546
2546
|
description: "\u68C0\u67E5\u6027\u80FD\u95EE\u9898\u548C\u4F18\u5316\u5EFA\u8BAE"
|
|
2547
|
+
},
|
|
2548
|
+
{
|
|
2549
|
+
id: "regression-test",
|
|
2550
|
+
name: "\u56DE\u5F52\u6D4B\u8BD5",
|
|
2551
|
+
description: "\u6267\u884C\u6D4B\u8BD5\u5957\u4EF6\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD"
|
|
2552
|
+
},
|
|
2553
|
+
{
|
|
2554
|
+
id: "coverage-analysis",
|
|
2555
|
+
name: "\u8986\u76D6\u7387\u5206\u6790",
|
|
2556
|
+
description: "\u5206\u6790\u6D4B\u8BD5\u8986\u76D6\u7387\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE"
|
|
2547
2557
|
}
|
|
2548
2558
|
],
|
|
2549
2559
|
tools: [
|
|
2550
2560
|
{ name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
2551
2561
|
{ name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
|
|
2552
2562
|
{ name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
2553
|
-
{ name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" }
|
|
2563
|
+
{ name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" },
|
|
2564
|
+
{ name: "run_shell_command", description: "\u6267\u884C\u6D4B\u8BD5\u547D\u4EE4", permission: "confirm" }
|
|
2554
2565
|
],
|
|
2555
2566
|
triggers: [
|
|
2556
2567
|
{ type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
|
|
@@ -2561,6 +2572,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
2561
2572
|
protectedPaths: ["node_modules", ".git"]
|
|
2562
2573
|
},
|
|
2563
2574
|
behavior: {
|
|
2575
|
+
requireConfirmation: ["run_shell_command"],
|
|
2564
2576
|
autoCommit: false
|
|
2565
2577
|
}
|
|
2566
2578
|
},
|
|
@@ -2569,6 +2581,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
2569
2581
|
## \u4F60\u7684\u804C\u8D23
|
|
2570
2582
|
- \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
|
|
2571
2583
|
- \u68C0\u67E5\u5B89\u5168\u6F0F\u6D1E\u548C\u98CE\u9669
|
|
2584
|
+
- \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD
|
|
2572
2585
|
- \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
|
|
2573
2586
|
- \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
|
|
2574
2587
|
|
|
@@ -2577,8 +2590,20 @@ var CODE_REVIEWER_AGENT = {
|
|
|
2577
2590
|
2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
|
|
2578
2591
|
3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
|
|
2579
2592
|
4. **\u89C4\u8303\u6027**: \u547D\u540D\u3001\u683C\u5F0F\u3001\u6CE8\u91CA
|
|
2593
|
+
5. **\u6D4B\u8BD5\u8986\u76D6**: \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C\u3001\u8986\u76D6\u7387\u5206\u6790
|
|
2594
|
+
|
|
2595
|
+
## \u56DE\u5F52\u6D4B\u8BD5\u6D41\u7A0B
|
|
2596
|
+
1. \u6267\u884C \`npm test -- --run\` \u8FD0\u884C\u6D4B\u8BD5\u5957\u4EF6
|
|
2597
|
+
2. \u5206\u6790\u6D4B\u8BD5\u7ED3\u679C\uFF0C\u8BC6\u522B\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
2598
|
+
3. \u5982\u679C\u6D4B\u8BD5\u5931\u8D25\uFF0C\u963B\u6B62\u5F52\u6863\u5E76\u63D0\u793A\u4FEE\u590D
|
|
2599
|
+
4. \u63D0\u4F9B\u8986\u76D6\u7387\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE
|
|
2580
2600
|
|
|
2581
2601
|
## \u8F93\u51FA\u683C\u5F0F
|
|
2602
|
+
### \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C
|
|
2603
|
+
- \u6D4B\u8BD5\u901A\u8FC7/\u5931\u8D25\u72B6\u6001
|
|
2604
|
+
- \u901A\u8FC7/\u5931\u8D25\u6570\u91CF
|
|
2605
|
+
- \u8986\u76D6\u7387\u767E\u5206\u6BD4
|
|
2606
|
+
|
|
2582
2607
|
### \u5BA1\u67E5\u7ED3\u679C
|
|
2583
2608
|
- \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
|
|
2584
2609
|
|
|
@@ -2604,7 +2629,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
2604
2629
|
## \u4E0A\u4E0B\u6587
|
|
2605
2630
|
{{context}}
|
|
2606
2631
|
|
|
2607
|
-
\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`,
|
|
2632
|
+
\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`,
|
|
2608
2633
|
outputFormat: {
|
|
2609
2634
|
type: "markdown"
|
|
2610
2635
|
}
|
|
@@ -3580,12 +3605,20 @@ function getScheduleRuleDescription(step) {
|
|
|
3580
3605
|
var DEFAULT_CONFIRMATION_POINTS = [
|
|
3581
3606
|
{
|
|
3582
3607
|
type: "spec-review",
|
|
3583
|
-
name: "\u89C4\
|
|
3584
|
-
description: "\u89C4\
|
|
3608
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
3609
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
3585
3610
|
triggerStep: "explore",
|
|
3586
3611
|
targetStep: "new",
|
|
3587
3612
|
required: true
|
|
3588
3613
|
},
|
|
3614
|
+
{
|
|
3615
|
+
type: "spec-review",
|
|
3616
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
3617
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
3618
|
+
triggerStep: "propose",
|
|
3619
|
+
targetStep: "apply",
|
|
3620
|
+
required: true
|
|
3621
|
+
},
|
|
3589
3622
|
{
|
|
3590
3623
|
type: "architecture",
|
|
3591
3624
|
name: "\u67B6\u6784\u8C03\u6574\u786E\u8BA4",
|
|
@@ -3621,19 +3654,15 @@ var ConfirmationManager = class {
|
|
|
3621
3654
|
confirmationPoints;
|
|
3622
3655
|
confirmations = /* @__PURE__ */ new Map();
|
|
3623
3656
|
constructor(customPoints) {
|
|
3624
|
-
|
|
3625
|
-
this.confirmationPoints = new Map(points.map((p) => [p.type, p]));
|
|
3657
|
+
this.confirmationPoints = customPoints || DEFAULT_CONFIRMATION_POINTS;
|
|
3626
3658
|
}
|
|
3627
3659
|
/**
|
|
3628
3660
|
* 获取指定阶段的确认点
|
|
3629
3661
|
*/
|
|
3630
3662
|
getConfirmationPointForTransition(from, to) {
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
}
|
|
3635
|
-
}
|
|
3636
|
-
return void 0;
|
|
3663
|
+
return this.confirmationPoints.find(
|
|
3664
|
+
(point) => point.triggerStep === from && point.targetStep === to
|
|
3665
|
+
);
|
|
3637
3666
|
}
|
|
3638
3667
|
/**
|
|
3639
3668
|
* 检查是否需要确认
|
|
@@ -3646,7 +3675,7 @@ var ConfirmationManager = class {
|
|
|
3646
3675
|
* 获取确认点详情
|
|
3647
3676
|
*/
|
|
3648
3677
|
getConfirmationPoint(type) {
|
|
3649
|
-
return this.confirmationPoints.
|
|
3678
|
+
return this.confirmationPoints.find((point) => point.type === type);
|
|
3650
3679
|
}
|
|
3651
3680
|
/**
|
|
3652
3681
|
* 记录确认
|
|
@@ -3690,14 +3719,14 @@ var ConfirmationManager = class {
|
|
|
3690
3719
|
* 获取所有确认点
|
|
3691
3720
|
*/
|
|
3692
3721
|
getAllConfirmationPoints() {
|
|
3693
|
-
return
|
|
3722
|
+
return [...this.confirmationPoints];
|
|
3694
3723
|
}
|
|
3695
3724
|
/**
|
|
3696
3725
|
* 获取指定阶段需要清除的确认点
|
|
3697
3726
|
*/
|
|
3698
3727
|
getConfirmationsToClear(targetStep) {
|
|
3699
3728
|
const types = [];
|
|
3700
|
-
for (const point of this.confirmationPoints
|
|
3729
|
+
for (const point of this.confirmationPoints) {
|
|
3701
3730
|
const stepOrder = ["explore", "new", "continue", "propose", "apply", "archive"];
|
|
3702
3731
|
const targetIndex = stepOrder.indexOf(targetStep);
|
|
3703
3732
|
const triggerIndex = stepOrder.indexOf(point.triggerStep);
|
|
@@ -3820,6 +3849,26 @@ var WorkflowEngine = class {
|
|
|
3820
3849
|
devStandards: this.devStandards
|
|
3821
3850
|
};
|
|
3822
3851
|
}
|
|
3852
|
+
/**
|
|
3853
|
+
* 获取规格文件路径
|
|
3854
|
+
*/
|
|
3855
|
+
getSpecFilePath() {
|
|
3856
|
+
if (!this.state) return null;
|
|
3857
|
+
return path5.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
|
|
3858
|
+
}
|
|
3859
|
+
/**
|
|
3860
|
+
* 检查规格文件是否存在
|
|
3861
|
+
*/
|
|
3862
|
+
async hasSpecFile() {
|
|
3863
|
+
const specPath = this.getSpecFilePath();
|
|
3864
|
+
if (!specPath) return false;
|
|
3865
|
+
try {
|
|
3866
|
+
await fs4.access(specPath);
|
|
3867
|
+
return true;
|
|
3868
|
+
} catch {
|
|
3869
|
+
return false;
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3823
3872
|
/**
|
|
3824
3873
|
* 启动新工作流
|
|
3825
3874
|
*/
|
|
@@ -5594,8 +5643,8 @@ var CommandParser = class {
|
|
|
5594
5643
|
};
|
|
5595
5644
|
}
|
|
5596
5645
|
parseAtPath(input) {
|
|
5597
|
-
const
|
|
5598
|
-
if (!
|
|
5646
|
+
const path15 = input.slice(1).trim();
|
|
5647
|
+
if (!path15) {
|
|
5599
5648
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
5600
5649
|
}
|
|
5601
5650
|
return {
|
|
@@ -5603,7 +5652,7 @@ var CommandParser = class {
|
|
|
5603
5652
|
command: {
|
|
5604
5653
|
type: "at" /* AT */,
|
|
5605
5654
|
raw: input,
|
|
5606
|
-
path:
|
|
5655
|
+
path: path15
|
|
5607
5656
|
}
|
|
5608
5657
|
};
|
|
5609
5658
|
}
|
|
@@ -6447,10 +6496,26 @@ function createSpinner(message) {
|
|
|
6447
6496
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
6448
6497
|
};
|
|
6449
6498
|
}
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6499
|
+
function getPackageInfo() {
|
|
6500
|
+
const possiblePaths = [
|
|
6501
|
+
path5.resolve(__dirname$1, "..", "..", "package.json"),
|
|
6502
|
+
path5.resolve(__dirname$1, "..", "..", "..", "package.json")
|
|
6503
|
+
];
|
|
6504
|
+
for (const pkgPath of possiblePaths) {
|
|
6505
|
+
try {
|
|
6506
|
+
if (fs10.existsSync(pkgPath)) {
|
|
6507
|
+
const content = fs10.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;
|
|
6454
6519
|
async function handleUpdate(args, ctx) {
|
|
6455
6520
|
const options = {
|
|
6456
6521
|
check: args.includes("--check") || args.includes("-c"),
|
|
@@ -6667,16 +6732,23 @@ async function handleNew(args, ctx) {
|
|
|
6667
6732
|
if (workflowEngine) {
|
|
6668
6733
|
const existingState = workflowEngine.getState();
|
|
6669
6734
|
if (existingState && existingState.status === "running") {
|
|
6735
|
+
if (existingState.currentStep === "explore" || existingState.currentStep === "propose") {
|
|
6736
|
+
const specPath = path5.join(workingDir, "openspec", "changes", `${existingState.id}-spec.md`);
|
|
6737
|
+
if (fs10.existsSync(specPath)) {
|
|
6738
|
+
return {
|
|
6739
|
+
output: chalk9.yellow("\u5F53\u524D\u5DE5\u4F5C\u6D41\u6B63\u5728\u7B49\u5F85\u89C4\u683C\u786E\u8BA4") + chalk9.gray(`
|
|
6740
|
+
|
|
6741
|
+
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9.gray(`
|
|
6742
|
+
\u53D8\u66F4ID: ${existingState.id}`) + chalk9.cyan("\n\n\u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210:") + chalk9.white(`
|
|
6743
|
+
${specPath}`) + chalk9.yellow("\n\n\u8BF7\u786E\u8BA4\u89C4\u683C\u540E\u7EE7\u7EED:") + chalk9.gray("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9.gray("\n /opsx:status - \u67E5\u770B\u8BE6\u60C5")
|
|
6744
|
+
};
|
|
6745
|
+
}
|
|
6746
|
+
}
|
|
6670
6747
|
return {
|
|
6671
6748
|
output: chalk9.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray(`
|
|
6672
6749
|
|
|
6673
6750
|
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9.gray(`
|
|
6674
|
-
\u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9.gray(
|
|
6675
|
-
|
|
6676
|
-
\u9009\u9879:`) + chalk9.gray(`
|
|
6677
|
-
1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:${existingState.currentStep}`) + chalk9.gray(`
|
|
6678
|
-
2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel`) + chalk9.gray(`
|
|
6679
|
-
3. \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001: /opsx:status`)
|
|
6751
|
+
\u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9.gray("\n\n\u9009\u9879:") + chalk9.gray("\n 1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:status") + chalk9.gray("\n 2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel")
|
|
6680
6752
|
};
|
|
6681
6753
|
}
|
|
6682
6754
|
}
|
|
@@ -6691,6 +6763,7 @@ async function handleNew(args, ctx) {
|
|
|
6691
6763
|
async function newFeature(options, workingDir, workflowEngine) {
|
|
6692
6764
|
const cwd = workingDir || process.cwd();
|
|
6693
6765
|
const { requirement, forceComplexity } = options;
|
|
6766
|
+
const lines = [];
|
|
6694
6767
|
try {
|
|
6695
6768
|
const stats = await fs4.stat(cwd);
|
|
6696
6769
|
if (!stats.isDirectory()) {
|
|
@@ -6698,51 +6771,283 @@ async function newFeature(options, workingDir, workflowEngine) {
|
|
|
6698
6771
|
output: chalk9.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
6699
6772
|
};
|
|
6700
6773
|
}
|
|
6701
|
-
} catch
|
|
6774
|
+
} catch {
|
|
6702
6775
|
return {
|
|
6703
6776
|
output: chalk9.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
6704
6777
|
};
|
|
6705
6778
|
}
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6779
|
+
lines.push(chalk9.cyan("\u{1F50D} \u5206\u6790\u9879\u76EE..."));
|
|
6780
|
+
const context = await readProjectContext(cwd);
|
|
6781
|
+
lines.push(chalk9.gray(` \u9879\u76EE: ${context.name}`));
|
|
6782
|
+
lines.push(chalk9.gray(` \u7C7B\u578B: ${context.type}`));
|
|
6783
|
+
lines.push(chalk9.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`));
|
|
6784
|
+
lines.push("");
|
|
6785
|
+
lines.push(chalk9.cyan("\u{1F4CA} \u5206\u6790\u9700\u6C42\u590D\u6742\u5EA6..."));
|
|
6786
|
+
const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
|
|
6787
|
+
lines.push(chalk9.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`));
|
|
6788
|
+
lines.push(chalk9.gray(` \u6D41\u7A0B\u7C7B\u578B: ${analysis.recommendation === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`));
|
|
6789
|
+
for (const factor of analysis.factors) {
|
|
6790
|
+
lines.push(chalk9.gray(` - ${factor}`));
|
|
6791
|
+
}
|
|
6792
|
+
lines.push("");
|
|
6793
|
+
lines.push(chalk9.cyan("\u{1F4CB} \u521D\u59CB\u5316\u5DE5\u4F5C\u6D41..."));
|
|
6794
|
+
const workflow = workflowEngine || new WorkflowEngine();
|
|
6795
|
+
if (!workflowEngine) {
|
|
6796
|
+
await workflow.initialize(cwd);
|
|
6797
|
+
}
|
|
6798
|
+
const state = await workflow.start(requirement, analysis.score, {
|
|
6799
|
+
title: extractTitle(requirement)
|
|
6800
|
+
});
|
|
6801
|
+
lines.push(chalk9.gray(` \u53D8\u66F4ID: ${state.id}`));
|
|
6802
|
+
lines.push(chalk9.gray(` \u5DE5\u4F5C\u6D41: ${state.type}`));
|
|
6803
|
+
lines.push("");
|
|
6804
|
+
lines.push(chalk9.cyan("\u{1F4DD} \u751F\u6210\u89C4\u683C\u62C6\u5206..."));
|
|
6805
|
+
const spec = await generateSpec(requirement, context, analysis, state.id);
|
|
6806
|
+
const specPath = await saveSpecFile(cwd, spec);
|
|
6807
|
+
lines.push(chalk9.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
6808
|
+
lines.push(chalk9.gray(` \u8DEF\u5F84: ${specPath}`));
|
|
6809
|
+
lines.push("");
|
|
6810
|
+
lines.push(chalk9.cyan.bold("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
|
|
6811
|
+
lines.push(chalk9.white(`
|
|
6812
|
+
${spec.summary}`));
|
|
6813
|
+
if (spec.items.length > 0) {
|
|
6814
|
+
lines.push("");
|
|
6815
|
+
lines.push(chalk9.cyan(" \u4EFB\u52A1\u62C6\u5206:"));
|
|
6816
|
+
for (const item of spec.items) {
|
|
6817
|
+
const priorityIcon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
6818
|
+
lines.push(chalk9.gray(` ${priorityIcon} [${item.id}] ${item.title}`));
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
if (spec.risks.length > 0) {
|
|
6822
|
+
lines.push("");
|
|
6823
|
+
lines.push(chalk9.yellow(" \u26A0\uFE0F \u98CE\u9669\u63D0\u793A:"));
|
|
6824
|
+
for (const risk of spec.risks) {
|
|
6825
|
+
lines.push(chalk9.gray(` - ${risk}`));
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
lines.push("");
|
|
6829
|
+
lines.push(chalk9.yellow.bold("\u23F3 \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
|
|
6830
|
+
lines.push(chalk9.gray("\n\u8BF7\u68C0\u67E5\u751F\u6210\u7684\u89C4\u683C\u6587\u4EF6\uFF0C\u786E\u8BA4\u540E\u7EE7\u7EED:"));
|
|
6831
|
+
lines.push(chalk9.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
|
|
6832
|
+
lines.push(chalk9.white(" /opsx:rollback explore - \u89C4\u683C\u4E0D\u7B26\uFF0C\u91CD\u65B0\u62C6\u5206"));
|
|
6833
|
+
lines.push(chalk9.white(" /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001"));
|
|
6834
|
+
return { output: lines.join("\n") };
|
|
6835
|
+
}
|
|
6836
|
+
async function generateSpec(requirement, context, analysis, changeId) {
|
|
6837
|
+
const spec = {
|
|
6838
|
+
changeId,
|
|
6839
|
+
requirement,
|
|
6840
|
+
summary: "",
|
|
6841
|
+
items: [],
|
|
6842
|
+
architectureNotes: [],
|
|
6843
|
+
risks: [],
|
|
6844
|
+
suggestions: []
|
|
6845
|
+
};
|
|
6846
|
+
spec.summary = generateSummary(requirement);
|
|
6847
|
+
if (analysis.recommendation === "complex") {
|
|
6848
|
+
spec.items = generateComplexTasks(requirement, context, analysis);
|
|
6849
|
+
spec.architectureNotes = generateArchitectureNotes(requirement, context);
|
|
6850
|
+
} else {
|
|
6851
|
+
spec.items = generateSimpleTasks(requirement);
|
|
6852
|
+
}
|
|
6853
|
+
spec.risks = generateRisks(requirement, context, analysis);
|
|
6854
|
+
spec.suggestions = generateSuggestions(requirement, context, analysis);
|
|
6855
|
+
return spec;
|
|
6856
|
+
}
|
|
6857
|
+
function generateSummary(requirement) {
|
|
6858
|
+
const firstSentence = requirement.split(/[。!?\n]/)[0];
|
|
6859
|
+
return firstSentence.length > 100 ? firstSentence.slice(0, 97) + "..." : firstSentence;
|
|
6860
|
+
}
|
|
6861
|
+
function generateComplexTasks(requirement, context, analysis) {
|
|
6862
|
+
const items = [];
|
|
6863
|
+
let itemId = 1;
|
|
6864
|
+
const featurePatterns = [
|
|
6865
|
+
{ pattern: /用户|登录|注册|认证|权限/, title: "\u7528\u6237\u8BA4\u8BC1\u6A21\u5757", priority: "high" },
|
|
6866
|
+
{ pattern: /数据|存储|缓存|数据库/, title: "\u6570\u636E\u5C42\u5B9E\u73B0", priority: "high" },
|
|
6867
|
+
{ pattern: /接口|API|请求|响应/, title: "API \u63A5\u53E3\u5F00\u53D1", priority: "high" },
|
|
6868
|
+
{ pattern: /界面|页面|组件|UI/, title: "\u754C\u9762\u5F00\u53D1", priority: "medium" },
|
|
6869
|
+
{ pattern: /测试|单测|覆盖/, title: "\u6D4B\u8BD5\u7528\u4F8B\u7F16\u5199", priority: "medium" },
|
|
6870
|
+
{ pattern: /文档|说明/, title: "\u6587\u6863\u7F16\u5199", priority: "low" },
|
|
6871
|
+
{ pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", priority: "low" },
|
|
6872
|
+
{ pattern: /优化|性能/, title: "\u6027\u80FD\u4F18\u5316", priority: "medium" },
|
|
6873
|
+
{ pattern: /安全|加密/, title: "\u5B89\u5168\u5B9E\u73B0", priority: "high" },
|
|
6874
|
+
{ pattern: /日志|监控/, title: "\u65E5\u5FD7\u76D1\u63A7", priority: "low" }
|
|
6875
|
+
];
|
|
6876
|
+
for (const { pattern, title, priority } of featurePatterns) {
|
|
6877
|
+
if (pattern.test(requirement)) {
|
|
6878
|
+
items.push({
|
|
6879
|
+
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
6880
|
+
title,
|
|
6881
|
+
description: `${title}\u76F8\u5173\u7684\u529F\u80FD\u5B9E\u73B0`,
|
|
6882
|
+
priority,
|
|
6883
|
+
dependencies: itemId > 1 ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
|
|
6884
|
+
estimatedComplexity: priority === "high" ? 3 : priority === "medium" ? 2 : 1
|
|
6885
|
+
});
|
|
6886
|
+
itemId++;
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
if (items.length === 0) {
|
|
6890
|
+
items.push({
|
|
6891
|
+
id: "T001",
|
|
6892
|
+
title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
|
|
6893
|
+
description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
|
|
6894
|
+
priority: "high",
|
|
6895
|
+
dependencies: [],
|
|
6896
|
+
estimatedComplexity: 2
|
|
6715
6897
|
});
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
"",
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6898
|
+
items.push({
|
|
6899
|
+
id: "T002",
|
|
6900
|
+
title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
|
|
6901
|
+
description: requirement,
|
|
6902
|
+
priority: "high",
|
|
6903
|
+
dependencies: ["T001"],
|
|
6904
|
+
estimatedComplexity: analysis.score
|
|
6905
|
+
});
|
|
6906
|
+
items.push({
|
|
6907
|
+
id: "T003",
|
|
6908
|
+
title: "\u6D4B\u8BD5\u4E0E\u9A8C\u8BC1",
|
|
6909
|
+
description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027",
|
|
6910
|
+
priority: "medium",
|
|
6911
|
+
dependencies: ["T002"],
|
|
6912
|
+
estimatedComplexity: 2
|
|
6913
|
+
});
|
|
6914
|
+
}
|
|
6915
|
+
return items;
|
|
6916
|
+
}
|
|
6917
|
+
function generateSimpleTasks(requirement, context) {
|
|
6918
|
+
return [
|
|
6919
|
+
{
|
|
6920
|
+
id: "T001",
|
|
6921
|
+
title: "\u5B9E\u73B0\u53D8\u66F4",
|
|
6922
|
+
description: requirement,
|
|
6923
|
+
priority: "high",
|
|
6924
|
+
dependencies: [],
|
|
6925
|
+
estimatedComplexity: 3
|
|
6926
|
+
},
|
|
6927
|
+
{
|
|
6928
|
+
id: "T002",
|
|
6929
|
+
title: "\u6D4B\u8BD5\u9A8C\u8BC1",
|
|
6930
|
+
description: "\u9A8C\u8BC1\u53D8\u66F4\u6B63\u786E\u6027",
|
|
6931
|
+
priority: "medium",
|
|
6932
|
+
dependencies: ["T001"],
|
|
6933
|
+
estimatedComplexity: 1
|
|
6741
6934
|
}
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6935
|
+
];
|
|
6936
|
+
}
|
|
6937
|
+
function generateArchitectureNotes(requirement, context) {
|
|
6938
|
+
const notes = [];
|
|
6939
|
+
if (context.framework) {
|
|
6940
|
+
notes.push(`\u9879\u76EE\u4F7F\u7528 ${context.framework} \u6846\u67B6\uFF0C\u9700\u9075\u5FAA\u5176\u6700\u4F73\u5B9E\u8DF5`);
|
|
6941
|
+
}
|
|
6942
|
+
if (requirement.includes("\u6A21\u5757") || requirement.includes("\u7EC4\u4EF6")) {
|
|
6943
|
+
notes.push("\u5EFA\u8BAE\u91C7\u7528\u6A21\u5757\u5316\u8BBE\u8BA1\uFF0C\u4FDD\u6301\u7EC4\u4EF6\u804C\u8D23\u5355\u4E00");
|
|
6944
|
+
}
|
|
6945
|
+
if (requirement.includes("API") || requirement.includes("\u63A5\u53E3")) {
|
|
6946
|
+
notes.push("API \u8BBE\u8BA1\u9700\u8003\u8651\u7248\u672C\u63A7\u5236\u548C\u5411\u540E\u517C\u5BB9");
|
|
6947
|
+
}
|
|
6948
|
+
if (context.structure.srcStructure) {
|
|
6949
|
+
notes.push(`\u73B0\u6709\u6E90\u7801\u7ED3\u6784: ${context.structure.srcStructure}`);
|
|
6950
|
+
}
|
|
6951
|
+
return notes;
|
|
6952
|
+
}
|
|
6953
|
+
function generateRisks(requirement, context, analysis) {
|
|
6954
|
+
const risks = [];
|
|
6955
|
+
if (!context.framework) {
|
|
6956
|
+
risks.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B\uFF0C\u53EF\u80FD\u5F71\u54CD\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u6027");
|
|
6957
|
+
}
|
|
6958
|
+
if (analysis.score >= 7) {
|
|
6959
|
+
risks.push("\u9700\u6C42\u590D\u6742\u5EA6\u8F83\u9AD8\uFF0C\u5EFA\u8BAE\u5206\u9636\u6BB5\u5B9E\u73B0");
|
|
6745
6960
|
}
|
|
6961
|
+
if (requirement.includes("\u8FC1\u79FB") || requirement.includes("\u91CD\u6784")) {
|
|
6962
|
+
risks.push("\u6D89\u53CA\u73B0\u6709\u4EE3\u7801\u4FEE\u6539\uFF0C\u9700\u6CE8\u610F\u56DE\u5F52\u6D4B\u8BD5");
|
|
6963
|
+
}
|
|
6964
|
+
if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168")) {
|
|
6965
|
+
risks.push("\u6D89\u53CA\u5B89\u5168\u654F\u611F\u529F\u80FD\uFF0C\u9700\u8981\u989D\u5916\u5BA1\u67E5");
|
|
6966
|
+
}
|
|
6967
|
+
return risks;
|
|
6968
|
+
}
|
|
6969
|
+
function generateSuggestions(requirement, context, analysis) {
|
|
6970
|
+
const suggestions = [];
|
|
6971
|
+
if (context.norms.devStandards) {
|
|
6972
|
+
suggestions.push("\u9879\u76EE\u5DF2\u6709\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u9075\u5FAA\u73B0\u6709\u89C4\u8303");
|
|
6973
|
+
}
|
|
6974
|
+
if (analysis.recommendation === "complex") {
|
|
6975
|
+
suggestions.push("\u590D\u6742\u9700\u6C42\u5EFA\u8BAE\u5148\u8FDB\u884C\u6280\u672F\u8BC4\u5BA1");
|
|
6976
|
+
suggestions.push("\u5EFA\u8BAE\u8C03\u7528 $architect \u83B7\u53D6\u67B6\u6784\u5EFA\u8BAE");
|
|
6977
|
+
}
|
|
6978
|
+
if (context.techStack.length > 0) {
|
|
6979
|
+
suggestions.push(`\u6280\u672F\u6808: ${context.techStack.join(", ")}`);
|
|
6980
|
+
}
|
|
6981
|
+
return suggestions;
|
|
6982
|
+
}
|
|
6983
|
+
async function saveSpecFile(cwd, spec) {
|
|
6984
|
+
const changesDir = path5.join(cwd, "openspec", "changes");
|
|
6985
|
+
await fs4.mkdir(changesDir, { recursive: true });
|
|
6986
|
+
const specPath = path5.join(changesDir, `${spec.changeId}-spec.md`);
|
|
6987
|
+
const content = formatSpecFile(spec);
|
|
6988
|
+
await fs4.writeFile(specPath, content, "utf-8");
|
|
6989
|
+
return specPath;
|
|
6990
|
+
}
|
|
6991
|
+
function formatSpecFile(spec) {
|
|
6992
|
+
const lines = [];
|
|
6993
|
+
lines.push(`# Spec: ${spec.summary}`);
|
|
6994
|
+
lines.push("");
|
|
6995
|
+
lines.push(`> \u53D8\u66F4ID: ${spec.changeId}`);
|
|
6996
|
+
lines.push(`> \u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
6997
|
+
lines.push("");
|
|
6998
|
+
lines.push("---");
|
|
6999
|
+
lines.push("");
|
|
7000
|
+
lines.push("## \u9700\u6C42\u6982\u8FF0");
|
|
7001
|
+
lines.push("");
|
|
7002
|
+
lines.push(spec.requirement);
|
|
7003
|
+
lines.push("");
|
|
7004
|
+
lines.push("## \u4EFB\u52A1\u62C6\u5206");
|
|
7005
|
+
lines.push("");
|
|
7006
|
+
for (const item of spec.items) {
|
|
7007
|
+
const priorityLabel = item.priority === "high" ? "\u{1F534} \u9AD8" : item.priority === "medium" ? "\u{1F7E1} \u4E2D" : "\u{1F7E2} \u4F4E";
|
|
7008
|
+
lines.push(`### ${item.id}: ${item.title}`);
|
|
7009
|
+
lines.push("");
|
|
7010
|
+
lines.push(`- **\u4F18\u5148\u7EA7**: ${priorityLabel}`);
|
|
7011
|
+
lines.push(`- **\u63CF\u8FF0**: ${item.description}`);
|
|
7012
|
+
lines.push(`- **\u9884\u4F30\u590D\u6742\u5EA6**: ${item.estimatedComplexity}/5`);
|
|
7013
|
+
if (item.dependencies.length > 0) {
|
|
7014
|
+
lines.push(`- **\u4F9D\u8D56**: ${item.dependencies.join(", ")}`);
|
|
7015
|
+
}
|
|
7016
|
+
lines.push("");
|
|
7017
|
+
}
|
|
7018
|
+
if (spec.architectureNotes.length > 0) {
|
|
7019
|
+
lines.push("## \u67B6\u6784\u8BF4\u660E");
|
|
7020
|
+
lines.push("");
|
|
7021
|
+
for (const note of spec.architectureNotes) {
|
|
7022
|
+
lines.push(`- ${note}`);
|
|
7023
|
+
}
|
|
7024
|
+
lines.push("");
|
|
7025
|
+
}
|
|
7026
|
+
if (spec.risks.length > 0) {
|
|
7027
|
+
lines.push("## \u26A0\uFE0F \u98CE\u9669\u8BC4\u4F30");
|
|
7028
|
+
lines.push("");
|
|
7029
|
+
for (const risk of spec.risks) {
|
|
7030
|
+
lines.push(`- ${risk}`);
|
|
7031
|
+
}
|
|
7032
|
+
lines.push("");
|
|
7033
|
+
}
|
|
7034
|
+
if (spec.suggestions.length > 0) {
|
|
7035
|
+
lines.push("## \u{1F4A1} \u5EFA\u8BAE");
|
|
7036
|
+
lines.push("");
|
|
7037
|
+
for (const suggestion of spec.suggestions) {
|
|
7038
|
+
lines.push(`- ${suggestion}`);
|
|
7039
|
+
}
|
|
7040
|
+
lines.push("");
|
|
7041
|
+
}
|
|
7042
|
+
lines.push("---");
|
|
7043
|
+
lines.push("");
|
|
7044
|
+
lines.push("## \u786E\u8BA4\u72B6\u6001");
|
|
7045
|
+
lines.push("");
|
|
7046
|
+
lines.push("- [ ] \u89C4\u683C\u5DF2\u5BA1\u9605");
|
|
7047
|
+
lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
|
|
7048
|
+
lines.push("");
|
|
7049
|
+
lines.push("**\u786E\u8BA4\u540E\u6267\u884C**: `/opsx:confirm spec-review`");
|
|
7050
|
+
return lines.join("\n");
|
|
6746
7051
|
}
|
|
6747
7052
|
function parseArgs(args) {
|
|
6748
7053
|
let forceComplexity;
|
|
@@ -6767,42 +7072,52 @@ async function readProjectContext(cwd) {
|
|
|
6767
7072
|
type: "unknown",
|
|
6768
7073
|
framework: null,
|
|
6769
7074
|
techStack: [],
|
|
6770
|
-
description: ""
|
|
7075
|
+
description: "",
|
|
7076
|
+
structure: {
|
|
7077
|
+
directories: [],
|
|
7078
|
+
keyFiles: [],
|
|
7079
|
+
srcStructure: ""
|
|
7080
|
+
},
|
|
7081
|
+
norms: {
|
|
7082
|
+
devStandards: "",
|
|
7083
|
+
patterns: "",
|
|
7084
|
+
weights: ""
|
|
7085
|
+
}
|
|
7086
|
+
};
|
|
7087
|
+
const [agentsContext, configContext, normsContext, structureContext] = await Promise.all([
|
|
7088
|
+
readAgentsMd(cwd),
|
|
7089
|
+
readConfigYaml(cwd),
|
|
7090
|
+
readNorms(cwd),
|
|
7091
|
+
analyzeStructure(cwd)
|
|
7092
|
+
]);
|
|
7093
|
+
return {
|
|
7094
|
+
...defaultContext,
|
|
7095
|
+
...agentsContext,
|
|
7096
|
+
...configContext,
|
|
7097
|
+
norms: normsContext,
|
|
7098
|
+
structure: structureContext
|
|
6771
7099
|
};
|
|
7100
|
+
}
|
|
7101
|
+
async function readAgentsMd(cwd) {
|
|
6772
7102
|
const agentsPath = path5.join(cwd, "AGENTS.md");
|
|
6773
7103
|
try {
|
|
6774
7104
|
const stats = await fs4.stat(agentsPath);
|
|
6775
7105
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
6776
7106
|
console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
6777
|
-
return
|
|
7107
|
+
return {};
|
|
6778
7108
|
}
|
|
6779
7109
|
const content = await fs4.readFile(agentsPath, "utf-8");
|
|
6780
|
-
return parseAgentsMd(content
|
|
7110
|
+
return parseAgentsMd(content);
|
|
6781
7111
|
} catch (e) {
|
|
6782
7112
|
const err = e;
|
|
6783
7113
|
if (err.code !== "ENOENT") {
|
|
6784
7114
|
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
6785
7115
|
}
|
|
7116
|
+
return {};
|
|
6786
7117
|
}
|
|
6787
|
-
const configPath = path5.join(cwd, "openspec", "config.yaml");
|
|
6788
|
-
try {
|
|
6789
|
-
const stats = await fs4.stat(configPath);
|
|
6790
|
-
if (stats.size > MAX_FILE_SIZE2) {
|
|
6791
|
-
console.warn(`\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
6792
|
-
return defaultContext;
|
|
6793
|
-
}
|
|
6794
|
-
const content = await fs4.readFile(configPath, "utf-8");
|
|
6795
|
-
return parseConfigYaml(content, defaultContext);
|
|
6796
|
-
} catch (e) {
|
|
6797
|
-
const err = e;
|
|
6798
|
-
if (err.code !== "ENOENT") {
|
|
6799
|
-
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
|
|
6800
|
-
}
|
|
6801
|
-
}
|
|
6802
|
-
return defaultContext;
|
|
6803
7118
|
}
|
|
6804
|
-
function parseAgentsMd(content
|
|
6805
|
-
const context = {
|
|
7119
|
+
function parseAgentsMd(content) {
|
|
7120
|
+
const context = {};
|
|
6806
7121
|
const nameMatch = content.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
6807
7122
|
if (nameMatch) {
|
|
6808
7123
|
context.name = nameMatch[1];
|
|
@@ -6819,10 +7134,32 @@ function parseAgentsMd(content, defaults) {
|
|
|
6819
7134
|
if (descMatch) {
|
|
6820
7135
|
context.description = descMatch[1].trim();
|
|
6821
7136
|
}
|
|
7137
|
+
const techStackMatch = content.match(/技术栈[::]\s*([^\n]+)/);
|
|
7138
|
+
if (techStackMatch) {
|
|
7139
|
+
context.techStack = techStackMatch[1].split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
|
|
7140
|
+
}
|
|
6822
7141
|
return context;
|
|
6823
7142
|
}
|
|
6824
|
-
function
|
|
6825
|
-
const
|
|
7143
|
+
async function readConfigYaml(cwd) {
|
|
7144
|
+
const configPath = path5.join(cwd, "openspec", "config.yaml");
|
|
7145
|
+
try {
|
|
7146
|
+
const stats = await fs4.stat(configPath);
|
|
7147
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
7148
|
+
console.warn("\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6");
|
|
7149
|
+
return {};
|
|
7150
|
+
}
|
|
7151
|
+
const content = await fs4.readFile(configPath, "utf-8");
|
|
7152
|
+
return parseConfigYaml(content);
|
|
7153
|
+
} catch (e) {
|
|
7154
|
+
const err = e;
|
|
7155
|
+
if (err.code !== "ENOENT") {
|
|
7156
|
+
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
|
|
7157
|
+
}
|
|
7158
|
+
return {};
|
|
7159
|
+
}
|
|
7160
|
+
}
|
|
7161
|
+
function parseConfigYaml(content) {
|
|
7162
|
+
const context = {};
|
|
6826
7163
|
const nameMatch = content.match(/name:\s*(.+)/);
|
|
6827
7164
|
if (nameMatch) {
|
|
6828
7165
|
context.name = nameMatch[1].trim();
|
|
@@ -6837,6 +7174,67 @@ function parseConfigYaml(content, defaults) {
|
|
|
6837
7174
|
}
|
|
6838
7175
|
return context;
|
|
6839
7176
|
}
|
|
7177
|
+
async function readNorms(cwd) {
|
|
7178
|
+
const normsDir = path5.join(cwd, ".sf-cli", "norms");
|
|
7179
|
+
const norms = {
|
|
7180
|
+
devStandards: "",
|
|
7181
|
+
patterns: "",
|
|
7182
|
+
weights: ""
|
|
7183
|
+
};
|
|
7184
|
+
try {
|
|
7185
|
+
const devStandardsPath = path5.join(normsDir, "devstanded.md");
|
|
7186
|
+
norms.devStandards = await fs4.readFile(devStandardsPath, "utf-8").catch(() => "");
|
|
7187
|
+
} catch {
|
|
7188
|
+
}
|
|
7189
|
+
try {
|
|
7190
|
+
const patternsPath = path5.join(normsDir, "patterns.json");
|
|
7191
|
+
norms.patterns = await fs4.readFile(patternsPath, "utf-8").catch(() => "");
|
|
7192
|
+
} catch {
|
|
7193
|
+
}
|
|
7194
|
+
try {
|
|
7195
|
+
const weightsPath = path5.join(normsDir, "weights.json");
|
|
7196
|
+
norms.weights = await fs4.readFile(weightsPath, "utf-8").catch(() => "");
|
|
7197
|
+
} catch {
|
|
7198
|
+
}
|
|
7199
|
+
return norms;
|
|
7200
|
+
}
|
|
7201
|
+
async function analyzeStructure(cwd) {
|
|
7202
|
+
const structure = {
|
|
7203
|
+
directories: [],
|
|
7204
|
+
keyFiles: [],
|
|
7205
|
+
srcStructure: ""
|
|
7206
|
+
};
|
|
7207
|
+
try {
|
|
7208
|
+
const entries = await fs4.readdir(cwd, { withFileTypes: true });
|
|
7209
|
+
for (const entry of entries) {
|
|
7210
|
+
if (entry.isDirectory() && !["node_modules", "dist", ".git", "build"].includes(entry.name)) {
|
|
7211
|
+
structure.directories.push(entry.name);
|
|
7212
|
+
}
|
|
7213
|
+
}
|
|
7214
|
+
const keyFiles = [
|
|
7215
|
+
"package.json",
|
|
7216
|
+
"tsconfig.json",
|
|
7217
|
+
"AGENTS.md",
|
|
7218
|
+
"README.md"
|
|
7219
|
+
];
|
|
7220
|
+
for (const file of keyFiles) {
|
|
7221
|
+
const filePath = path5.join(cwd, file);
|
|
7222
|
+
try {
|
|
7223
|
+
await fs4.access(filePath);
|
|
7224
|
+
structure.keyFiles.push(file);
|
|
7225
|
+
} catch {
|
|
7226
|
+
}
|
|
7227
|
+
}
|
|
7228
|
+
const srcDir = path5.join(cwd, "src");
|
|
7229
|
+
try {
|
|
7230
|
+
const srcEntries = await fs4.readdir(srcDir, { withFileTypes: true });
|
|
7231
|
+
structure.srcStructure = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name).join("/");
|
|
7232
|
+
} catch {
|
|
7233
|
+
}
|
|
7234
|
+
} catch (e) {
|
|
7235
|
+
}
|
|
7236
|
+
return { structure };
|
|
7237
|
+
}
|
|
6840
7238
|
function analyzeComplexity(requirement, context) {
|
|
6841
7239
|
let score = 3;
|
|
6842
7240
|
const factors = [];
|
|
@@ -6904,6 +7302,78 @@ function extractTitle(requirement) {
|
|
|
6904
7302
|
return requirement.slice(0, 47) + "...";
|
|
6905
7303
|
}
|
|
6906
7304
|
var autoScheduleEnabled = true;
|
|
7305
|
+
var DEFAULT_REGRESSION_CONFIG = {
|
|
7306
|
+
enabled: true,
|
|
7307
|
+
command: "npm test -- --run",
|
|
7308
|
+
timeout: 12e4,
|
|
7309
|
+
// 2分钟
|
|
7310
|
+
coverageThreshold: 80
|
|
7311
|
+
};
|
|
7312
|
+
async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_CONFIG) {
|
|
7313
|
+
const startTime = Date.now();
|
|
7314
|
+
const result = {
|
|
7315
|
+
success: false,
|
|
7316
|
+
passed: 0,
|
|
7317
|
+
failed: 0,
|
|
7318
|
+
total: 0,
|
|
7319
|
+
duration: 0,
|
|
7320
|
+
output: "",
|
|
7321
|
+
errors: []
|
|
7322
|
+
};
|
|
7323
|
+
return new Promise((resolve4) => {
|
|
7324
|
+
const proc = spawn(config.command, [], {
|
|
7325
|
+
cwd: workingDirectory,
|
|
7326
|
+
shell: true,
|
|
7327
|
+
stdio: "pipe"
|
|
7328
|
+
});
|
|
7329
|
+
let stdout = "";
|
|
7330
|
+
let stderr = "";
|
|
7331
|
+
proc.stdout?.on("data", (data) => {
|
|
7332
|
+
stdout += data.toString();
|
|
7333
|
+
});
|
|
7334
|
+
proc.stderr?.on("data", (data) => {
|
|
7335
|
+
stderr += data.toString();
|
|
7336
|
+
});
|
|
7337
|
+
const timeout = setTimeout(() => {
|
|
7338
|
+
proc.kill();
|
|
7339
|
+
result.errors.push("\u6D4B\u8BD5\u8D85\u65F6");
|
|
7340
|
+
result.output = stdout + stderr;
|
|
7341
|
+
result.duration = Date.now() - startTime;
|
|
7342
|
+
resolve4(result);
|
|
7343
|
+
}, config.timeout);
|
|
7344
|
+
proc.on("close", (code) => {
|
|
7345
|
+
clearTimeout(timeout);
|
|
7346
|
+
result.output = stdout + stderr;
|
|
7347
|
+
result.duration = Date.now() - startTime;
|
|
7348
|
+
const passMatch = stdout.match(/(\d+)\s+(?:passed|tests?\s+passed)/i);
|
|
7349
|
+
const failMatch = stdout.match(/(\d+)\s+(?:failed|tests?\s+failed)/i);
|
|
7350
|
+
const totalMatch = stdout.match(/Tests?:\s*(\d+)/i);
|
|
7351
|
+
if (passMatch) {
|
|
7352
|
+
result.passed = parseInt(passMatch[1], 10);
|
|
7353
|
+
}
|
|
7354
|
+
if (failMatch) {
|
|
7355
|
+
result.failed = parseInt(failMatch[1], 10);
|
|
7356
|
+
}
|
|
7357
|
+
if (totalMatch) {
|
|
7358
|
+
result.total = parseInt(totalMatch[1], 10);
|
|
7359
|
+
} else {
|
|
7360
|
+
result.total = result.passed + result.failed;
|
|
7361
|
+
}
|
|
7362
|
+
const coverageMatch = stdout.match(/All files[^\d]*(\d+(?:\.\d+)?)/);
|
|
7363
|
+
if (coverageMatch) {
|
|
7364
|
+
result.coverage = parseFloat(coverageMatch[1]);
|
|
7365
|
+
}
|
|
7366
|
+
result.success = code === 0 && result.failed === 0;
|
|
7367
|
+
resolve4(result);
|
|
7368
|
+
});
|
|
7369
|
+
proc.on("error", (err) => {
|
|
7370
|
+
clearTimeout(timeout);
|
|
7371
|
+
result.errors.push(err.message);
|
|
7372
|
+
result.duration = Date.now() - startTime;
|
|
7373
|
+
resolve4(result);
|
|
7374
|
+
});
|
|
7375
|
+
});
|
|
7376
|
+
}
|
|
6907
7377
|
async function handleOpsx(command, args, ctx) {
|
|
6908
7378
|
const step = command.replace("opsx:", "");
|
|
6909
7379
|
const workflow = new WorkflowEngine();
|
|
@@ -6918,7 +7388,7 @@ async function handleOpsx(command, args, ctx) {
|
|
|
6918
7388
|
case "apply":
|
|
6919
7389
|
return handleApply(workflow);
|
|
6920
7390
|
case "archive":
|
|
6921
|
-
return handleArchive(workflow, args);
|
|
7391
|
+
return handleArchive(workflow, args, ctx);
|
|
6922
7392
|
case "propose":
|
|
6923
7393
|
return handlePropose(workflow, args);
|
|
6924
7394
|
case "status":
|
|
@@ -6928,17 +7398,55 @@ async function handleOpsx(command, args, ctx) {
|
|
|
6928
7398
|
case "rollback":
|
|
6929
7399
|
return handleRollback(workflow, args);
|
|
6930
7400
|
case "confirm":
|
|
6931
|
-
return handleConfirm(workflow, args);
|
|
7401
|
+
return handleConfirm(workflow, args, ctx);
|
|
6932
7402
|
case "next":
|
|
6933
7403
|
return handleNext(workflow);
|
|
6934
7404
|
case "auto":
|
|
6935
7405
|
return handleAutoSchedule(args);
|
|
7406
|
+
case "test":
|
|
7407
|
+
return handleRegressionTest(ctx);
|
|
6936
7408
|
default:
|
|
6937
7409
|
return {
|
|
6938
7410
|
output: chalk9.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
|
|
6939
7411
|
};
|
|
6940
7412
|
}
|
|
6941
7413
|
}
|
|
7414
|
+
async function handleRegressionTest(ctx) {
|
|
7415
|
+
const lines = [];
|
|
7416
|
+
lines.push(chalk9.cyan("\u{1F50D} \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5..."));
|
|
7417
|
+
lines.push(chalk9.gray(` \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`));
|
|
7418
|
+
lines.push("");
|
|
7419
|
+
const result = await runRegressionTest(ctx.options.workingDirectory);
|
|
7420
|
+
lines.push(chalk9.gray("\u2500".repeat(50)));
|
|
7421
|
+
if (result.success) {
|
|
7422
|
+
lines.push(chalk9.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
|
|
7423
|
+
} else {
|
|
7424
|
+
lines.push(chalk9.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
|
|
7425
|
+
}
|
|
7426
|
+
lines.push("");
|
|
7427
|
+
lines.push(chalk9.cyan("\u6D4B\u8BD5\u7ED3\u679C:"));
|
|
7428
|
+
lines.push(chalk9.gray(` \u901A\u8FC7: ${result.passed}`));
|
|
7429
|
+
lines.push(chalk9.gray(` \u5931\u8D25: ${result.failed}`));
|
|
7430
|
+
lines.push(chalk9.gray(` \u603B\u8BA1: ${result.total}`));
|
|
7431
|
+
lines.push(chalk9.gray(` \u8017\u65F6: ${(result.duration / 1e3).toFixed(2)}s`));
|
|
7432
|
+
if (result.coverage !== void 0) {
|
|
7433
|
+
const coverageColor = result.coverage >= 80 ? chalk9.green : result.coverage >= 60 ? chalk9.yellow : chalk9.red;
|
|
7434
|
+
lines.push(coverageColor(` \u8986\u76D6\u7387: ${result.coverage}%`));
|
|
7435
|
+
}
|
|
7436
|
+
if (result.errors.length > 0) {
|
|
7437
|
+
lines.push("");
|
|
7438
|
+
lines.push(chalk9.red("\u9519\u8BEF\u4FE1\u606F:"));
|
|
7439
|
+
for (const error of result.errors) {
|
|
7440
|
+
lines.push(chalk9.gray(` - ${error}`));
|
|
7441
|
+
}
|
|
7442
|
+
}
|
|
7443
|
+
if (result.failed > 0) {
|
|
7444
|
+
lines.push("");
|
|
7445
|
+
lines.push(chalk9.yellow("\u26A0 \u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u8BF7\u68C0\u67E5\u5E76\u4FEE\u590D"));
|
|
7446
|
+
lines.push(chalk9.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
|
|
7447
|
+
}
|
|
7448
|
+
return { output: lines.join("\n") };
|
|
7449
|
+
}
|
|
6942
7450
|
function handleAutoSchedule(args) {
|
|
6943
7451
|
const action = args[0]?.toLowerCase();
|
|
6944
7452
|
if (!action) {
|
|
@@ -7068,13 +7576,70 @@ async function handleArchive(workflow, args, ctx) {
|
|
|
7068
7576
|
${generateConfirmationPrompt(confirmation.point)}`) + chalk9.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
|
|
7069
7577
|
};
|
|
7070
7578
|
}
|
|
7579
|
+
const lines = [];
|
|
7580
|
+
lines.push(chalk9.cyan("\u{1F50D} \u6267\u884C\u5F52\u6863\u524D\u56DE\u5F52\u6D4B\u8BD5..."));
|
|
7581
|
+
lines.push("");
|
|
7582
|
+
const testResult = await runRegressionTest(ctx.options.workingDirectory);
|
|
7583
|
+
if (!testResult.success) {
|
|
7584
|
+
lines.push(chalk9.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
|
|
7585
|
+
lines.push(chalk9.gray(` \u901A\u8FC7: ${testResult.passed} | \u5931\u8D25: ${testResult.failed} | \u603B\u8BA1: ${testResult.total}`));
|
|
7586
|
+
lines.push("");
|
|
7587
|
+
lines.push(chalk9.yellow("\u26A0 \u5F52\u6863\u88AB\u963B\u6B62"));
|
|
7588
|
+
lines.push(chalk9.gray("\n\u8BF7\u4FEE\u590D\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\u540E\u91CD\u8BD5"));
|
|
7589
|
+
lines.push(chalk9.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
|
|
7590
|
+
lines.push(chalk9.gray("\u4F7F\u7528 /opsx:test \u53EF\u5355\u72EC\u8FD0\u884C\u56DE\u5F52\u6D4B\u8BD5"));
|
|
7591
|
+
return { output: lines.join("\n") };
|
|
7592
|
+
}
|
|
7593
|
+
lines.push(chalk9.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
|
|
7594
|
+
lines.push(chalk9.gray(` \u901A\u8FC7: ${testResult.passed} | \u8017\u65F6: ${(testResult.duration / 1e3).toFixed(2)}s`));
|
|
7595
|
+
lines.push("");
|
|
7071
7596
|
const changeId = state.id;
|
|
7072
7597
|
const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
|
|
7073
7598
|
await workflow.archive(summary);
|
|
7074
|
-
|
|
7075
|
-
|
|
7076
|
-
\u53D8\u66F4ID: ${changeId}`) + chalk9.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
|
|
7077
|
-
};
|
|
7599
|
+
await updateChangelog(ctx.options.workingDirectory, summary, changeId);
|
|
7600
|
+
lines.push(chalk9.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9.gray(`
|
|
7601
|
+
\u53D8\u66F4ID: ${changeId}`) + chalk9.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55") + chalk9.gray("\nCHANGELOG.md \u5DF2\u66F4\u65B0"));
|
|
7602
|
+
return { output: lines.join("\n") };
|
|
7603
|
+
}
|
|
7604
|
+
async function updateChangelog(workingDirectory, summary, changeId) {
|
|
7605
|
+
try {
|
|
7606
|
+
const changelogPath = path5.join(workingDirectory, "CHANGELOG.md");
|
|
7607
|
+
const pkgPath = path5.join(workingDirectory, "package.json");
|
|
7608
|
+
let version = "1.0.0";
|
|
7609
|
+
try {
|
|
7610
|
+
const pkgContent = fs10.readFileSync(pkgPath, "utf-8");
|
|
7611
|
+
const pkg = JSON.parse(pkgContent);
|
|
7612
|
+
version = pkg.version || "1.0.0";
|
|
7613
|
+
} catch {
|
|
7614
|
+
}
|
|
7615
|
+
const today = /* @__PURE__ */ new Date();
|
|
7616
|
+
const dateStr = today.toISOString().split("T")[0];
|
|
7617
|
+
const entry = `
|
|
7618
|
+
## v${version} (${dateStr})
|
|
7619
|
+
|
|
7620
|
+
**\u53D8\u66F4\u5185\u5BB9**
|
|
7621
|
+
|
|
7622
|
+
- ${summary} (${changeId})
|
|
7623
|
+
`;
|
|
7624
|
+
if (fs10.existsSync(changelogPath)) {
|
|
7625
|
+
const content = fs10.readFileSync(changelogPath, "utf-8");
|
|
7626
|
+
const versionPattern = /^## v\d+\.\d+\.\d+/m;
|
|
7627
|
+
const match = content.match(versionPattern);
|
|
7628
|
+
if (match && match.index !== void 0) {
|
|
7629
|
+
const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
|
|
7630
|
+
fs10.writeFileSync(changelogPath, newContent, "utf-8");
|
|
7631
|
+
} else {
|
|
7632
|
+
fs10.appendFileSync(changelogPath, entry, "utf-8");
|
|
7633
|
+
}
|
|
7634
|
+
} else {
|
|
7635
|
+
const header = `# Changelog
|
|
7636
|
+
|
|
7637
|
+
All notable changes to this project will be documented in this file.
|
|
7638
|
+
`;
|
|
7639
|
+
fs10.writeFileSync(changelogPath, header + entry, "utf-8");
|
|
7640
|
+
}
|
|
7641
|
+
} catch (error) {
|
|
7642
|
+
}
|
|
7078
7643
|
}
|
|
7079
7644
|
async function handlePropose(workflow, args, ctx) {
|
|
7080
7645
|
const state = workflow.getState();
|
|
@@ -7201,6 +7766,16 @@ async function handleConfirm(workflow, args, ctx) {
|
|
|
7201
7766
|
const type = args[0];
|
|
7202
7767
|
if (!type) {
|
|
7203
7768
|
const pendingPoint = workflow.getCurrentConfirmationPoint();
|
|
7769
|
+
const specPath = await checkPendingSpec(ctx.options.workingDirectory, state.id);
|
|
7770
|
+
if (specPath) {
|
|
7771
|
+
return {
|
|
7772
|
+
output: chalk9.cyan("\u{1F4CB} \u89C4\u683C\u6587\u4EF6\u5F85\u786E\u8BA4") + chalk9.gray(`
|
|
7773
|
+
|
|
7774
|
+
\u89C4\u683C\u6587\u4EF6: ${specPath}`) + chalk9.gray(`
|
|
7775
|
+
|
|
7776
|
+
\u8BF7\u68C0\u67E5\u89C4\u683C\u6587\u4EF6\u540E\u786E\u8BA4:`) + chalk9.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9.white("\n /opsx:rollback explore - \u91CD\u65B0\u62C6\u5206")
|
|
7777
|
+
};
|
|
7778
|
+
}
|
|
7204
7779
|
if (pendingPoint) {
|
|
7205
7780
|
return {
|
|
7206
7781
|
output: chalk9.cyan("\u5F85\u786E\u8BA4\u7684\u68C0\u67E5\u70B9:") + chalk9.white(`
|
|
@@ -7222,12 +7797,54 @@ ${generateConfirmationPrompt(pendingPoint)}`) + chalk9.gray(`
|
|
|
7222
7797
|
}
|
|
7223
7798
|
const comment = args.slice(1).join(" ");
|
|
7224
7799
|
workflow.confirm(type, comment);
|
|
7225
|
-
|
|
7226
|
-
|
|
7800
|
+
const lines = [];
|
|
7801
|
+
lines.push(chalk9.green("\u2713 \u5DF2\u786E\u8BA4"));
|
|
7802
|
+
lines.push(chalk9.white(`
|
|
7803
|
+
${point.name}`));
|
|
7804
|
+
if (comment) {
|
|
7805
|
+
lines.push(chalk9.gray(`
|
|
7806
|
+
\u5907\u6CE8: ${comment}`));
|
|
7807
|
+
}
|
|
7808
|
+
if (type === "spec-review") {
|
|
7809
|
+
const allowed = workflow.getAllowedTransitions();
|
|
7810
|
+
if (allowed.length > 0) {
|
|
7811
|
+
const nextStep = allowed[0];
|
|
7812
|
+
try {
|
|
7813
|
+
const transition = await workflow.transition(nextStep);
|
|
7814
|
+
lines.push("");
|
|
7815
|
+
lines.push(chalk9.cyan(`\u2713 \u5DF2\u81EA\u52A8\u8FDB\u5165 ${nextStep} \u9636\u6BB5`));
|
|
7816
|
+
lines.push(chalk9.gray(`\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`));
|
|
7817
|
+
if (nextStep === "new") {
|
|
7818
|
+
lines.push(chalk9.yellow("\n\u4E0B\u4E00\u6B65: \u8BBE\u8BA1\u65B9\u6848"));
|
|
7819
|
+
lines.push(chalk9.gray(" \u53EF\u8C03\u7528 $architect \u6216 $frontend-dev \u8FDB\u884C\u8BBE\u8BA1"));
|
|
7820
|
+
} else if (nextStep === "apply") {
|
|
7821
|
+
lines.push(chalk9.yellow("\n\u4E0B\u4E00\u6B65: \u6267\u884C\u53D8\u66F4"));
|
|
7822
|
+
lines.push(chalk9.gray(" \u53EF\u8C03\u7528 $frontend-dev \u6267\u884C\u4EE3\u7801\u4FEE\u6539"));
|
|
7823
|
+
}
|
|
7824
|
+
return { output: lines.join("\n") };
|
|
7825
|
+
} catch (e) {
|
|
7826
|
+
if (e instanceof ConfirmationRequiredError) {
|
|
7827
|
+
lines.push(chalk9.yellow("\n\u26A0 \u8FD8\u9700\u8981\u786E\u8BA4:") + chalk9.white(`
|
|
7828
|
+
${generateConfirmationPrompt(e.point)}`) + chalk9.cyan(`
|
|
7227
7829
|
|
|
7228
|
-
${point.
|
|
7229
|
-
|
|
7230
|
-
|
|
7830
|
+
\u4F7F\u7528 /opsx:confirm ${e.point.type}`));
|
|
7831
|
+
return { output: lines.join("\n") };
|
|
7832
|
+
}
|
|
7833
|
+
throw e;
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
lines.push(chalk9.yellow("\n\u4F7F\u7528 /opsx:next \u7EE7\u7EED\u4E0B\u4E00\u9636\u6BB5"));
|
|
7838
|
+
return { output: lines.join("\n") };
|
|
7839
|
+
}
|
|
7840
|
+
async function checkPendingSpec(workingDirectory, changeId) {
|
|
7841
|
+
const specPath = path5.join(workingDirectory, "openspec", "changes", `${changeId}-spec.md`);
|
|
7842
|
+
try {
|
|
7843
|
+
await fs10.promises.access(specPath);
|
|
7844
|
+
return specPath;
|
|
7845
|
+
} catch {
|
|
7846
|
+
return null;
|
|
7847
|
+
}
|
|
7231
7848
|
}
|
|
7232
7849
|
async function handleNext(workflow, args, ctx) {
|
|
7233
7850
|
const state = workflow.getState();
|
|
@@ -7265,9 +7882,25 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9.cyan(`
|
|
|
7265
7882
|
}
|
|
7266
7883
|
|
|
7267
7884
|
// src/commands/runner.ts
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7885
|
+
function getVersion() {
|
|
7886
|
+
const possiblePaths = [
|
|
7887
|
+
path5.resolve(__dirname$1, "..", "..", "package.json"),
|
|
7888
|
+
path5.resolve(__dirname$1, "..", "..", "..", "package.json")
|
|
7889
|
+
];
|
|
7890
|
+
for (const pkgPath of possiblePaths) {
|
|
7891
|
+
try {
|
|
7892
|
+
if (fs10.existsSync(pkgPath)) {
|
|
7893
|
+
const content = fs10.readFileSync(pkgPath, "utf-8");
|
|
7894
|
+
const pkg = JSON.parse(content);
|
|
7895
|
+
return pkg.version;
|
|
7896
|
+
}
|
|
7897
|
+
} catch {
|
|
7898
|
+
continue;
|
|
7899
|
+
}
|
|
7900
|
+
}
|
|
7901
|
+
return "1.0.0";
|
|
7902
|
+
}
|
|
7903
|
+
var VERSION2 = getVersion();
|
|
7271
7904
|
async function runSlashCommand(command, args, ctx) {
|
|
7272
7905
|
const normalizedCommand = normalizeCommand(command);
|
|
7273
7906
|
switch (normalizedCommand) {
|