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