@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/cli/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var chalk9 = require('chalk');
|
|
6
|
-
var
|
|
6
|
+
var fs9 = require('fs');
|
|
7
7
|
var path6 = require('path');
|
|
8
8
|
var readline = require('readline');
|
|
9
9
|
var fs6 = require('fs/promises');
|
|
@@ -37,7 +37,7 @@ function _interopNamespace(e) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
40
|
-
var
|
|
40
|
+
var fs9__namespace = /*#__PURE__*/_interopNamespace(fs9);
|
|
41
41
|
var path6__namespace = /*#__PURE__*/_interopNamespace(path6);
|
|
42
42
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
43
43
|
var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
|
|
@@ -125,8 +125,8 @@ var CommandParser = class {
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
parseAtPath(input) {
|
|
128
|
-
const
|
|
129
|
-
if (!
|
|
128
|
+
const path16 = input.slice(1).trim();
|
|
129
|
+
if (!path16) {
|
|
130
130
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
131
131
|
}
|
|
132
132
|
return {
|
|
@@ -134,7 +134,7 @@ var CommandParser = class {
|
|
|
134
134
|
command: {
|
|
135
135
|
type: "at" /* AT */,
|
|
136
136
|
raw: input,
|
|
137
|
-
path:
|
|
137
|
+
path: path16
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
}
|
|
@@ -1705,8 +1705,8 @@ var KEY_FILE = ".key";
|
|
|
1705
1705
|
function getOrCreateEncryptionKey() {
|
|
1706
1706
|
const keyPath = path6__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
|
|
1707
1707
|
try {
|
|
1708
|
-
if (
|
|
1709
|
-
const keyBase64 =
|
|
1708
|
+
if (fs9__namespace.existsSync(keyPath)) {
|
|
1709
|
+
const keyBase64 = fs9__namespace.readFileSync(keyPath, "utf-8").trim();
|
|
1710
1710
|
return Buffer.from(keyBase64, "base64");
|
|
1711
1711
|
}
|
|
1712
1712
|
} catch {
|
|
@@ -1714,10 +1714,10 @@ function getOrCreateEncryptionKey() {
|
|
|
1714
1714
|
const key = crypto__namespace.randomBytes(32);
|
|
1715
1715
|
try {
|
|
1716
1716
|
const keyDir = path6__namespace.dirname(keyPath);
|
|
1717
|
-
if (!
|
|
1718
|
-
|
|
1717
|
+
if (!fs9__namespace.existsSync(keyDir)) {
|
|
1718
|
+
fs9__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
1719
1719
|
}
|
|
1720
|
-
|
|
1720
|
+
fs9__namespace.writeFileSync(keyPath, key.toString("base64"), {
|
|
1721
1721
|
mode: 384,
|
|
1722
1722
|
// 仅所有者可读写
|
|
1723
1723
|
encoding: "utf-8"
|
|
@@ -3081,10 +3081,26 @@ function createSpinner(message) {
|
|
|
3081
3081
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
3082
3082
|
};
|
|
3083
3083
|
}
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3084
|
+
function getPackageInfo() {
|
|
3085
|
+
const possiblePaths = [
|
|
3086
|
+
path6__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
3087
|
+
path6__namespace.resolve(__dirname, "..", "..", "..", "package.json")
|
|
3088
|
+
];
|
|
3089
|
+
for (const pkgPath of possiblePaths) {
|
|
3090
|
+
try {
|
|
3091
|
+
if (fs9__namespace.existsSync(pkgPath)) {
|
|
3092
|
+
const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
|
|
3093
|
+
return JSON.parse(content);
|
|
3094
|
+
}
|
|
3095
|
+
} catch {
|
|
3096
|
+
continue;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
return { version: "1.0.0", name: "@nick848/sf-cli" };
|
|
3100
|
+
}
|
|
3101
|
+
var packageInfo = getPackageInfo();
|
|
3102
|
+
var CURRENT_VERSION = packageInfo.version;
|
|
3103
|
+
var PACKAGE_NAME = packageInfo.name;
|
|
3088
3104
|
async function handleUpdate(args, ctx) {
|
|
3089
3105
|
const options = {
|
|
3090
3106
|
check: args.includes("--check") || args.includes("-c"),
|
|
@@ -3298,12 +3314,20 @@ async function exitCLI(ctx, options = {}) {
|
|
|
3298
3314
|
var DEFAULT_CONFIRMATION_POINTS = [
|
|
3299
3315
|
{
|
|
3300
3316
|
type: "spec-review",
|
|
3301
|
-
name: "\u89C4\
|
|
3302
|
-
description: "\u89C4\
|
|
3317
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
3318
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
3303
3319
|
triggerStep: "explore",
|
|
3304
3320
|
targetStep: "new",
|
|
3305
3321
|
required: true
|
|
3306
3322
|
},
|
|
3323
|
+
{
|
|
3324
|
+
type: "spec-review",
|
|
3325
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
3326
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
3327
|
+
triggerStep: "propose",
|
|
3328
|
+
targetStep: "apply",
|
|
3329
|
+
required: true
|
|
3330
|
+
},
|
|
3307
3331
|
{
|
|
3308
3332
|
type: "architecture",
|
|
3309
3333
|
name: "\u67B6\u6784\u8C03\u6574\u786E\u8BA4",
|
|
@@ -3339,19 +3363,15 @@ var ConfirmationManager = class {
|
|
|
3339
3363
|
confirmationPoints;
|
|
3340
3364
|
confirmations = /* @__PURE__ */ new Map();
|
|
3341
3365
|
constructor(customPoints) {
|
|
3342
|
-
|
|
3343
|
-
this.confirmationPoints = new Map(points.map((p) => [p.type, p]));
|
|
3366
|
+
this.confirmationPoints = customPoints || DEFAULT_CONFIRMATION_POINTS;
|
|
3344
3367
|
}
|
|
3345
3368
|
/**
|
|
3346
3369
|
* 获取指定阶段的确认点
|
|
3347
3370
|
*/
|
|
3348
3371
|
getConfirmationPointForTransition(from, to) {
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
}
|
|
3353
|
-
}
|
|
3354
|
-
return void 0;
|
|
3372
|
+
return this.confirmationPoints.find(
|
|
3373
|
+
(point) => point.triggerStep === from && point.targetStep === to
|
|
3374
|
+
);
|
|
3355
3375
|
}
|
|
3356
3376
|
/**
|
|
3357
3377
|
* 检查是否需要确认
|
|
@@ -3364,7 +3384,7 @@ var ConfirmationManager = class {
|
|
|
3364
3384
|
* 获取确认点详情
|
|
3365
3385
|
*/
|
|
3366
3386
|
getConfirmationPoint(type) {
|
|
3367
|
-
return this.confirmationPoints.
|
|
3387
|
+
return this.confirmationPoints.find((point) => point.type === type);
|
|
3368
3388
|
}
|
|
3369
3389
|
/**
|
|
3370
3390
|
* 记录确认
|
|
@@ -3408,14 +3428,14 @@ var ConfirmationManager = class {
|
|
|
3408
3428
|
* 获取所有确认点
|
|
3409
3429
|
*/
|
|
3410
3430
|
getAllConfirmationPoints() {
|
|
3411
|
-
return
|
|
3431
|
+
return [...this.confirmationPoints];
|
|
3412
3432
|
}
|
|
3413
3433
|
/**
|
|
3414
3434
|
* 获取指定阶段需要清除的确认点
|
|
3415
3435
|
*/
|
|
3416
3436
|
getConfirmationsToClear(targetStep) {
|
|
3417
3437
|
const types = [];
|
|
3418
|
-
for (const point of this.confirmationPoints
|
|
3438
|
+
for (const point of this.confirmationPoints) {
|
|
3419
3439
|
const stepOrder = ["explore", "new", "continue", "propose", "apply", "archive"];
|
|
3420
3440
|
const targetIndex = stepOrder.indexOf(targetStep);
|
|
3421
3441
|
const triggerIndex = stepOrder.indexOf(point.triggerStep);
|
|
@@ -3538,6 +3558,26 @@ var WorkflowEngine = class {
|
|
|
3538
3558
|
devStandards: this.devStandards
|
|
3539
3559
|
};
|
|
3540
3560
|
}
|
|
3561
|
+
/**
|
|
3562
|
+
* 获取规格文件路径
|
|
3563
|
+
*/
|
|
3564
|
+
getSpecFilePath() {
|
|
3565
|
+
if (!this.state) return null;
|
|
3566
|
+
return path6__namespace.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
|
|
3567
|
+
}
|
|
3568
|
+
/**
|
|
3569
|
+
* 检查规格文件是否存在
|
|
3570
|
+
*/
|
|
3571
|
+
async hasSpecFile() {
|
|
3572
|
+
const specPath = this.getSpecFilePath();
|
|
3573
|
+
if (!specPath) return false;
|
|
3574
|
+
try {
|
|
3575
|
+
await fs6__namespace.access(specPath);
|
|
3576
|
+
return true;
|
|
3577
|
+
} catch {
|
|
3578
|
+
return false;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3541
3581
|
/**
|
|
3542
3582
|
* 启动新工作流
|
|
3543
3583
|
*/
|
|
@@ -3999,16 +4039,23 @@ async function handleNew(args, ctx) {
|
|
|
3999
4039
|
if (workflowEngine) {
|
|
4000
4040
|
const existingState = workflowEngine.getState();
|
|
4001
4041
|
if (existingState && existingState.status === "running") {
|
|
4042
|
+
if (existingState.currentStep === "explore" || existingState.currentStep === "propose") {
|
|
4043
|
+
const specPath = path6__namespace.join(workingDir, "openspec", "changes", `${existingState.id}-spec.md`);
|
|
4044
|
+
if (fs9__namespace.existsSync(specPath)) {
|
|
4045
|
+
return {
|
|
4046
|
+
output: chalk9__default.default.yellow("\u5F53\u524D\u5DE5\u4F5C\u6D41\u6B63\u5728\u7B49\u5F85\u89C4\u683C\u786E\u8BA4") + chalk9__default.default.gray(`
|
|
4047
|
+
|
|
4048
|
+
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9__default.default.gray(`
|
|
4049
|
+
\u53D8\u66F4ID: ${existingState.id}`) + chalk9__default.default.cyan("\n\n\u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210:") + chalk9__default.default.white(`
|
|
4050
|
+
${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")
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4002
4054
|
return {
|
|
4003
4055
|
output: chalk9__default.default.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray(`
|
|
4004
4056
|
|
|
4005
4057
|
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9__default.default.gray(`
|
|
4006
|
-
\u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9__default.default.gray(
|
|
4007
|
-
|
|
4008
|
-
\u9009\u9879:`) + chalk9__default.default.gray(`
|
|
4009
|
-
1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:${existingState.currentStep}`) + chalk9__default.default.gray(`
|
|
4010
|
-
2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel`) + chalk9__default.default.gray(`
|
|
4011
|
-
3. \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001: /opsx:status`)
|
|
4058
|
+
\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")
|
|
4012
4059
|
};
|
|
4013
4060
|
}
|
|
4014
4061
|
}
|
|
@@ -4023,6 +4070,7 @@ async function handleNew(args, ctx) {
|
|
|
4023
4070
|
async function newFeature(options, workingDir, workflowEngine) {
|
|
4024
4071
|
const cwd = workingDir || process.cwd();
|
|
4025
4072
|
const { requirement, forceComplexity } = options;
|
|
4073
|
+
const lines = [];
|
|
4026
4074
|
try {
|
|
4027
4075
|
const stats = await fs6__namespace.stat(cwd);
|
|
4028
4076
|
if (!stats.isDirectory()) {
|
|
@@ -4030,51 +4078,283 @@ async function newFeature(options, workingDir, workflowEngine) {
|
|
|
4030
4078
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
4031
4079
|
};
|
|
4032
4080
|
}
|
|
4033
|
-
} catch
|
|
4081
|
+
} catch {
|
|
4034
4082
|
return {
|
|
4035
4083
|
output: chalk9__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
4036
4084
|
};
|
|
4037
4085
|
}
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4086
|
+
lines.push(chalk9__default.default.cyan("\u{1F50D} \u5206\u6790\u9879\u76EE..."));
|
|
4087
|
+
const context = await readProjectContext(cwd);
|
|
4088
|
+
lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${context.name}`));
|
|
4089
|
+
lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${context.type}`));
|
|
4090
|
+
lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`));
|
|
4091
|
+
lines.push("");
|
|
4092
|
+
lines.push(chalk9__default.default.cyan("\u{1F4CA} \u5206\u6790\u9700\u6C42\u590D\u6742\u5EA6..."));
|
|
4093
|
+
const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
|
|
4094
|
+
lines.push(chalk9__default.default.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`));
|
|
4095
|
+
lines.push(chalk9__default.default.gray(` \u6D41\u7A0B\u7C7B\u578B: ${analysis.recommendation === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`));
|
|
4096
|
+
for (const factor of analysis.factors) {
|
|
4097
|
+
lines.push(chalk9__default.default.gray(` - ${factor}`));
|
|
4098
|
+
}
|
|
4099
|
+
lines.push("");
|
|
4100
|
+
lines.push(chalk9__default.default.cyan("\u{1F4CB} \u521D\u59CB\u5316\u5DE5\u4F5C\u6D41..."));
|
|
4101
|
+
const workflow = workflowEngine || new WorkflowEngine();
|
|
4102
|
+
if (!workflowEngine) {
|
|
4103
|
+
await workflow.initialize(cwd);
|
|
4104
|
+
}
|
|
4105
|
+
const state = await workflow.start(requirement, analysis.score, {
|
|
4106
|
+
title: extractTitle(requirement)
|
|
4107
|
+
});
|
|
4108
|
+
lines.push(chalk9__default.default.gray(` \u53D8\u66F4ID: ${state.id}`));
|
|
4109
|
+
lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u6D41: ${state.type}`));
|
|
4110
|
+
lines.push("");
|
|
4111
|
+
lines.push(chalk9__default.default.cyan("\u{1F4DD} \u751F\u6210\u89C4\u683C\u62C6\u5206..."));
|
|
4112
|
+
const spec = await generateSpec(requirement, context, analysis, state.id);
|
|
4113
|
+
const specPath = await saveSpecFile(cwd, spec);
|
|
4114
|
+
lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
4115
|
+
lines.push(chalk9__default.default.gray(` \u8DEF\u5F84: ${specPath}`));
|
|
4116
|
+
lines.push("");
|
|
4117
|
+
lines.push(chalk9__default.default.cyan.bold("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
|
|
4118
|
+
lines.push(chalk9__default.default.white(`
|
|
4119
|
+
${spec.summary}`));
|
|
4120
|
+
if (spec.items.length > 0) {
|
|
4121
|
+
lines.push("");
|
|
4122
|
+
lines.push(chalk9__default.default.cyan(" \u4EFB\u52A1\u62C6\u5206:"));
|
|
4123
|
+
for (const item of spec.items) {
|
|
4124
|
+
const priorityIcon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
4125
|
+
lines.push(chalk9__default.default.gray(` ${priorityIcon} [${item.id}] ${item.title}`));
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
if (spec.risks.length > 0) {
|
|
4129
|
+
lines.push("");
|
|
4130
|
+
lines.push(chalk9__default.default.yellow(" \u26A0\uFE0F \u98CE\u9669\u63D0\u793A:"));
|
|
4131
|
+
for (const risk of spec.risks) {
|
|
4132
|
+
lines.push(chalk9__default.default.gray(` - ${risk}`));
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
lines.push("");
|
|
4136
|
+
lines.push(chalk9__default.default.yellow.bold("\u23F3 \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
|
|
4137
|
+
lines.push(chalk9__default.default.gray("\n\u8BF7\u68C0\u67E5\u751F\u6210\u7684\u89C4\u683C\u6587\u4EF6\uFF0C\u786E\u8BA4\u540E\u7EE7\u7EED:"));
|
|
4138
|
+
lines.push(chalk9__default.default.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
|
|
4139
|
+
lines.push(chalk9__default.default.white(" /opsx:rollback explore - \u89C4\u683C\u4E0D\u7B26\uFF0C\u91CD\u65B0\u62C6\u5206"));
|
|
4140
|
+
lines.push(chalk9__default.default.white(" /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001"));
|
|
4141
|
+
return { output: lines.join("\n") };
|
|
4142
|
+
}
|
|
4143
|
+
async function generateSpec(requirement, context, analysis, changeId) {
|
|
4144
|
+
const spec = {
|
|
4145
|
+
changeId,
|
|
4146
|
+
requirement,
|
|
4147
|
+
summary: "",
|
|
4148
|
+
items: [],
|
|
4149
|
+
architectureNotes: [],
|
|
4150
|
+
risks: [],
|
|
4151
|
+
suggestions: []
|
|
4152
|
+
};
|
|
4153
|
+
spec.summary = generateSummary(requirement);
|
|
4154
|
+
if (analysis.recommendation === "complex") {
|
|
4155
|
+
spec.items = generateComplexTasks(requirement, context, analysis);
|
|
4156
|
+
spec.architectureNotes = generateArchitectureNotes(requirement, context);
|
|
4157
|
+
} else {
|
|
4158
|
+
spec.items = generateSimpleTasks(requirement);
|
|
4159
|
+
}
|
|
4160
|
+
spec.risks = generateRisks(requirement, context, analysis);
|
|
4161
|
+
spec.suggestions = generateSuggestions(requirement, context, analysis);
|
|
4162
|
+
return spec;
|
|
4163
|
+
}
|
|
4164
|
+
function generateSummary(requirement) {
|
|
4165
|
+
const firstSentence = requirement.split(/[。!?\n]/)[0];
|
|
4166
|
+
return firstSentence.length > 100 ? firstSentence.slice(0, 97) + "..." : firstSentence;
|
|
4167
|
+
}
|
|
4168
|
+
function generateComplexTasks(requirement, context, analysis) {
|
|
4169
|
+
const items = [];
|
|
4170
|
+
let itemId = 1;
|
|
4171
|
+
const featurePatterns = [
|
|
4172
|
+
{ pattern: /用户|登录|注册|认证|权限/, title: "\u7528\u6237\u8BA4\u8BC1\u6A21\u5757", priority: "high" },
|
|
4173
|
+
{ pattern: /数据|存储|缓存|数据库/, title: "\u6570\u636E\u5C42\u5B9E\u73B0", priority: "high" },
|
|
4174
|
+
{ pattern: /接口|API|请求|响应/, title: "API \u63A5\u53E3\u5F00\u53D1", priority: "high" },
|
|
4175
|
+
{ pattern: /界面|页面|组件|UI/, title: "\u754C\u9762\u5F00\u53D1", priority: "medium" },
|
|
4176
|
+
{ pattern: /测试|单测|覆盖/, title: "\u6D4B\u8BD5\u7528\u4F8B\u7F16\u5199", priority: "medium" },
|
|
4177
|
+
{ pattern: /文档|说明/, title: "\u6587\u6863\u7F16\u5199", priority: "low" },
|
|
4178
|
+
{ pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", priority: "low" },
|
|
4179
|
+
{ pattern: /优化|性能/, title: "\u6027\u80FD\u4F18\u5316", priority: "medium" },
|
|
4180
|
+
{ pattern: /安全|加密/, title: "\u5B89\u5168\u5B9E\u73B0", priority: "high" },
|
|
4181
|
+
{ pattern: /日志|监控/, title: "\u65E5\u5FD7\u76D1\u63A7", priority: "low" }
|
|
4182
|
+
];
|
|
4183
|
+
for (const { pattern, title, priority } of featurePatterns) {
|
|
4184
|
+
if (pattern.test(requirement)) {
|
|
4185
|
+
items.push({
|
|
4186
|
+
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
4187
|
+
title,
|
|
4188
|
+
description: `${title}\u76F8\u5173\u7684\u529F\u80FD\u5B9E\u73B0`,
|
|
4189
|
+
priority,
|
|
4190
|
+
dependencies: itemId > 1 ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
|
|
4191
|
+
estimatedComplexity: priority === "high" ? 3 : priority === "medium" ? 2 : 1
|
|
4192
|
+
});
|
|
4193
|
+
itemId++;
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
if (items.length === 0) {
|
|
4197
|
+
items.push({
|
|
4198
|
+
id: "T001",
|
|
4199
|
+
title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
|
|
4200
|
+
description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
|
|
4201
|
+
priority: "high",
|
|
4202
|
+
dependencies: [],
|
|
4203
|
+
estimatedComplexity: 2
|
|
4047
4204
|
});
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
"",
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4205
|
+
items.push({
|
|
4206
|
+
id: "T002",
|
|
4207
|
+
title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
|
|
4208
|
+
description: requirement,
|
|
4209
|
+
priority: "high",
|
|
4210
|
+
dependencies: ["T001"],
|
|
4211
|
+
estimatedComplexity: analysis.score
|
|
4212
|
+
});
|
|
4213
|
+
items.push({
|
|
4214
|
+
id: "T003",
|
|
4215
|
+
title: "\u6D4B\u8BD5\u4E0E\u9A8C\u8BC1",
|
|
4216
|
+
description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027",
|
|
4217
|
+
priority: "medium",
|
|
4218
|
+
dependencies: ["T002"],
|
|
4219
|
+
estimatedComplexity: 2
|
|
4220
|
+
});
|
|
4221
|
+
}
|
|
4222
|
+
return items;
|
|
4223
|
+
}
|
|
4224
|
+
function generateSimpleTasks(requirement, context) {
|
|
4225
|
+
return [
|
|
4226
|
+
{
|
|
4227
|
+
id: "T001",
|
|
4228
|
+
title: "\u5B9E\u73B0\u53D8\u66F4",
|
|
4229
|
+
description: requirement,
|
|
4230
|
+
priority: "high",
|
|
4231
|
+
dependencies: [],
|
|
4232
|
+
estimatedComplexity: 3
|
|
4233
|
+
},
|
|
4234
|
+
{
|
|
4235
|
+
id: "T002",
|
|
4236
|
+
title: "\u6D4B\u8BD5\u9A8C\u8BC1",
|
|
4237
|
+
description: "\u9A8C\u8BC1\u53D8\u66F4\u6B63\u786E\u6027",
|
|
4238
|
+
priority: "medium",
|
|
4239
|
+
dependencies: ["T001"],
|
|
4240
|
+
estimatedComplexity: 1
|
|
4073
4241
|
}
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4242
|
+
];
|
|
4243
|
+
}
|
|
4244
|
+
function generateArchitectureNotes(requirement, context) {
|
|
4245
|
+
const notes = [];
|
|
4246
|
+
if (context.framework) {
|
|
4247
|
+
notes.push(`\u9879\u76EE\u4F7F\u7528 ${context.framework} \u6846\u67B6\uFF0C\u9700\u9075\u5FAA\u5176\u6700\u4F73\u5B9E\u8DF5`);
|
|
4248
|
+
}
|
|
4249
|
+
if (requirement.includes("\u6A21\u5757") || requirement.includes("\u7EC4\u4EF6")) {
|
|
4250
|
+
notes.push("\u5EFA\u8BAE\u91C7\u7528\u6A21\u5757\u5316\u8BBE\u8BA1\uFF0C\u4FDD\u6301\u7EC4\u4EF6\u804C\u8D23\u5355\u4E00");
|
|
4077
4251
|
}
|
|
4252
|
+
if (requirement.includes("API") || requirement.includes("\u63A5\u53E3")) {
|
|
4253
|
+
notes.push("API \u8BBE\u8BA1\u9700\u8003\u8651\u7248\u672C\u63A7\u5236\u548C\u5411\u540E\u517C\u5BB9");
|
|
4254
|
+
}
|
|
4255
|
+
if (context.structure.srcStructure) {
|
|
4256
|
+
notes.push(`\u73B0\u6709\u6E90\u7801\u7ED3\u6784: ${context.structure.srcStructure}`);
|
|
4257
|
+
}
|
|
4258
|
+
return notes;
|
|
4259
|
+
}
|
|
4260
|
+
function generateRisks(requirement, context, analysis) {
|
|
4261
|
+
const risks = [];
|
|
4262
|
+
if (!context.framework) {
|
|
4263
|
+
risks.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B\uFF0C\u53EF\u80FD\u5F71\u54CD\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u6027");
|
|
4264
|
+
}
|
|
4265
|
+
if (analysis.score >= 7) {
|
|
4266
|
+
risks.push("\u9700\u6C42\u590D\u6742\u5EA6\u8F83\u9AD8\uFF0C\u5EFA\u8BAE\u5206\u9636\u6BB5\u5B9E\u73B0");
|
|
4267
|
+
}
|
|
4268
|
+
if (requirement.includes("\u8FC1\u79FB") || requirement.includes("\u91CD\u6784")) {
|
|
4269
|
+
risks.push("\u6D89\u53CA\u73B0\u6709\u4EE3\u7801\u4FEE\u6539\uFF0C\u9700\u6CE8\u610F\u56DE\u5F52\u6D4B\u8BD5");
|
|
4270
|
+
}
|
|
4271
|
+
if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168")) {
|
|
4272
|
+
risks.push("\u6D89\u53CA\u5B89\u5168\u654F\u611F\u529F\u80FD\uFF0C\u9700\u8981\u989D\u5916\u5BA1\u67E5");
|
|
4273
|
+
}
|
|
4274
|
+
return risks;
|
|
4275
|
+
}
|
|
4276
|
+
function generateSuggestions(requirement, context, analysis) {
|
|
4277
|
+
const suggestions = [];
|
|
4278
|
+
if (context.norms.devStandards) {
|
|
4279
|
+
suggestions.push("\u9879\u76EE\u5DF2\u6709\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u9075\u5FAA\u73B0\u6709\u89C4\u8303");
|
|
4280
|
+
}
|
|
4281
|
+
if (analysis.recommendation === "complex") {
|
|
4282
|
+
suggestions.push("\u590D\u6742\u9700\u6C42\u5EFA\u8BAE\u5148\u8FDB\u884C\u6280\u672F\u8BC4\u5BA1");
|
|
4283
|
+
suggestions.push("\u5EFA\u8BAE\u8C03\u7528 $architect \u83B7\u53D6\u67B6\u6784\u5EFA\u8BAE");
|
|
4284
|
+
}
|
|
4285
|
+
if (context.techStack.length > 0) {
|
|
4286
|
+
suggestions.push(`\u6280\u672F\u6808: ${context.techStack.join(", ")}`);
|
|
4287
|
+
}
|
|
4288
|
+
return suggestions;
|
|
4289
|
+
}
|
|
4290
|
+
async function saveSpecFile(cwd, spec) {
|
|
4291
|
+
const changesDir = path6__namespace.join(cwd, "openspec", "changes");
|
|
4292
|
+
await fs6__namespace.mkdir(changesDir, { recursive: true });
|
|
4293
|
+
const specPath = path6__namespace.join(changesDir, `${spec.changeId}-spec.md`);
|
|
4294
|
+
const content = formatSpecFile(spec);
|
|
4295
|
+
await fs6__namespace.writeFile(specPath, content, "utf-8");
|
|
4296
|
+
return specPath;
|
|
4297
|
+
}
|
|
4298
|
+
function formatSpecFile(spec) {
|
|
4299
|
+
const lines = [];
|
|
4300
|
+
lines.push(`# Spec: ${spec.summary}`);
|
|
4301
|
+
lines.push("");
|
|
4302
|
+
lines.push(`> \u53D8\u66F4ID: ${spec.changeId}`);
|
|
4303
|
+
lines.push(`> \u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
4304
|
+
lines.push("");
|
|
4305
|
+
lines.push("---");
|
|
4306
|
+
lines.push("");
|
|
4307
|
+
lines.push("## \u9700\u6C42\u6982\u8FF0");
|
|
4308
|
+
lines.push("");
|
|
4309
|
+
lines.push(spec.requirement);
|
|
4310
|
+
lines.push("");
|
|
4311
|
+
lines.push("## \u4EFB\u52A1\u62C6\u5206");
|
|
4312
|
+
lines.push("");
|
|
4313
|
+
for (const item of spec.items) {
|
|
4314
|
+
const priorityLabel = item.priority === "high" ? "\u{1F534} \u9AD8" : item.priority === "medium" ? "\u{1F7E1} \u4E2D" : "\u{1F7E2} \u4F4E";
|
|
4315
|
+
lines.push(`### ${item.id}: ${item.title}`);
|
|
4316
|
+
lines.push("");
|
|
4317
|
+
lines.push(`- **\u4F18\u5148\u7EA7**: ${priorityLabel}`);
|
|
4318
|
+
lines.push(`- **\u63CF\u8FF0**: ${item.description}`);
|
|
4319
|
+
lines.push(`- **\u9884\u4F30\u590D\u6742\u5EA6**: ${item.estimatedComplexity}/5`);
|
|
4320
|
+
if (item.dependencies.length > 0) {
|
|
4321
|
+
lines.push(`- **\u4F9D\u8D56**: ${item.dependencies.join(", ")}`);
|
|
4322
|
+
}
|
|
4323
|
+
lines.push("");
|
|
4324
|
+
}
|
|
4325
|
+
if (spec.architectureNotes.length > 0) {
|
|
4326
|
+
lines.push("## \u67B6\u6784\u8BF4\u660E");
|
|
4327
|
+
lines.push("");
|
|
4328
|
+
for (const note of spec.architectureNotes) {
|
|
4329
|
+
lines.push(`- ${note}`);
|
|
4330
|
+
}
|
|
4331
|
+
lines.push("");
|
|
4332
|
+
}
|
|
4333
|
+
if (spec.risks.length > 0) {
|
|
4334
|
+
lines.push("## \u26A0\uFE0F \u98CE\u9669\u8BC4\u4F30");
|
|
4335
|
+
lines.push("");
|
|
4336
|
+
for (const risk of spec.risks) {
|
|
4337
|
+
lines.push(`- ${risk}`);
|
|
4338
|
+
}
|
|
4339
|
+
lines.push("");
|
|
4340
|
+
}
|
|
4341
|
+
if (spec.suggestions.length > 0) {
|
|
4342
|
+
lines.push("## \u{1F4A1} \u5EFA\u8BAE");
|
|
4343
|
+
lines.push("");
|
|
4344
|
+
for (const suggestion of spec.suggestions) {
|
|
4345
|
+
lines.push(`- ${suggestion}`);
|
|
4346
|
+
}
|
|
4347
|
+
lines.push("");
|
|
4348
|
+
}
|
|
4349
|
+
lines.push("---");
|
|
4350
|
+
lines.push("");
|
|
4351
|
+
lines.push("## \u786E\u8BA4\u72B6\u6001");
|
|
4352
|
+
lines.push("");
|
|
4353
|
+
lines.push("- [ ] \u89C4\u683C\u5DF2\u5BA1\u9605");
|
|
4354
|
+
lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
|
|
4355
|
+
lines.push("");
|
|
4356
|
+
lines.push("**\u786E\u8BA4\u540E\u6267\u884C**: `/opsx:confirm spec-review`");
|
|
4357
|
+
return lines.join("\n");
|
|
4078
4358
|
}
|
|
4079
4359
|
function parseArgs(args) {
|
|
4080
4360
|
let forceComplexity;
|
|
@@ -4099,42 +4379,52 @@ async function readProjectContext(cwd) {
|
|
|
4099
4379
|
type: "unknown",
|
|
4100
4380
|
framework: null,
|
|
4101
4381
|
techStack: [],
|
|
4102
|
-
description: ""
|
|
4382
|
+
description: "",
|
|
4383
|
+
structure: {
|
|
4384
|
+
directories: [],
|
|
4385
|
+
keyFiles: [],
|
|
4386
|
+
srcStructure: ""
|
|
4387
|
+
},
|
|
4388
|
+
norms: {
|
|
4389
|
+
devStandards: "",
|
|
4390
|
+
patterns: "",
|
|
4391
|
+
weights: ""
|
|
4392
|
+
}
|
|
4393
|
+
};
|
|
4394
|
+
const [agentsContext, configContext, normsContext, structureContext] = await Promise.all([
|
|
4395
|
+
readAgentsMd(cwd),
|
|
4396
|
+
readConfigYaml(cwd),
|
|
4397
|
+
readNorms(cwd),
|
|
4398
|
+
analyzeStructure(cwd)
|
|
4399
|
+
]);
|
|
4400
|
+
return {
|
|
4401
|
+
...defaultContext,
|
|
4402
|
+
...agentsContext,
|
|
4403
|
+
...configContext,
|
|
4404
|
+
norms: normsContext,
|
|
4405
|
+
structure: structureContext
|
|
4103
4406
|
};
|
|
4407
|
+
}
|
|
4408
|
+
async function readAgentsMd(cwd) {
|
|
4104
4409
|
const agentsPath = path6__namespace.join(cwd, "AGENTS.md");
|
|
4105
4410
|
try {
|
|
4106
4411
|
const stats = await fs6__namespace.stat(agentsPath);
|
|
4107
4412
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
4108
4413
|
console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
4109
|
-
return
|
|
4414
|
+
return {};
|
|
4110
4415
|
}
|
|
4111
4416
|
const content = await fs6__namespace.readFile(agentsPath, "utf-8");
|
|
4112
|
-
return parseAgentsMd(content
|
|
4417
|
+
return parseAgentsMd(content);
|
|
4113
4418
|
} catch (e) {
|
|
4114
4419
|
const err = e;
|
|
4115
4420
|
if (err.code !== "ENOENT") {
|
|
4116
4421
|
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
4117
4422
|
}
|
|
4423
|
+
return {};
|
|
4118
4424
|
}
|
|
4119
|
-
const configPath = path6__namespace.join(cwd, "openspec", "config.yaml");
|
|
4120
|
-
try {
|
|
4121
|
-
const stats = await fs6__namespace.stat(configPath);
|
|
4122
|
-
if (stats.size > MAX_FILE_SIZE2) {
|
|
4123
|
-
console.warn(`\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
4124
|
-
return defaultContext;
|
|
4125
|
-
}
|
|
4126
|
-
const content = await fs6__namespace.readFile(configPath, "utf-8");
|
|
4127
|
-
return parseConfigYaml(content, defaultContext);
|
|
4128
|
-
} catch (e) {
|
|
4129
|
-
const err = e;
|
|
4130
|
-
if (err.code !== "ENOENT") {
|
|
4131
|
-
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4134
|
-
return defaultContext;
|
|
4135
4425
|
}
|
|
4136
|
-
function parseAgentsMd(content
|
|
4137
|
-
const context = {
|
|
4426
|
+
function parseAgentsMd(content) {
|
|
4427
|
+
const context = {};
|
|
4138
4428
|
const nameMatch = content.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
4139
4429
|
if (nameMatch) {
|
|
4140
4430
|
context.name = nameMatch[1];
|
|
@@ -4151,10 +4441,32 @@ function parseAgentsMd(content, defaults) {
|
|
|
4151
4441
|
if (descMatch) {
|
|
4152
4442
|
context.description = descMatch[1].trim();
|
|
4153
4443
|
}
|
|
4444
|
+
const techStackMatch = content.match(/技术栈[::]\s*([^\n]+)/);
|
|
4445
|
+
if (techStackMatch) {
|
|
4446
|
+
context.techStack = techStackMatch[1].split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
|
|
4447
|
+
}
|
|
4154
4448
|
return context;
|
|
4155
4449
|
}
|
|
4156
|
-
function
|
|
4157
|
-
const
|
|
4450
|
+
async function readConfigYaml(cwd) {
|
|
4451
|
+
const configPath = path6__namespace.join(cwd, "openspec", "config.yaml");
|
|
4452
|
+
try {
|
|
4453
|
+
const stats = await fs6__namespace.stat(configPath);
|
|
4454
|
+
if (stats.size > MAX_FILE_SIZE2) {
|
|
4455
|
+
console.warn("\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6");
|
|
4456
|
+
return {};
|
|
4457
|
+
}
|
|
4458
|
+
const content = await fs6__namespace.readFile(configPath, "utf-8");
|
|
4459
|
+
return parseConfigYaml(content);
|
|
4460
|
+
} catch (e) {
|
|
4461
|
+
const err = e;
|
|
4462
|
+
if (err.code !== "ENOENT") {
|
|
4463
|
+
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
|
|
4464
|
+
}
|
|
4465
|
+
return {};
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
function parseConfigYaml(content) {
|
|
4469
|
+
const context = {};
|
|
4158
4470
|
const nameMatch = content.match(/name:\s*(.+)/);
|
|
4159
4471
|
if (nameMatch) {
|
|
4160
4472
|
context.name = nameMatch[1].trim();
|
|
@@ -4169,6 +4481,67 @@ function parseConfigYaml(content, defaults) {
|
|
|
4169
4481
|
}
|
|
4170
4482
|
return context;
|
|
4171
4483
|
}
|
|
4484
|
+
async function readNorms(cwd) {
|
|
4485
|
+
const normsDir = path6__namespace.join(cwd, ".sf-cli", "norms");
|
|
4486
|
+
const norms = {
|
|
4487
|
+
devStandards: "",
|
|
4488
|
+
patterns: "",
|
|
4489
|
+
weights: ""
|
|
4490
|
+
};
|
|
4491
|
+
try {
|
|
4492
|
+
const devStandardsPath = path6__namespace.join(normsDir, "devstanded.md");
|
|
4493
|
+
norms.devStandards = await fs6__namespace.readFile(devStandardsPath, "utf-8").catch(() => "");
|
|
4494
|
+
} catch {
|
|
4495
|
+
}
|
|
4496
|
+
try {
|
|
4497
|
+
const patternsPath = path6__namespace.join(normsDir, "patterns.json");
|
|
4498
|
+
norms.patterns = await fs6__namespace.readFile(patternsPath, "utf-8").catch(() => "");
|
|
4499
|
+
} catch {
|
|
4500
|
+
}
|
|
4501
|
+
try {
|
|
4502
|
+
const weightsPath = path6__namespace.join(normsDir, "weights.json");
|
|
4503
|
+
norms.weights = await fs6__namespace.readFile(weightsPath, "utf-8").catch(() => "");
|
|
4504
|
+
} catch {
|
|
4505
|
+
}
|
|
4506
|
+
return norms;
|
|
4507
|
+
}
|
|
4508
|
+
async function analyzeStructure(cwd) {
|
|
4509
|
+
const structure = {
|
|
4510
|
+
directories: [],
|
|
4511
|
+
keyFiles: [],
|
|
4512
|
+
srcStructure: ""
|
|
4513
|
+
};
|
|
4514
|
+
try {
|
|
4515
|
+
const entries = await fs6__namespace.readdir(cwd, { withFileTypes: true });
|
|
4516
|
+
for (const entry of entries) {
|
|
4517
|
+
if (entry.isDirectory() && !["node_modules", "dist", ".git", "build"].includes(entry.name)) {
|
|
4518
|
+
structure.directories.push(entry.name);
|
|
4519
|
+
}
|
|
4520
|
+
}
|
|
4521
|
+
const keyFiles = [
|
|
4522
|
+
"package.json",
|
|
4523
|
+
"tsconfig.json",
|
|
4524
|
+
"AGENTS.md",
|
|
4525
|
+
"README.md"
|
|
4526
|
+
];
|
|
4527
|
+
for (const file of keyFiles) {
|
|
4528
|
+
const filePath = path6__namespace.join(cwd, file);
|
|
4529
|
+
try {
|
|
4530
|
+
await fs6__namespace.access(filePath);
|
|
4531
|
+
structure.keyFiles.push(file);
|
|
4532
|
+
} catch {
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
const srcDir = path6__namespace.join(cwd, "src");
|
|
4536
|
+
try {
|
|
4537
|
+
const srcEntries = await fs6__namespace.readdir(srcDir, { withFileTypes: true });
|
|
4538
|
+
structure.srcStructure = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name).join("/");
|
|
4539
|
+
} catch {
|
|
4540
|
+
}
|
|
4541
|
+
} catch (e) {
|
|
4542
|
+
}
|
|
4543
|
+
return { structure };
|
|
4544
|
+
}
|
|
4172
4545
|
function analyzeComplexity(requirement, context) {
|
|
4173
4546
|
let score = 3;
|
|
4174
4547
|
const factors = [];
|
|
@@ -4343,10 +4716,10 @@ var FRONTEND_DEV_AGENT = {
|
|
|
4343
4716
|
var CODE_REVIEWER_AGENT = {
|
|
4344
4717
|
id: "code-reviewer",
|
|
4345
4718
|
name: "\u4EE3\u7801\u5BA1\u6838",
|
|
4346
|
-
description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
|
|
4719
|
+
description: "\u5BA1\u6838\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u89C4\u8303\u6027\uFF0C\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE",
|
|
4347
4720
|
icon: "\u{1F50D}",
|
|
4348
|
-
version: "1.
|
|
4349
|
-
role: "\u4F60\u662F\u4E00\u540D\u8D44\u6DF1\u4EE3\u7801\u5BA1\u6838\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u6700\u4F73\u5B9E\u8DF5\u3002\u4F60\u8D1F\u8D23\u5BA1\u67E5\u4EE3\u7801\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE\u3002",
|
|
4721
|
+
version: "1.1.0",
|
|
4722
|
+
role: "\u4F60\u662F\u4E00\u540D\u8D44\u6DF1\u4EE3\u7801\u5BA1\u6838\u4E13\u5BB6\uFF0C\u4E13\u6CE8\u4E8E\u4EE3\u7801\u8D28\u91CF\u3001\u5B89\u5168\u6027\u548C\u6700\u4F73\u5B9E\u8DF5\u3002\u4F60\u8D1F\u8D23\u5BA1\u67E5\u4EE3\u7801\u3001\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE\u3002",
|
|
4350
4723
|
capabilities: [
|
|
4351
4724
|
{
|
|
4352
4725
|
id: "quality-review",
|
|
@@ -4367,13 +4740,24 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4367
4740
|
id: "performance-review",
|
|
4368
4741
|
name: "\u6027\u80FD\u5BA1\u67E5",
|
|
4369
4742
|
description: "\u68C0\u67E5\u6027\u80FD\u95EE\u9898\u548C\u4F18\u5316\u5EFA\u8BAE"
|
|
4743
|
+
},
|
|
4744
|
+
{
|
|
4745
|
+
id: "regression-test",
|
|
4746
|
+
name: "\u56DE\u5F52\u6D4B\u8BD5",
|
|
4747
|
+
description: "\u6267\u884C\u6D4B\u8BD5\u5957\u4EF6\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD"
|
|
4748
|
+
},
|
|
4749
|
+
{
|
|
4750
|
+
id: "coverage-analysis",
|
|
4751
|
+
name: "\u8986\u76D6\u7387\u5206\u6790",
|
|
4752
|
+
description: "\u5206\u6790\u6D4B\u8BD5\u8986\u76D6\u7387\u5E76\u63D0\u4F9B\u6539\u8FDB\u5EFA\u8BAE"
|
|
4370
4753
|
}
|
|
4371
4754
|
],
|
|
4372
4755
|
tools: [
|
|
4373
4756
|
{ name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
4374
4757
|
{ name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
|
|
4375
4758
|
{ name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
4376
|
-
{ name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" }
|
|
4759
|
+
{ name: "list_directory", description: "\u5217\u51FA\u76EE\u5F55\u5185\u5BB9", permission: "full" },
|
|
4760
|
+
{ name: "run_shell_command", description: "\u6267\u884C\u6D4B\u8BD5\u547D\u4EE4", permission: "confirm" }
|
|
4377
4761
|
],
|
|
4378
4762
|
triggers: [
|
|
4379
4763
|
{ type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
|
|
@@ -4384,6 +4768,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4384
4768
|
protectedPaths: ["node_modules", ".git"]
|
|
4385
4769
|
},
|
|
4386
4770
|
behavior: {
|
|
4771
|
+
requireConfirmation: ["run_shell_command"],
|
|
4387
4772
|
autoCommit: false
|
|
4388
4773
|
}
|
|
4389
4774
|
},
|
|
@@ -4392,6 +4777,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4392
4777
|
## \u4F60\u7684\u804C\u8D23
|
|
4393
4778
|
- \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
|
|
4394
4779
|
- \u68C0\u67E5\u5B89\u5168\u6F0F\u6D1E\u548C\u98CE\u9669
|
|
4780
|
+
- \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u786E\u4FDD\u4FEE\u6539\u4E0D\u7834\u574F\u5DF2\u6709\u529F\u80FD
|
|
4395
4781
|
- \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
|
|
4396
4782
|
- \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
|
|
4397
4783
|
|
|
@@ -4400,8 +4786,20 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4400
4786
|
2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
|
|
4401
4787
|
3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
|
|
4402
4788
|
4. **\u89C4\u8303\u6027**: \u547D\u540D\u3001\u683C\u5F0F\u3001\u6CE8\u91CA
|
|
4789
|
+
5. **\u6D4B\u8BD5\u8986\u76D6**: \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C\u3001\u8986\u76D6\u7387\u5206\u6790
|
|
4790
|
+
|
|
4791
|
+
## \u56DE\u5F52\u6D4B\u8BD5\u6D41\u7A0B
|
|
4792
|
+
1. \u6267\u884C \`npm test -- --run\` \u8FD0\u884C\u6D4B\u8BD5\u5957\u4EF6
|
|
4793
|
+
2. \u5206\u6790\u6D4B\u8BD5\u7ED3\u679C\uFF0C\u8BC6\u522B\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
4794
|
+
3. \u5982\u679C\u6D4B\u8BD5\u5931\u8D25\uFF0C\u963B\u6B62\u5F52\u6863\u5E76\u63D0\u793A\u4FEE\u590D
|
|
4795
|
+
4. \u63D0\u4F9B\u8986\u76D6\u7387\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE
|
|
4403
4796
|
|
|
4404
4797
|
## \u8F93\u51FA\u683C\u5F0F
|
|
4798
|
+
### \u56DE\u5F52\u6D4B\u8BD5\u7ED3\u679C
|
|
4799
|
+
- \u6D4B\u8BD5\u901A\u8FC7/\u5931\u8D25\u72B6\u6001
|
|
4800
|
+
- \u901A\u8FC7/\u5931\u8D25\u6570\u91CF
|
|
4801
|
+
- \u8986\u76D6\u7387\u767E\u5206\u6BD4
|
|
4802
|
+
|
|
4405
4803
|
### \u5BA1\u67E5\u7ED3\u679C
|
|
4406
4804
|
- \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
|
|
4407
4805
|
|
|
@@ -4427,7 +4825,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4427
4825
|
## \u4E0A\u4E0B\u6587
|
|
4428
4826
|
{{context}}
|
|
4429
4827
|
|
|
4430
|
-
\u8BF7\u5BF9\u4EE5\u4E0A\u6587\u4EF6\u8FDB\u884C\u5168\u9762\u5BA1\u67E5\uFF0C\u63D0\u4F9B\u8BE6\u7EC6\u7684\u5BA1\u67E5\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE\u3002`,
|
|
4828
|
+
\u8BF7\u5BF9\u4EE5\u4E0A\u6587\u4EF6\u8FDB\u884C\u5168\u9762\u5BA1\u67E5\uFF0C\u6267\u884C\u56DE\u5F52\u6D4B\u8BD5\uFF0C\u5E76\u63D0\u4F9B\u8BE6\u7EC6\u7684\u5BA1\u67E5\u62A5\u544A\u548C\u6539\u8FDB\u5EFA\u8BAE\u3002`,
|
|
4431
4829
|
outputFormat: {
|
|
4432
4830
|
type: "markdown"
|
|
4433
4831
|
}
|
|
@@ -4853,6 +5251,78 @@ ${content}`;
|
|
|
4853
5251
|
|
|
4854
5252
|
// src/commands/opsx.ts
|
|
4855
5253
|
var autoScheduleEnabled = true;
|
|
5254
|
+
var DEFAULT_REGRESSION_CONFIG = {
|
|
5255
|
+
enabled: true,
|
|
5256
|
+
command: "npm test -- --run",
|
|
5257
|
+
timeout: 12e4,
|
|
5258
|
+
// 2分钟
|
|
5259
|
+
coverageThreshold: 80
|
|
5260
|
+
};
|
|
5261
|
+
async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_CONFIG) {
|
|
5262
|
+
const startTime = Date.now();
|
|
5263
|
+
const result = {
|
|
5264
|
+
success: false,
|
|
5265
|
+
passed: 0,
|
|
5266
|
+
failed: 0,
|
|
5267
|
+
total: 0,
|
|
5268
|
+
duration: 0,
|
|
5269
|
+
output: "",
|
|
5270
|
+
errors: []
|
|
5271
|
+
};
|
|
5272
|
+
return new Promise((resolve5) => {
|
|
5273
|
+
const proc = child_process.spawn(config.command, [], {
|
|
5274
|
+
cwd: workingDirectory,
|
|
5275
|
+
shell: true,
|
|
5276
|
+
stdio: "pipe"
|
|
5277
|
+
});
|
|
5278
|
+
let stdout = "";
|
|
5279
|
+
let stderr = "";
|
|
5280
|
+
proc.stdout?.on("data", (data) => {
|
|
5281
|
+
stdout += data.toString();
|
|
5282
|
+
});
|
|
5283
|
+
proc.stderr?.on("data", (data) => {
|
|
5284
|
+
stderr += data.toString();
|
|
5285
|
+
});
|
|
5286
|
+
const timeout = setTimeout(() => {
|
|
5287
|
+
proc.kill();
|
|
5288
|
+
result.errors.push("\u6D4B\u8BD5\u8D85\u65F6");
|
|
5289
|
+
result.output = stdout + stderr;
|
|
5290
|
+
result.duration = Date.now() - startTime;
|
|
5291
|
+
resolve5(result);
|
|
5292
|
+
}, config.timeout);
|
|
5293
|
+
proc.on("close", (code) => {
|
|
5294
|
+
clearTimeout(timeout);
|
|
5295
|
+
result.output = stdout + stderr;
|
|
5296
|
+
result.duration = Date.now() - startTime;
|
|
5297
|
+
const passMatch = stdout.match(/(\d+)\s+(?:passed|tests?\s+passed)/i);
|
|
5298
|
+
const failMatch = stdout.match(/(\d+)\s+(?:failed|tests?\s+failed)/i);
|
|
5299
|
+
const totalMatch = stdout.match(/Tests?:\s*(\d+)/i);
|
|
5300
|
+
if (passMatch) {
|
|
5301
|
+
result.passed = parseInt(passMatch[1], 10);
|
|
5302
|
+
}
|
|
5303
|
+
if (failMatch) {
|
|
5304
|
+
result.failed = parseInt(failMatch[1], 10);
|
|
5305
|
+
}
|
|
5306
|
+
if (totalMatch) {
|
|
5307
|
+
result.total = parseInt(totalMatch[1], 10);
|
|
5308
|
+
} else {
|
|
5309
|
+
result.total = result.passed + result.failed;
|
|
5310
|
+
}
|
|
5311
|
+
const coverageMatch = stdout.match(/All files[^\d]*(\d+(?:\.\d+)?)/);
|
|
5312
|
+
if (coverageMatch) {
|
|
5313
|
+
result.coverage = parseFloat(coverageMatch[1]);
|
|
5314
|
+
}
|
|
5315
|
+
result.success = code === 0 && result.failed === 0;
|
|
5316
|
+
resolve5(result);
|
|
5317
|
+
});
|
|
5318
|
+
proc.on("error", (err) => {
|
|
5319
|
+
clearTimeout(timeout);
|
|
5320
|
+
result.errors.push(err.message);
|
|
5321
|
+
result.duration = Date.now() - startTime;
|
|
5322
|
+
resolve5(result);
|
|
5323
|
+
});
|
|
5324
|
+
});
|
|
5325
|
+
}
|
|
4856
5326
|
async function handleOpsx(command, args, ctx) {
|
|
4857
5327
|
const step = command.replace("opsx:", "");
|
|
4858
5328
|
const workflow = new WorkflowEngine();
|
|
@@ -4867,7 +5337,7 @@ async function handleOpsx(command, args, ctx) {
|
|
|
4867
5337
|
case "apply":
|
|
4868
5338
|
return handleApply(workflow);
|
|
4869
5339
|
case "archive":
|
|
4870
|
-
return handleArchive(workflow, args);
|
|
5340
|
+
return handleArchive(workflow, args, ctx);
|
|
4871
5341
|
case "propose":
|
|
4872
5342
|
return handlePropose(workflow, args);
|
|
4873
5343
|
case "status":
|
|
@@ -4877,17 +5347,55 @@ async function handleOpsx(command, args, ctx) {
|
|
|
4877
5347
|
case "rollback":
|
|
4878
5348
|
return handleRollback(workflow, args);
|
|
4879
5349
|
case "confirm":
|
|
4880
|
-
return handleConfirm(workflow, args);
|
|
5350
|
+
return handleConfirm(workflow, args, ctx);
|
|
4881
5351
|
case "next":
|
|
4882
5352
|
return handleNext(workflow);
|
|
4883
5353
|
case "auto":
|
|
4884
5354
|
return handleAutoSchedule(args);
|
|
5355
|
+
case "test":
|
|
5356
|
+
return handleRegressionTest(ctx);
|
|
4885
5357
|
default:
|
|
4886
5358
|
return {
|
|
4887
5359
|
output: chalk9__default.default.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
|
|
4888
5360
|
};
|
|
4889
5361
|
}
|
|
4890
5362
|
}
|
|
5363
|
+
async function handleRegressionTest(ctx) {
|
|
5364
|
+
const lines = [];
|
|
5365
|
+
lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u56DE\u5F52\u6D4B\u8BD5..."));
|
|
5366
|
+
lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`));
|
|
5367
|
+
lines.push("");
|
|
5368
|
+
const result = await runRegressionTest(ctx.options.workingDirectory);
|
|
5369
|
+
lines.push(chalk9__default.default.gray("\u2500".repeat(50)));
|
|
5370
|
+
if (result.success) {
|
|
5371
|
+
lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
|
|
5372
|
+
} else {
|
|
5373
|
+
lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
|
|
5374
|
+
}
|
|
5375
|
+
lines.push("");
|
|
5376
|
+
lines.push(chalk9__default.default.cyan("\u6D4B\u8BD5\u7ED3\u679C:"));
|
|
5377
|
+
lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${result.passed}`));
|
|
5378
|
+
lines.push(chalk9__default.default.gray(` \u5931\u8D25: ${result.failed}`));
|
|
5379
|
+
lines.push(chalk9__default.default.gray(` \u603B\u8BA1: ${result.total}`));
|
|
5380
|
+
lines.push(chalk9__default.default.gray(` \u8017\u65F6: ${(result.duration / 1e3).toFixed(2)}s`));
|
|
5381
|
+
if (result.coverage !== void 0) {
|
|
5382
|
+
const coverageColor = result.coverage >= 80 ? chalk9__default.default.green : result.coverage >= 60 ? chalk9__default.default.yellow : chalk9__default.default.red;
|
|
5383
|
+
lines.push(coverageColor(` \u8986\u76D6\u7387: ${result.coverage}%`));
|
|
5384
|
+
}
|
|
5385
|
+
if (result.errors.length > 0) {
|
|
5386
|
+
lines.push("");
|
|
5387
|
+
lines.push(chalk9__default.default.red("\u9519\u8BEF\u4FE1\u606F:"));
|
|
5388
|
+
for (const error of result.errors) {
|
|
5389
|
+
lines.push(chalk9__default.default.gray(` - ${error}`));
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
if (result.failed > 0) {
|
|
5393
|
+
lines.push("");
|
|
5394
|
+
lines.push(chalk9__default.default.yellow("\u26A0 \u5B58\u5728\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u8BF7\u68C0\u67E5\u5E76\u4FEE\u590D"));
|
|
5395
|
+
lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
|
|
5396
|
+
}
|
|
5397
|
+
return { output: lines.join("\n") };
|
|
5398
|
+
}
|
|
4891
5399
|
function handleAutoSchedule(args) {
|
|
4892
5400
|
const action = args[0]?.toLowerCase();
|
|
4893
5401
|
if (!action) {
|
|
@@ -5017,13 +5525,70 @@ async function handleArchive(workflow, args, ctx) {
|
|
|
5017
5525
|
${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
|
|
5018
5526
|
};
|
|
5019
5527
|
}
|
|
5528
|
+
const lines = [];
|
|
5529
|
+
lines.push(chalk9__default.default.cyan("\u{1F50D} \u6267\u884C\u5F52\u6863\u524D\u56DE\u5F52\u6D4B\u8BD5..."));
|
|
5530
|
+
lines.push("");
|
|
5531
|
+
const testResult = await runRegressionTest(ctx.options.workingDirectory);
|
|
5532
|
+
if (!testResult.success) {
|
|
5533
|
+
lines.push(chalk9__default.default.red("\u2717 \u56DE\u5F52\u6D4B\u8BD5\u5931\u8D25"));
|
|
5534
|
+
lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u5931\u8D25: ${testResult.failed} | \u603B\u8BA1: ${testResult.total}`));
|
|
5535
|
+
lines.push("");
|
|
5536
|
+
lines.push(chalk9__default.default.yellow("\u26A0 \u5F52\u6863\u88AB\u963B\u6B62"));
|
|
5537
|
+
lines.push(chalk9__default.default.gray("\n\u8BF7\u4FEE\u590D\u5931\u8D25\u7684\u6D4B\u8BD5\u7528\u4F8B\u540E\u91CD\u8BD5"));
|
|
5538
|
+
lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:rollback \u53EF\u56DE\u6EDA\u5230\u4E4B\u524D\u7684\u9636\u6BB5"));
|
|
5539
|
+
lines.push(chalk9__default.default.gray("\u4F7F\u7528 /opsx:test \u53EF\u5355\u72EC\u8FD0\u884C\u56DE\u5F52\u6D4B\u8BD5"));
|
|
5540
|
+
return { output: lines.join("\n") };
|
|
5541
|
+
}
|
|
5542
|
+
lines.push(chalk9__default.default.green("\u2713 \u56DE\u5F52\u6D4B\u8BD5\u901A\u8FC7"));
|
|
5543
|
+
lines.push(chalk9__default.default.gray(` \u901A\u8FC7: ${testResult.passed} | \u8017\u65F6: ${(testResult.duration / 1e3).toFixed(2)}s`));
|
|
5544
|
+
lines.push("");
|
|
5020
5545
|
const changeId = state.id;
|
|
5021
5546
|
const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
|
|
5022
5547
|
await workflow.archive(summary);
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
\u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
|
|
5026
|
-
};
|
|
5548
|
+
await updateChangelog(ctx.options.workingDirectory, summary, changeId);
|
|
5549
|
+
lines.push(chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5F52\u6863") + chalk9__default.default.gray(`
|
|
5550
|
+
\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"));
|
|
5551
|
+
return { output: lines.join("\n") };
|
|
5552
|
+
}
|
|
5553
|
+
async function updateChangelog(workingDirectory, summary, changeId) {
|
|
5554
|
+
try {
|
|
5555
|
+
const changelogPath = path6__namespace.join(workingDirectory, "CHANGELOG.md");
|
|
5556
|
+
const pkgPath = path6__namespace.join(workingDirectory, "package.json");
|
|
5557
|
+
let version = "1.0.0";
|
|
5558
|
+
try {
|
|
5559
|
+
const pkgContent = fs9__namespace.readFileSync(pkgPath, "utf-8");
|
|
5560
|
+
const pkg = JSON.parse(pkgContent);
|
|
5561
|
+
version = pkg.version || "1.0.0";
|
|
5562
|
+
} catch {
|
|
5563
|
+
}
|
|
5564
|
+
const today = /* @__PURE__ */ new Date();
|
|
5565
|
+
const dateStr = today.toISOString().split("T")[0];
|
|
5566
|
+
const entry = `
|
|
5567
|
+
## v${version} (${dateStr})
|
|
5568
|
+
|
|
5569
|
+
**\u53D8\u66F4\u5185\u5BB9**
|
|
5570
|
+
|
|
5571
|
+
- ${summary} (${changeId})
|
|
5572
|
+
`;
|
|
5573
|
+
if (fs9__namespace.existsSync(changelogPath)) {
|
|
5574
|
+
const content = fs9__namespace.readFileSync(changelogPath, "utf-8");
|
|
5575
|
+
const versionPattern = /^## v\d+\.\d+\.\d+/m;
|
|
5576
|
+
const match = content.match(versionPattern);
|
|
5577
|
+
if (match && match.index !== void 0) {
|
|
5578
|
+
const newContent = content.slice(0, match.index) + entry + content.slice(match.index);
|
|
5579
|
+
fs9__namespace.writeFileSync(changelogPath, newContent, "utf-8");
|
|
5580
|
+
} else {
|
|
5581
|
+
fs9__namespace.appendFileSync(changelogPath, entry, "utf-8");
|
|
5582
|
+
}
|
|
5583
|
+
} else {
|
|
5584
|
+
const header = `# Changelog
|
|
5585
|
+
|
|
5586
|
+
All notable changes to this project will be documented in this file.
|
|
5587
|
+
`;
|
|
5588
|
+
fs9__namespace.writeFileSync(changelogPath, header + entry, "utf-8");
|
|
5589
|
+
}
|
|
5590
|
+
} catch (error) {
|
|
5591
|
+
}
|
|
5027
5592
|
}
|
|
5028
5593
|
async function handlePropose(workflow, args, ctx) {
|
|
5029
5594
|
const state = workflow.getState();
|
|
@@ -5150,6 +5715,16 @@ async function handleConfirm(workflow, args, ctx) {
|
|
|
5150
5715
|
const type = args[0];
|
|
5151
5716
|
if (!type) {
|
|
5152
5717
|
const pendingPoint = workflow.getCurrentConfirmationPoint();
|
|
5718
|
+
const specPath = await checkPendingSpec(ctx.options.workingDirectory, state.id);
|
|
5719
|
+
if (specPath) {
|
|
5720
|
+
return {
|
|
5721
|
+
output: chalk9__default.default.cyan("\u{1F4CB} \u89C4\u683C\u6587\u4EF6\u5F85\u786E\u8BA4") + chalk9__default.default.gray(`
|
|
5722
|
+
|
|
5723
|
+
\u89C4\u683C\u6587\u4EF6: ${specPath}`) + chalk9__default.default.gray(`
|
|
5724
|
+
|
|
5725
|
+
\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")
|
|
5726
|
+
};
|
|
5727
|
+
}
|
|
5153
5728
|
if (pendingPoint) {
|
|
5154
5729
|
return {
|
|
5155
5730
|
output: chalk9__default.default.cyan("\u5F85\u786E\u8BA4\u7684\u68C0\u67E5\u70B9:") + chalk9__default.default.white(`
|
|
@@ -5171,12 +5746,54 @@ ${generateConfirmationPrompt(pendingPoint)}`) + chalk9__default.default.gray(`
|
|
|
5171
5746
|
}
|
|
5172
5747
|
const comment = args.slice(1).join(" ");
|
|
5173
5748
|
workflow.confirm(type, comment);
|
|
5174
|
-
|
|
5175
|
-
|
|
5749
|
+
const lines = [];
|
|
5750
|
+
lines.push(chalk9__default.default.green("\u2713 \u5DF2\u786E\u8BA4"));
|
|
5751
|
+
lines.push(chalk9__default.default.white(`
|
|
5752
|
+
${point.name}`));
|
|
5753
|
+
if (comment) {
|
|
5754
|
+
lines.push(chalk9__default.default.gray(`
|
|
5755
|
+
\u5907\u6CE8: ${comment}`));
|
|
5756
|
+
}
|
|
5757
|
+
if (type === "spec-review") {
|
|
5758
|
+
const allowed = workflow.getAllowedTransitions();
|
|
5759
|
+
if (allowed.length > 0) {
|
|
5760
|
+
const nextStep = allowed[0];
|
|
5761
|
+
try {
|
|
5762
|
+
const transition = await workflow.transition(nextStep);
|
|
5763
|
+
lines.push("");
|
|
5764
|
+
lines.push(chalk9__default.default.cyan(`\u2713 \u5DF2\u81EA\u52A8\u8FDB\u5165 ${nextStep} \u9636\u6BB5`));
|
|
5765
|
+
lines.push(chalk9__default.default.gray(`\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`));
|
|
5766
|
+
if (nextStep === "new") {
|
|
5767
|
+
lines.push(chalk9__default.default.yellow("\n\u4E0B\u4E00\u6B65: \u8BBE\u8BA1\u65B9\u6848"));
|
|
5768
|
+
lines.push(chalk9__default.default.gray(" \u53EF\u8C03\u7528 $architect \u6216 $frontend-dev \u8FDB\u884C\u8BBE\u8BA1"));
|
|
5769
|
+
} else if (nextStep === "apply") {
|
|
5770
|
+
lines.push(chalk9__default.default.yellow("\n\u4E0B\u4E00\u6B65: \u6267\u884C\u53D8\u66F4"));
|
|
5771
|
+
lines.push(chalk9__default.default.gray(" \u53EF\u8C03\u7528 $frontend-dev \u6267\u884C\u4EE3\u7801\u4FEE\u6539"));
|
|
5772
|
+
}
|
|
5773
|
+
return { output: lines.join("\n") };
|
|
5774
|
+
} catch (e) {
|
|
5775
|
+
if (e instanceof ConfirmationRequiredError) {
|
|
5776
|
+
lines.push(chalk9__default.default.yellow("\n\u26A0 \u8FD8\u9700\u8981\u786E\u8BA4:") + chalk9__default.default.white(`
|
|
5777
|
+
${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
|
|
5176
5778
|
|
|
5177
|
-
${point.
|
|
5178
|
-
|
|
5179
|
-
|
|
5779
|
+
\u4F7F\u7528 /opsx:confirm ${e.point.type}`));
|
|
5780
|
+
return { output: lines.join("\n") };
|
|
5781
|
+
}
|
|
5782
|
+
throw e;
|
|
5783
|
+
}
|
|
5784
|
+
}
|
|
5785
|
+
}
|
|
5786
|
+
lines.push(chalk9__default.default.yellow("\n\u4F7F\u7528 /opsx:next \u7EE7\u7EED\u4E0B\u4E00\u9636\u6BB5"));
|
|
5787
|
+
return { output: lines.join("\n") };
|
|
5788
|
+
}
|
|
5789
|
+
async function checkPendingSpec(workingDirectory, changeId) {
|
|
5790
|
+
const specPath = path6__namespace.join(workingDirectory, "openspec", "changes", `${changeId}-spec.md`);
|
|
5791
|
+
try {
|
|
5792
|
+
await fs9__namespace.promises.access(specPath);
|
|
5793
|
+
return specPath;
|
|
5794
|
+
} catch {
|
|
5795
|
+
return null;
|
|
5796
|
+
}
|
|
5180
5797
|
}
|
|
5181
5798
|
async function handleNext(workflow, args, ctx) {
|
|
5182
5799
|
const state = workflow.getState();
|
|
@@ -5214,9 +5831,25 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
|
|
|
5214
5831
|
}
|
|
5215
5832
|
|
|
5216
5833
|
// src/commands/runner.ts
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5834
|
+
function getVersion() {
|
|
5835
|
+
const possiblePaths = [
|
|
5836
|
+
path6__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
5837
|
+
path6__namespace.resolve(__dirname, "..", "..", "..", "package.json")
|
|
5838
|
+
];
|
|
5839
|
+
for (const pkgPath of possiblePaths) {
|
|
5840
|
+
try {
|
|
5841
|
+
if (fs9__namespace.existsSync(pkgPath)) {
|
|
5842
|
+
const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
|
|
5843
|
+
const pkg = JSON.parse(content);
|
|
5844
|
+
return pkg.version;
|
|
5845
|
+
}
|
|
5846
|
+
} catch {
|
|
5847
|
+
continue;
|
|
5848
|
+
}
|
|
5849
|
+
}
|
|
5850
|
+
return "1.0.0";
|
|
5851
|
+
}
|
|
5852
|
+
var VERSION2 = getVersion();
|
|
5220
5853
|
async function runSlashCommand(command, args, ctx) {
|
|
5221
5854
|
const normalizedCommand = normalizeCommand(command);
|
|
5222
5855
|
switch (normalizedCommand) {
|
|
@@ -6222,9 +6855,27 @@ async function startInteractiveMode(options) {
|
|
|
6222
6855
|
}
|
|
6223
6856
|
|
|
6224
6857
|
// src/cli/index.ts
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6858
|
+
function getPackageJson() {
|
|
6859
|
+
const possiblePaths = [
|
|
6860
|
+
// 开发环境: dist/cli/index.js -> ../../package.json (项目根目录)
|
|
6861
|
+
path6__namespace.resolve(__dirname, "..", "..", "package.json"),
|
|
6862
|
+
// 生产环境: node_modules/@nick848/sf-cli/dist/cli/index.js -> ../../package.json
|
|
6863
|
+
path6__namespace.resolve(__dirname, "..", "..", "package.json")
|
|
6864
|
+
];
|
|
6865
|
+
for (const pkgPath of possiblePaths) {
|
|
6866
|
+
try {
|
|
6867
|
+
if (fs9__namespace.existsSync(pkgPath)) {
|
|
6868
|
+
const content = fs9__namespace.readFileSync(pkgPath, "utf-8");
|
|
6869
|
+
return JSON.parse(content);
|
|
6870
|
+
}
|
|
6871
|
+
} catch {
|
|
6872
|
+
continue;
|
|
6873
|
+
}
|
|
6874
|
+
}
|
|
6875
|
+
return { version: "1.0.0", name: "@nick848/sf-cli" };
|
|
6876
|
+
}
|
|
6877
|
+
var packageJson = getPackageJson();
|
|
6878
|
+
var VERSION3 = packageJson.version;
|
|
6228
6879
|
var NAME = "sf-cli";
|
|
6229
6880
|
commander.program.name(NAME).description("\u4E13\u4E3A\u524D\u7AEF\u5F00\u53D1\u8BBE\u8BA1\u7684AI\u9A71\u52A8CLI\u5DE5\u5177").version(VERSION3, "-v, --version", "\u663E\u793A\u7248\u672C\u53F7");
|
|
6230
6881
|
commander.program.argument("[directory]", "\u5DE5\u4F5C\u76EE\u5F55", process.cwd()).option("-m, --model <model>", "\u6307\u5B9AAI\u6A21\u578B").option("-y, --yolo", "\u81EA\u52A8\u786E\u8BA4\u6240\u6709\u64CD\u4F5C").action(async (directory, options) => {
|