@nick848/sf-cli 1.0.1 → 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 +459 -182
- 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 +313 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +375 -109
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/cli/index.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var chalk9 = require('chalk');
|
|
6
|
+
var fs9 = require('fs');
|
|
7
|
+
var path6 = require('path');
|
|
6
8
|
var readline = require('readline');
|
|
7
|
-
var
|
|
8
|
-
var fs5 = require('fs/promises');
|
|
9
|
+
var fs6 = require('fs/promises');
|
|
9
10
|
var enquirer = require('enquirer');
|
|
10
|
-
var fsSync = require('fs');
|
|
11
11
|
var crypto = require('crypto');
|
|
12
12
|
var os = require('os');
|
|
13
13
|
var tiktoken = require('tiktoken');
|
|
@@ -15,7 +15,6 @@ require('@modelcontextprotocol/sdk/client/index.js');
|
|
|
15
15
|
require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
16
16
|
var child_process = require('child_process');
|
|
17
17
|
require('uuid');
|
|
18
|
-
var module$1 = require('module');
|
|
19
18
|
|
|
20
19
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
20
|
|
|
@@ -38,17 +37,13 @@ function _interopNamespace(e) {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
40
|
+
var fs9__namespace = /*#__PURE__*/_interopNamespace(fs9);
|
|
41
|
+
var path6__namespace = /*#__PURE__*/_interopNamespace(path6);
|
|
41
42
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
42
|
-
var
|
|
43
|
-
var fs5__namespace = /*#__PURE__*/_interopNamespace(fs5);
|
|
44
|
-
var fsSync__namespace = /*#__PURE__*/_interopNamespace(fsSync);
|
|
43
|
+
var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
|
|
45
44
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
46
45
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
47
46
|
|
|
48
|
-
// node_modules/tsup/assets/cjs_shims.js
|
|
49
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
50
|
-
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
51
|
-
|
|
52
47
|
// src/cli/parser.ts
|
|
53
48
|
var CommandParser = class {
|
|
54
49
|
slashCommands = [
|
|
@@ -130,8 +125,8 @@ var CommandParser = class {
|
|
|
130
125
|
};
|
|
131
126
|
}
|
|
132
127
|
parseAtPath(input) {
|
|
133
|
-
const
|
|
134
|
-
if (!
|
|
128
|
+
const path16 = input.slice(1).trim();
|
|
129
|
+
if (!path16) {
|
|
135
130
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
136
131
|
}
|
|
137
132
|
return {
|
|
@@ -139,7 +134,7 @@ var CommandParser = class {
|
|
|
139
134
|
command: {
|
|
140
135
|
type: "at" /* AT */,
|
|
141
136
|
raw: input,
|
|
142
|
-
path:
|
|
137
|
+
path: path16
|
|
143
138
|
}
|
|
144
139
|
};
|
|
145
140
|
}
|
|
@@ -211,11 +206,11 @@ var NormsManager = class {
|
|
|
211
206
|
const files = await this.collectProjectFiles(projectPath);
|
|
212
207
|
for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
|
|
213
208
|
try {
|
|
214
|
-
const stats = await
|
|
209
|
+
const stats = await fs6__namespace.stat(filePath);
|
|
215
210
|
if (stats.size > MAX_FILE_SIZE) {
|
|
216
211
|
continue;
|
|
217
212
|
}
|
|
218
|
-
const content = await
|
|
213
|
+
const content = await fs6__namespace.readFile(filePath, "utf-8");
|
|
219
214
|
const patterns = await this.learnFromFile(filePath, content);
|
|
220
215
|
result.patternsFound += patterns.length;
|
|
221
216
|
result.filesScanned++;
|
|
@@ -238,7 +233,7 @@ var NormsManager = class {
|
|
|
238
233
|
* 从单个文件学习规范
|
|
239
234
|
*/
|
|
240
235
|
async learnFromFile(filePath, content) {
|
|
241
|
-
const ext =
|
|
236
|
+
const ext = path6__namespace.extname(filePath);
|
|
242
237
|
const patterns = this.extractPatterns(filePath, content, ext);
|
|
243
238
|
for (const pattern of patterns) {
|
|
244
239
|
await this.recordOccurrence({
|
|
@@ -841,16 +836,16 @@ ${response}`;
|
|
|
841
836
|
const files = [];
|
|
842
837
|
async function scan(dir) {
|
|
843
838
|
try {
|
|
844
|
-
const entries = await
|
|
839
|
+
const entries = await fs6__namespace.readdir(dir, { withFileTypes: true });
|
|
845
840
|
for (const entry of entries) {
|
|
846
841
|
if (entry.isDirectory()) {
|
|
847
842
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
848
|
-
await scan(
|
|
843
|
+
await scan(path6__namespace.join(dir, entry.name));
|
|
849
844
|
}
|
|
850
845
|
} else if (entry.isFile()) {
|
|
851
|
-
const ext =
|
|
846
|
+
const ext = path6__namespace.extname(entry.name);
|
|
852
847
|
if (SCAN_EXTENSIONS.includes(ext)) {
|
|
853
|
-
files.push(
|
|
848
|
+
files.push(path6__namespace.join(dir, entry.name));
|
|
854
849
|
}
|
|
855
850
|
}
|
|
856
851
|
}
|
|
@@ -884,16 +879,16 @@ ${response}`;
|
|
|
884
879
|
* 确保规范目录存在
|
|
885
880
|
*/
|
|
886
881
|
async ensureNormsDir() {
|
|
887
|
-
await
|
|
888
|
-
await
|
|
882
|
+
await fs6__namespace.mkdir(this.config.normsDir, { recursive: true });
|
|
883
|
+
await fs6__namespace.mkdir(path6__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
889
884
|
}
|
|
890
885
|
/**
|
|
891
886
|
* 加载已有规范
|
|
892
887
|
*/
|
|
893
888
|
async loadStandards() {
|
|
894
|
-
const standardsPath =
|
|
889
|
+
const standardsPath = path6__namespace.join(this.config.normsDir, "patterns.json");
|
|
895
890
|
try {
|
|
896
|
-
const content = await
|
|
891
|
+
const content = await fs6__namespace.readFile(standardsPath, "utf-8");
|
|
897
892
|
const data = JSON.parse(content);
|
|
898
893
|
for (const standard of data) {
|
|
899
894
|
this.standards.set(standard.id, {
|
|
@@ -909,9 +904,9 @@ ${response}`;
|
|
|
909
904
|
* 加载权重数据
|
|
910
905
|
*/
|
|
911
906
|
async loadWeights() {
|
|
912
|
-
const weightsPath =
|
|
907
|
+
const weightsPath = path6__namespace.join(this.config.normsDir, "weights.json");
|
|
913
908
|
try {
|
|
914
|
-
const content = await
|
|
909
|
+
const content = await fs6__namespace.readFile(weightsPath, "utf-8");
|
|
915
910
|
const data = JSON.parse(content);
|
|
916
911
|
for (const weight of data) {
|
|
917
912
|
this.weights.set(weight.standardId, {
|
|
@@ -926,8 +921,8 @@ ${response}`;
|
|
|
926
921
|
* 保存规范
|
|
927
922
|
*/
|
|
928
923
|
async saveStandards() {
|
|
929
|
-
const standardsPath =
|
|
930
|
-
await
|
|
924
|
+
const standardsPath = path6__namespace.join(this.config.normsDir, "patterns.json");
|
|
925
|
+
await fs6__namespace.writeFile(
|
|
931
926
|
standardsPath,
|
|
932
927
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
933
928
|
"utf-8"
|
|
@@ -937,8 +932,8 @@ ${response}`;
|
|
|
937
932
|
* 保存权重
|
|
938
933
|
*/
|
|
939
934
|
async saveWeights() {
|
|
940
|
-
const weightsPath =
|
|
941
|
-
await
|
|
935
|
+
const weightsPath = path6__namespace.join(this.config.normsDir, "weights.json");
|
|
936
|
+
await fs6__namespace.writeFile(
|
|
942
937
|
weightsPath,
|
|
943
938
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
944
939
|
"utf-8"
|
|
@@ -965,8 +960,8 @@ ${response}`;
|
|
|
965
960
|
* 保存周报
|
|
966
961
|
*/
|
|
967
962
|
async saveWeeklyReport(weekId, report) {
|
|
968
|
-
const reportPath =
|
|
969
|
-
await
|
|
963
|
+
const reportPath = path6__namespace.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
964
|
+
await fs6__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
970
965
|
}
|
|
971
966
|
/**
|
|
972
967
|
* 更新 devstanded.md
|
|
@@ -974,8 +969,8 @@ ${response}`;
|
|
|
974
969
|
async updateDevStandedMd() {
|
|
975
970
|
const standards = this.getEffectiveStandards();
|
|
976
971
|
const content = this.generateDevStandedMd(standards);
|
|
977
|
-
const mdPath =
|
|
978
|
-
await
|
|
972
|
+
const mdPath = path6__namespace.join(this.config.normsDir, "devstanded.md");
|
|
973
|
+
await fs6__namespace.writeFile(mdPath, content, "utf-8");
|
|
979
974
|
}
|
|
980
975
|
/**
|
|
981
976
|
* 生成 devstanded.md 内容
|
|
@@ -1114,7 +1109,7 @@ async function handleInit(args, ctx) {
|
|
|
1114
1109
|
async function initProject(options = {}, workingDir) {
|
|
1115
1110
|
const cwd = workingDir || process.cwd();
|
|
1116
1111
|
try {
|
|
1117
|
-
const stats = await
|
|
1112
|
+
const stats = await fs6__namespace.stat(cwd);
|
|
1118
1113
|
if (!stats.isDirectory()) {
|
|
1119
1114
|
return {
|
|
1120
1115
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -1125,9 +1120,9 @@ async function initProject(options = {}, workingDir) {
|
|
|
1125
1120
|
output: chalk9__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
1126
1121
|
};
|
|
1127
1122
|
}
|
|
1128
|
-
const sfCliDir =
|
|
1129
|
-
const openspecDir =
|
|
1130
|
-
const agentsMdPath =
|
|
1123
|
+
const sfCliDir = path6__namespace.join(cwd, ".sf-cli");
|
|
1124
|
+
const openspecDir = path6__namespace.join(cwd, "openspec");
|
|
1125
|
+
const agentsMdPath = path6__namespace.join(cwd, "AGENTS.md");
|
|
1131
1126
|
try {
|
|
1132
1127
|
const agentsExists = await fileExists(agentsMdPath);
|
|
1133
1128
|
if (agentsExists && !options.force) {
|
|
@@ -1146,7 +1141,7 @@ async function initProject(options = {}, workingDir) {
|
|
|
1146
1141
|
await normsManager.initialize();
|
|
1147
1142
|
const scanResult = await normsManager.scanProject(cwd);
|
|
1148
1143
|
const agentsContent = generateAgentsMd(projectInfo, scanResult);
|
|
1149
|
-
await
|
|
1144
|
+
await fs6__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
|
|
1150
1145
|
await generateOpenSpecConfig(openspecDir, projectInfo);
|
|
1151
1146
|
return {
|
|
1152
1147
|
output: chalk9__default.default.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9__default.default.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
|
|
@@ -1187,7 +1182,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
1187
1182
|
"logs"
|
|
1188
1183
|
];
|
|
1189
1184
|
for (const dir of dirs) {
|
|
1190
|
-
await
|
|
1185
|
+
await fs6__namespace.mkdir(path6__namespace.join(basePath, dir), { recursive: true });
|
|
1191
1186
|
}
|
|
1192
1187
|
const config = {
|
|
1193
1188
|
version: "1.0.0",
|
|
@@ -1195,37 +1190,37 @@ async function createSfCliDirectory(basePath) {
|
|
|
1195
1190
|
yolo: false,
|
|
1196
1191
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1197
1192
|
};
|
|
1198
|
-
await
|
|
1199
|
-
|
|
1193
|
+
await fs6__namespace.writeFile(
|
|
1194
|
+
path6__namespace.join(basePath, "config.json"),
|
|
1200
1195
|
JSON.stringify(config, null, 2),
|
|
1201
1196
|
"utf-8"
|
|
1202
1197
|
);
|
|
1203
|
-
await
|
|
1204
|
-
|
|
1198
|
+
await fs6__namespace.writeFile(
|
|
1199
|
+
path6__namespace.join(basePath, "agents", "registry.json"),
|
|
1205
1200
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
1206
1201
|
"utf-8"
|
|
1207
1202
|
);
|
|
1208
|
-
await
|
|
1209
|
-
|
|
1203
|
+
await fs6__namespace.writeFile(
|
|
1204
|
+
path6__namespace.join(basePath, "skills", "registry.json"),
|
|
1210
1205
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
1211
1206
|
"utf-8"
|
|
1212
1207
|
);
|
|
1213
|
-
await
|
|
1214
|
-
|
|
1208
|
+
await fs6__namespace.writeFile(
|
|
1209
|
+
path6__namespace.join(basePath, "health", "health.md"),
|
|
1215
1210
|
generateHealthTemplate(),
|
|
1216
1211
|
"utf-8"
|
|
1217
1212
|
);
|
|
1218
1213
|
}
|
|
1219
1214
|
async function createOpenSpecDirectory(basePath) {
|
|
1220
|
-
const changesDir =
|
|
1221
|
-
const archiveDir =
|
|
1222
|
-
const specDir =
|
|
1223
|
-
await
|
|
1224
|
-
await
|
|
1215
|
+
const changesDir = path6__namespace.join(basePath, "changes");
|
|
1216
|
+
const archiveDir = path6__namespace.join(changesDir, "archive");
|
|
1217
|
+
const specDir = path6__namespace.join(basePath, "spec");
|
|
1218
|
+
await fs6__namespace.mkdir(archiveDir, { recursive: true });
|
|
1219
|
+
await fs6__namespace.mkdir(specDir, { recursive: true });
|
|
1225
1220
|
}
|
|
1226
1221
|
async function analyzeProject(cwd) {
|
|
1227
1222
|
const result = {
|
|
1228
|
-
name:
|
|
1223
|
+
name: path6__namespace.basename(cwd),
|
|
1229
1224
|
type: "unknown",
|
|
1230
1225
|
framework: null,
|
|
1231
1226
|
techStack: [],
|
|
@@ -1244,9 +1239,9 @@ async function analyzeProject(cwd) {
|
|
|
1244
1239
|
hasEslint: false,
|
|
1245
1240
|
hasPrettier: false
|
|
1246
1241
|
};
|
|
1247
|
-
const pkgPath =
|
|
1242
|
+
const pkgPath = path6__namespace.join(cwd, "package.json");
|
|
1248
1243
|
try {
|
|
1249
|
-
const pkgContent = await
|
|
1244
|
+
const pkgContent = await fs6__namespace.readFile(pkgPath, "utf-8");
|
|
1250
1245
|
const pkg = JSON.parse(pkgContent);
|
|
1251
1246
|
result.name = pkg.name || result.name;
|
|
1252
1247
|
result.dependencies = pkg.dependencies || {};
|
|
@@ -1329,7 +1324,7 @@ async function analyzeDirectoryStructure(cwd) {
|
|
|
1329
1324
|
others: []
|
|
1330
1325
|
};
|
|
1331
1326
|
try {
|
|
1332
|
-
const entries = await
|
|
1327
|
+
const entries = await fs6__namespace.readdir(cwd, { withFileTypes: true });
|
|
1333
1328
|
for (const entry of entries) {
|
|
1334
1329
|
if (!entry.isDirectory()) continue;
|
|
1335
1330
|
const name = entry.name;
|
|
@@ -1365,7 +1360,7 @@ async function findEntryPoints(cwd) {
|
|
|
1365
1360
|
"index.js"
|
|
1366
1361
|
];
|
|
1367
1362
|
for (const entry of possibleEntries) {
|
|
1368
|
-
const fullPath =
|
|
1363
|
+
const fullPath = path6__namespace.join(cwd, entry);
|
|
1369
1364
|
if (await fileExists(fullPath)) {
|
|
1370
1365
|
entryPoints.push(entry);
|
|
1371
1366
|
}
|
|
@@ -1373,15 +1368,15 @@ async function findEntryPoints(cwd) {
|
|
|
1373
1368
|
return entryPoints;
|
|
1374
1369
|
}
|
|
1375
1370
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
1376
|
-
const analysisPath =
|
|
1377
|
-
await
|
|
1371
|
+
const analysisPath = path6__namespace.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
1372
|
+
await fs6__namespace.writeFile(
|
|
1378
1373
|
analysisPath,
|
|
1379
1374
|
JSON.stringify(analysis, null, 2),
|
|
1380
1375
|
"utf-8"
|
|
1381
1376
|
);
|
|
1382
1377
|
}
|
|
1383
1378
|
async function generateOpenSpecConfig(openspecDir, analysis) {
|
|
1384
|
-
const configPath =
|
|
1379
|
+
const configPath = path6__namespace.join(openspecDir, "config.yaml");
|
|
1385
1380
|
const content = `# OpenSpec \u9879\u76EE\u914D\u7F6E
|
|
1386
1381
|
# \u6B64\u6587\u4EF6\u5B9A\u4E49\u9879\u76EE\u7684\u57FA\u672C\u4FE1\u606F\uFF0C\u7528\u4E8E\u6307\u5BFC AI \u7406\u89E3\u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1387
1382
|
|
|
@@ -1415,7 +1410,7 @@ architecture:
|
|
|
1415
1410
|
# \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
|
|
1416
1411
|
standards: []
|
|
1417
1412
|
`;
|
|
1418
|
-
await
|
|
1413
|
+
await fs6__namespace.writeFile(configPath, content, "utf-8");
|
|
1419
1414
|
}
|
|
1420
1415
|
function generateAgentsMd(info, scanResult) {
|
|
1421
1416
|
const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
|
|
@@ -1537,7 +1532,7 @@ function generateHealthTemplate() {
|
|
|
1537
1532
|
}
|
|
1538
1533
|
async function fileExists(filePath) {
|
|
1539
1534
|
try {
|
|
1540
|
-
await
|
|
1535
|
+
await fs6__namespace.access(filePath);
|
|
1541
1536
|
return true;
|
|
1542
1537
|
} catch {
|
|
1543
1538
|
return false;
|
|
@@ -1708,21 +1703,21 @@ var IV_LENGTH = 16;
|
|
|
1708
1703
|
var KEY_DIR = ".sf-cli";
|
|
1709
1704
|
var KEY_FILE = ".key";
|
|
1710
1705
|
function getOrCreateEncryptionKey() {
|
|
1711
|
-
const keyPath =
|
|
1706
|
+
const keyPath = path6__namespace.join(os__namespace.homedir(), KEY_DIR, KEY_FILE);
|
|
1712
1707
|
try {
|
|
1713
|
-
if (
|
|
1714
|
-
const keyBase64 =
|
|
1708
|
+
if (fs9__namespace.existsSync(keyPath)) {
|
|
1709
|
+
const keyBase64 = fs9__namespace.readFileSync(keyPath, "utf-8").trim();
|
|
1715
1710
|
return Buffer.from(keyBase64, "base64");
|
|
1716
1711
|
}
|
|
1717
1712
|
} catch {
|
|
1718
1713
|
}
|
|
1719
1714
|
const key = crypto__namespace.randomBytes(32);
|
|
1720
1715
|
try {
|
|
1721
|
-
const keyDir =
|
|
1722
|
-
if (!
|
|
1723
|
-
|
|
1716
|
+
const keyDir = path6__namespace.dirname(keyPath);
|
|
1717
|
+
if (!fs9__namespace.existsSync(keyDir)) {
|
|
1718
|
+
fs9__namespace.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
1724
1719
|
}
|
|
1725
|
-
|
|
1720
|
+
fs9__namespace.writeFileSync(keyPath, key.toString("base64"), {
|
|
1726
1721
|
mode: 384,
|
|
1727
1722
|
// 仅所有者可读写
|
|
1728
1723
|
encoding: "utf-8"
|
|
@@ -1749,9 +1744,9 @@ var ConfigManager = class {
|
|
|
1749
1744
|
}
|
|
1750
1745
|
async load(projectPath) {
|
|
1751
1746
|
this.projectPath = projectPath;
|
|
1752
|
-
this.configPath =
|
|
1747
|
+
this.configPath = path6__namespace.join(projectPath, ".sf-cli", "config.json");
|
|
1753
1748
|
try {
|
|
1754
|
-
const content = await
|
|
1749
|
+
const content = await fs6__namespace.readFile(this.configPath, "utf-8");
|
|
1755
1750
|
const loaded = JSON.parse(content);
|
|
1756
1751
|
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
1757
1752
|
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
@@ -1770,8 +1765,8 @@ var ConfigManager = class {
|
|
|
1770
1765
|
configToSave.apiKey = encrypted;
|
|
1771
1766
|
configToSave.apiKeyEncrypted = true;
|
|
1772
1767
|
}
|
|
1773
|
-
await
|
|
1774
|
-
await
|
|
1768
|
+
await fs6__namespace.mkdir(path6__namespace.dirname(this.configPath), { recursive: true });
|
|
1769
|
+
await fs6__namespace.writeFile(
|
|
1775
1770
|
this.configPath,
|
|
1776
1771
|
JSON.stringify(configToSave, null, 2),
|
|
1777
1772
|
"utf-8"
|
|
@@ -2029,7 +2024,7 @@ var BaseAdapter = class {
|
|
|
2029
2024
|
* 延迟工具函数
|
|
2030
2025
|
*/
|
|
2031
2026
|
delay(ms) {
|
|
2032
|
-
return new Promise((
|
|
2027
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
2033
2028
|
}
|
|
2034
2029
|
};
|
|
2035
2030
|
|
|
@@ -2650,7 +2645,7 @@ var ModelService = class {
|
|
|
2650
2645
|
* 初始化服务
|
|
2651
2646
|
*/
|
|
2652
2647
|
async initialize(statsDir) {
|
|
2653
|
-
this.statsPath =
|
|
2648
|
+
this.statsPath = path6__namespace.join(statsDir, "tokens");
|
|
2654
2649
|
await this.loadStats();
|
|
2655
2650
|
const model = this.configManager.get("model");
|
|
2656
2651
|
const apiKey = this.configManager.get("apiKey");
|
|
@@ -2823,11 +2818,11 @@ var ModelService = class {
|
|
|
2823
2818
|
async loadStats() {
|
|
2824
2819
|
if (!this.statsPath) return;
|
|
2825
2820
|
try {
|
|
2826
|
-
const dailyPath =
|
|
2827
|
-
const totalPath =
|
|
2821
|
+
const dailyPath = path6__namespace.join(this.statsPath, "daily.json");
|
|
2822
|
+
const totalPath = path6__namespace.join(this.statsPath, "total.json");
|
|
2828
2823
|
const [daily, total] = await Promise.all([
|
|
2829
|
-
|
|
2830
|
-
|
|
2824
|
+
fs6__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
2825
|
+
fs6__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
2831
2826
|
]);
|
|
2832
2827
|
const dailyData = JSON.parse(daily);
|
|
2833
2828
|
const totalData = JSON.parse(total);
|
|
@@ -2840,12 +2835,12 @@ var ModelService = class {
|
|
|
2840
2835
|
async saveStats() {
|
|
2841
2836
|
if (!this.statsPath) return;
|
|
2842
2837
|
try {
|
|
2843
|
-
await
|
|
2844
|
-
const dailyPath =
|
|
2845
|
-
const totalPath =
|
|
2838
|
+
await fs6__namespace.mkdir(this.statsPath, { recursive: true });
|
|
2839
|
+
const dailyPath = path6__namespace.join(this.statsPath, "daily.json");
|
|
2840
|
+
const totalPath = path6__namespace.join(this.statsPath, "total.json");
|
|
2846
2841
|
await Promise.all([
|
|
2847
|
-
|
|
2848
|
-
|
|
2842
|
+
fs6__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
2843
|
+
fs6__namespace.writeFile(totalPath, JSON.stringify({
|
|
2849
2844
|
totalInput: this.stats.totalInput,
|
|
2850
2845
|
totalOutput: this.stats.totalOutput
|
|
2851
2846
|
}))
|
|
@@ -3086,8 +3081,26 @@ function createSpinner(message) {
|
|
|
3086
3081
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
3087
3082
|
};
|
|
3088
3083
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
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;
|
|
3091
3104
|
async function handleUpdate(args, ctx) {
|
|
3092
3105
|
const options = {
|
|
3093
3106
|
check: args.includes("--check") || args.includes("-c"),
|
|
@@ -3114,6 +3127,9 @@ async function checkForUpdates() {
|
|
|
3114
3127
|
const latestVersion = await getLatestVersion();
|
|
3115
3128
|
if (!latestVersion) {
|
|
3116
3129
|
lines.push(chalk9__default.default.yellow("\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
3130
|
+
lines.push(chalk9__default.default.gray("\u53EF\u80FD\u539F\u56E0:"));
|
|
3131
|
+
lines.push(chalk9__default.default.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
|
|
3132
|
+
lines.push(chalk9__default.default.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
|
|
3117
3133
|
return { output: lines.join("\n") };
|
|
3118
3134
|
}
|
|
3119
3135
|
lines.push(chalk9__default.default.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
|
|
@@ -3132,43 +3148,70 @@ async function checkForUpdates() {
|
|
|
3132
3148
|
async function performUpdate(targetVersion) {
|
|
3133
3149
|
const lines = [];
|
|
3134
3150
|
lines.push(chalk9__default.default.cyan("\u6B63\u5728\u66F4\u65B0 sf-cli..."));
|
|
3151
|
+
lines.push(chalk9__default.default.gray(` \u5305\u540D: ${PACKAGE_NAME}`));
|
|
3152
|
+
lines.push(chalk9__default.default.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
|
|
3135
3153
|
try {
|
|
3136
3154
|
const latestVersion = await getLatestVersion();
|
|
3155
|
+
if (!latestVersion && !targetVersion) {
|
|
3156
|
+
lines.push(chalk9__default.default.yellow("\n\u26A0 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
3157
|
+
lines.push(chalk9__default.default.gray("\n\u53EF\u80FD\u539F\u56E0:"));
|
|
3158
|
+
lines.push(chalk9__default.default.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
|
|
3159
|
+
lines.push(chalk9__default.default.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
|
|
3160
|
+
lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
3161
|
+
lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
3162
|
+
return { output: lines.join("\n") };
|
|
3163
|
+
}
|
|
3137
3164
|
if (latestVersion === CURRENT_VERSION && !targetVersion) {
|
|
3138
|
-
lines.push(chalk9__default.default.green("\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
|
|
3165
|
+
lines.push(chalk9__default.default.green("\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
|
|
3139
3166
|
return { output: lines.join("\n") };
|
|
3140
3167
|
}
|
|
3141
3168
|
const packageSpec = targetVersion ? `${PACKAGE_NAME}@${targetVersion}` : `${PACKAGE_NAME}@latest`;
|
|
3142
3169
|
lines.push(chalk9__default.default.gray(` \u5B89\u88C5: ${packageSpec}`));
|
|
3143
|
-
await new Promise((
|
|
3170
|
+
const installResult = await new Promise((resolve5) => {
|
|
3144
3171
|
const proc = child_process.spawn("npm", ["install", "-g", packageSpec], {
|
|
3145
3172
|
stdio: "pipe",
|
|
3146
3173
|
shell: true
|
|
3147
3174
|
});
|
|
3175
|
+
let output = "";
|
|
3176
|
+
proc.stdout?.on("data", (data) => {
|
|
3177
|
+
output += data.toString();
|
|
3178
|
+
});
|
|
3179
|
+
proc.stderr?.on("data", (data) => {
|
|
3180
|
+
output += data.toString();
|
|
3181
|
+
});
|
|
3148
3182
|
proc.on("close", (code) => {
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
}
|
|
3183
|
+
resolve5({
|
|
3184
|
+
success: code === 0,
|
|
3185
|
+
output
|
|
3186
|
+
});
|
|
3154
3187
|
});
|
|
3155
3188
|
proc.on("error", (err) => {
|
|
3156
|
-
|
|
3189
|
+
resolve5({
|
|
3190
|
+
success: false,
|
|
3191
|
+
output: err.message
|
|
3192
|
+
});
|
|
3157
3193
|
});
|
|
3158
3194
|
});
|
|
3159
|
-
|
|
3195
|
+
if (!installResult.success) {
|
|
3196
|
+
lines.push(chalk9__default.default.red("\n\u2717 \u66F4\u65B0\u5931\u8D25"));
|
|
3197
|
+
lines.push(chalk9__default.default.gray(installResult.output));
|
|
3198
|
+
lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
3199
|
+
lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
3200
|
+
return { output: lines.join("\n") };
|
|
3201
|
+
}
|
|
3202
|
+
lines.push(chalk9__default.default.green("\n\u2713 \u66F4\u65B0\u5B8C\u6210!"));
|
|
3160
3203
|
lines.push(chalk9__default.default.gray(` \u65B0\u7248\u672C: v${targetVersion || latestVersion}`));
|
|
3161
3204
|
lines.push(chalk9__default.default.gray("\n\u8BF7\u91CD\u542F CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"));
|
|
3162
3205
|
} catch (error) {
|
|
3163
3206
|
lines.push(chalk9__default.default.red("\u66F4\u65B0\u5931\u8D25: " + error.message));
|
|
3164
3207
|
lines.push(chalk9__default.default.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
3165
|
-
lines.push(chalk9__default.default.
|
|
3208
|
+
lines.push(chalk9__default.default.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
3166
3209
|
}
|
|
3167
3210
|
return { output: lines.join("\n") };
|
|
3168
3211
|
}
|
|
3169
3212
|
async function getLatestVersion() {
|
|
3170
3213
|
try {
|
|
3171
|
-
const result = await new Promise((
|
|
3214
|
+
const result = await new Promise((resolve5, reject) => {
|
|
3172
3215
|
const proc = child_process.spawn("npm", ["view", PACKAGE_NAME, "version"], {
|
|
3173
3216
|
stdio: "pipe",
|
|
3174
3217
|
shell: true
|
|
@@ -3178,13 +3221,17 @@ async function getLatestVersion() {
|
|
|
3178
3221
|
output += data.toString();
|
|
3179
3222
|
});
|
|
3180
3223
|
proc.on("close", (code) => {
|
|
3181
|
-
if (code === 0) {
|
|
3182
|
-
|
|
3224
|
+
if (code === 0 && output.trim()) {
|
|
3225
|
+
resolve5(output.trim());
|
|
3183
3226
|
} else {
|
|
3184
3227
|
reject(new Error("Failed to get version"));
|
|
3185
3228
|
}
|
|
3186
3229
|
});
|
|
3187
3230
|
proc.on("error", reject);
|
|
3231
|
+
setTimeout(() => {
|
|
3232
|
+
proc.kill();
|
|
3233
|
+
reject(new Error("Timeout"));
|
|
3234
|
+
}, 1e4);
|
|
3188
3235
|
});
|
|
3189
3236
|
return result || null;
|
|
3190
3237
|
} catch {
|
|
@@ -3468,7 +3515,7 @@ var WorkflowEngine = class {
|
|
|
3468
3515
|
*/
|
|
3469
3516
|
async initialize(projectPath) {
|
|
3470
3517
|
this.projectPath = projectPath;
|
|
3471
|
-
this.openspecPath =
|
|
3518
|
+
this.openspecPath = path6__namespace.join(projectPath, "openspec");
|
|
3472
3519
|
await this.ensureDirectories();
|
|
3473
3520
|
await this.loadProjectContext();
|
|
3474
3521
|
await this.restoreState();
|
|
@@ -3478,21 +3525,21 @@ var WorkflowEngine = class {
|
|
|
3478
3525
|
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
3479
3526
|
*/
|
|
3480
3527
|
async loadProjectContext() {
|
|
3481
|
-
const agentsMdPath =
|
|
3528
|
+
const agentsMdPath = path6__namespace.join(this.projectPath, "AGENTS.md");
|
|
3482
3529
|
try {
|
|
3483
|
-
this.projectContext = await
|
|
3530
|
+
this.projectContext = await fs6__namespace.readFile(agentsMdPath, "utf-8");
|
|
3484
3531
|
} catch {
|
|
3485
3532
|
this.projectContext = "";
|
|
3486
3533
|
}
|
|
3487
|
-
const configPath =
|
|
3534
|
+
const configPath = path6__namespace.join(this.openspecPath, "config.yaml");
|
|
3488
3535
|
try {
|
|
3489
|
-
this.projectConfig = await
|
|
3536
|
+
this.projectConfig = await fs6__namespace.readFile(configPath, "utf-8");
|
|
3490
3537
|
} catch {
|
|
3491
3538
|
this.projectConfig = "";
|
|
3492
3539
|
}
|
|
3493
|
-
const devstandedPath =
|
|
3540
|
+
const devstandedPath = path6__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
3494
3541
|
try {
|
|
3495
|
-
this.devStandards = await
|
|
3542
|
+
this.devStandards = await fs6__namespace.readFile(devstandedPath, "utf-8");
|
|
3496
3543
|
} catch {
|
|
3497
3544
|
this.devStandards = "";
|
|
3498
3545
|
}
|
|
@@ -3778,12 +3825,12 @@ var WorkflowEngine = class {
|
|
|
3778
3825
|
await this.createSpecDocument(summary);
|
|
3779
3826
|
await this.updateChangeRecord("archived");
|
|
3780
3827
|
await this.saveState();
|
|
3781
|
-
const changesDir =
|
|
3782
|
-
const archiveDir =
|
|
3783
|
-
const changeFile =
|
|
3784
|
-
const archiveFile =
|
|
3785
|
-
await
|
|
3786
|
-
await
|
|
3828
|
+
const changesDir = path6__namespace.join(this.openspecPath, "changes");
|
|
3829
|
+
const archiveDir = path6__namespace.join(changesDir, "archive");
|
|
3830
|
+
const changeFile = path6__namespace.join(changesDir, `${changeId}.md`);
|
|
3831
|
+
const archiveFile = path6__namespace.join(archiveDir, `${changeId}.md`);
|
|
3832
|
+
await fs6__namespace.mkdir(archiveDir, { recursive: true });
|
|
3833
|
+
await fs6__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
3787
3834
|
});
|
|
3788
3835
|
this.state = null;
|
|
3789
3836
|
this.snapshots.clear();
|
|
@@ -3805,16 +3852,16 @@ var WorkflowEngine = class {
|
|
|
3805
3852
|
}
|
|
3806
3853
|
// ==================== 私有方法 ====================
|
|
3807
3854
|
async ensureDirectories() {
|
|
3808
|
-
const changesDir =
|
|
3809
|
-
const archiveDir =
|
|
3810
|
-
const specDir =
|
|
3811
|
-
await
|
|
3812
|
-
await
|
|
3855
|
+
const changesDir = path6__namespace.join(this.openspecPath, "changes");
|
|
3856
|
+
const archiveDir = path6__namespace.join(changesDir, "archive");
|
|
3857
|
+
const specDir = path6__namespace.join(this.openspecPath, "spec");
|
|
3858
|
+
await fs6__namespace.mkdir(archiveDir, { recursive: true });
|
|
3859
|
+
await fs6__namespace.mkdir(specDir, { recursive: true });
|
|
3813
3860
|
}
|
|
3814
3861
|
async restoreState() {
|
|
3815
|
-
const statePath =
|
|
3862
|
+
const statePath = path6__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
3816
3863
|
try {
|
|
3817
|
-
const content = await
|
|
3864
|
+
const content = await fs6__namespace.readFile(statePath, "utf-8");
|
|
3818
3865
|
this.state = JSON.parse(content, (key, value) => {
|
|
3819
3866
|
if (key.endsWith("At") && typeof value === "string") {
|
|
3820
3867
|
return new Date(value);
|
|
@@ -3825,20 +3872,20 @@ var WorkflowEngine = class {
|
|
|
3825
3872
|
const err = e;
|
|
3826
3873
|
if (err.code !== "ENOENT") {
|
|
3827
3874
|
console.warn("\u8B66\u544A: \u5DE5\u4F5C\u6D41\u72B6\u6001\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u5C06\u91CD\u65B0\u5F00\u59CB");
|
|
3828
|
-
await
|
|
3875
|
+
await fs6__namespace.unlink(statePath).catch(() => {
|
|
3829
3876
|
});
|
|
3830
3877
|
}
|
|
3831
3878
|
this.state = null;
|
|
3832
3879
|
}
|
|
3833
3880
|
}
|
|
3834
3881
|
async saveState() {
|
|
3835
|
-
const statePath =
|
|
3836
|
-
await
|
|
3882
|
+
const statePath = path6__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
3883
|
+
await fs6__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
3837
3884
|
}
|
|
3838
3885
|
async restoreSnapshots() {
|
|
3839
|
-
const snapshotsPath =
|
|
3886
|
+
const snapshotsPath = path6__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
3840
3887
|
try {
|
|
3841
|
-
const content = await
|
|
3888
|
+
const content = await fs6__namespace.readFile(snapshotsPath, "utf-8");
|
|
3842
3889
|
const data = JSON.parse(content, (key, value) => {
|
|
3843
3890
|
if (key === "timestamp" && typeof value === "string") {
|
|
3844
3891
|
return new Date(value);
|
|
@@ -3853,9 +3900,9 @@ var WorkflowEngine = class {
|
|
|
3853
3900
|
}
|
|
3854
3901
|
}
|
|
3855
3902
|
async saveSnapshots() {
|
|
3856
|
-
const snapshotsPath =
|
|
3903
|
+
const snapshotsPath = path6__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
3857
3904
|
const data = Array.from(this.snapshots.values());
|
|
3858
|
-
await
|
|
3905
|
+
await fs6__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
3859
3906
|
}
|
|
3860
3907
|
async createSnapshot() {
|
|
3861
3908
|
if (!this.state) return;
|
|
@@ -3875,16 +3922,16 @@ var WorkflowEngine = class {
|
|
|
3875
3922
|
}
|
|
3876
3923
|
async createChangeRecord() {
|
|
3877
3924
|
if (!this.state) return;
|
|
3878
|
-
const changePath =
|
|
3879
|
-
await
|
|
3925
|
+
const changePath = path6__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
3926
|
+
await fs6__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
3880
3927
|
}
|
|
3881
3928
|
async updateChangeRecord(status) {
|
|
3882
3929
|
if (!this.state) return;
|
|
3883
3930
|
if (status) {
|
|
3884
3931
|
this.state.status = status;
|
|
3885
3932
|
}
|
|
3886
|
-
const changePath =
|
|
3887
|
-
await
|
|
3933
|
+
const changePath = path6__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
3934
|
+
await fs6__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
3888
3935
|
}
|
|
3889
3936
|
formatChangeRecord() {
|
|
3890
3937
|
if (!this.state) return "";
|
|
@@ -3923,7 +3970,7 @@ ${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
|
3923
3970
|
}
|
|
3924
3971
|
async createSpecDocument(summary) {
|
|
3925
3972
|
if (!this.state) return;
|
|
3926
|
-
const specPath =
|
|
3973
|
+
const specPath = path6__namespace.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
3927
3974
|
const content = `# Spec: ${this.state.title}
|
|
3928
3975
|
|
|
3929
3976
|
> \u53D8\u66F4ID: ${this.state.id}
|
|
@@ -3948,7 +3995,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
|
|
|
3948
3995
|
|
|
3949
3996
|
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
3950
3997
|
`;
|
|
3951
|
-
await
|
|
3998
|
+
await fs6__namespace.writeFile(specPath, content);
|
|
3952
3999
|
}
|
|
3953
4000
|
};
|
|
3954
4001
|
var ConfirmationRequiredError = class extends Error {
|
|
@@ -3993,7 +4040,7 @@ async function newFeature(options, workingDir, workflowEngine) {
|
|
|
3993
4040
|
const cwd = workingDir || process.cwd();
|
|
3994
4041
|
const { requirement, forceComplexity } = options;
|
|
3995
4042
|
try {
|
|
3996
|
-
const stats = await
|
|
4043
|
+
const stats = await fs6__namespace.stat(cwd);
|
|
3997
4044
|
if (!stats.isDirectory()) {
|
|
3998
4045
|
return {
|
|
3999
4046
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -4064,20 +4111,20 @@ function parseArgs(args) {
|
|
|
4064
4111
|
}
|
|
4065
4112
|
async function readProjectContext(cwd) {
|
|
4066
4113
|
const defaultContext = {
|
|
4067
|
-
name:
|
|
4114
|
+
name: path6__namespace.basename(cwd),
|
|
4068
4115
|
type: "unknown",
|
|
4069
4116
|
framework: null,
|
|
4070
4117
|
techStack: [],
|
|
4071
4118
|
description: ""
|
|
4072
4119
|
};
|
|
4073
|
-
const agentsPath =
|
|
4120
|
+
const agentsPath = path6__namespace.join(cwd, "AGENTS.md");
|
|
4074
4121
|
try {
|
|
4075
|
-
const stats = await
|
|
4122
|
+
const stats = await fs6__namespace.stat(agentsPath);
|
|
4076
4123
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
4077
4124
|
console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
4078
4125
|
return defaultContext;
|
|
4079
4126
|
}
|
|
4080
|
-
const content = await
|
|
4127
|
+
const content = await fs6__namespace.readFile(agentsPath, "utf-8");
|
|
4081
4128
|
return parseAgentsMd(content, defaultContext);
|
|
4082
4129
|
} catch (e) {
|
|
4083
4130
|
const err = e;
|
|
@@ -4085,14 +4132,14 @@ async function readProjectContext(cwd) {
|
|
|
4085
4132
|
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
4086
4133
|
}
|
|
4087
4134
|
}
|
|
4088
|
-
const configPath =
|
|
4135
|
+
const configPath = path6__namespace.join(cwd, "openspec", "config.yaml");
|
|
4089
4136
|
try {
|
|
4090
|
-
const stats = await
|
|
4137
|
+
const stats = await fs6__namespace.stat(configPath);
|
|
4091
4138
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
4092
4139
|
console.warn(`\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
|
|
4093
4140
|
return defaultContext;
|
|
4094
4141
|
}
|
|
4095
|
-
const content = await
|
|
4142
|
+
const content = await fs6__namespace.readFile(configPath, "utf-8");
|
|
4096
4143
|
return parseConfigYaml(content, defaultContext);
|
|
4097
4144
|
} catch (e) {
|
|
4098
4145
|
const err = e;
|
|
@@ -4312,10 +4359,10 @@ var FRONTEND_DEV_AGENT = {
|
|
|
4312
4359
|
var CODE_REVIEWER_AGENT = {
|
|
4313
4360
|
id: "code-reviewer",
|
|
4314
4361
|
name: "\u4EE3\u7801\u5BA1\u6838",
|
|
4315
|
-
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",
|
|
4316
4363
|
icon: "\u{1F50D}",
|
|
4317
|
-
version: "1.
|
|
4318
|
-
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",
|
|
4319
4366
|
capabilities: [
|
|
4320
4367
|
{
|
|
4321
4368
|
id: "quality-review",
|
|
@@ -4336,13 +4383,24 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4336
4383
|
id: "performance-review",
|
|
4337
4384
|
name: "\u6027\u80FD\u5BA1\u67E5",
|
|
4338
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"
|
|
4339
4396
|
}
|
|
4340
4397
|
],
|
|
4341
4398
|
tools: [
|
|
4342
4399
|
{ name: "read_file", description: "\u8BFB\u53D6\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
4343
4400
|
{ name: "glob", description: "\u641C\u7D22\u6587\u4EF6", permission: "full" },
|
|
4344
4401
|
{ name: "search_file_content", description: "\u641C\u7D22\u6587\u4EF6\u5185\u5BB9", permission: "full" },
|
|
4345
|
-
{ 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" }
|
|
4346
4404
|
],
|
|
4347
4405
|
triggers: [
|
|
4348
4406
|
{ type: "workflow", condition: { workflowStep: "apply" }, priority: 10 },
|
|
@@ -4353,6 +4411,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4353
4411
|
protectedPaths: ["node_modules", ".git"]
|
|
4354
4412
|
},
|
|
4355
4413
|
behavior: {
|
|
4414
|
+
requireConfirmation: ["run_shell_command"],
|
|
4356
4415
|
autoCommit: false
|
|
4357
4416
|
}
|
|
4358
4417
|
},
|
|
@@ -4361,6 +4420,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4361
4420
|
## \u4F60\u7684\u804C\u8D23
|
|
4362
4421
|
- \u5BA1\u67E5\u4EE3\u7801\u8D28\u91CF\u548C\u53EF\u7EF4\u62A4\u6027
|
|
4363
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
|
|
4364
4424
|
- \u63D0\u4F9B\u5177\u4F53\u7684\u6539\u8FDB\u5EFA\u8BAE
|
|
4365
4425
|
- \u786E\u4FDD\u9075\u5FAA\u6700\u4F73\u5B9E\u8DF5
|
|
4366
4426
|
|
|
@@ -4369,8 +4429,20 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4369
4429
|
2. **\u5B89\u5168\u6027**: XSS\u3001\u6CE8\u5165\u3001\u654F\u611F\u6570\u636E\u5904\u7406
|
|
4370
4430
|
3. **\u6027\u80FD**: \u7B97\u6CD5\u6548\u7387\u3001\u5185\u5B58\u4F7F\u7528\u3001\u6E32\u67D3\u4F18\u5316
|
|
4371
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
|
|
4372
4439
|
|
|
4373
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
|
+
|
|
4374
4446
|
### \u5BA1\u67E5\u7ED3\u679C
|
|
4375
4447
|
- \u901A\u8FC7/\u9700\u4FEE\u6539/\u4E0D\u901A\u8FC7
|
|
4376
4448
|
|
|
@@ -4396,7 +4468,7 @@ var CODE_REVIEWER_AGENT = {
|
|
|
4396
4468
|
## \u4E0A\u4E0B\u6587
|
|
4397
4469
|
{{context}}
|
|
4398
4470
|
|
|
4399
|
-
\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`,
|
|
4400
4472
|
outputFormat: {
|
|
4401
4473
|
type: "markdown"
|
|
4402
4474
|
}
|
|
@@ -4652,15 +4724,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
4652
4724
|
devStandards: ""
|
|
4653
4725
|
};
|
|
4654
4726
|
try {
|
|
4655
|
-
context.agentsMd = await
|
|
4727
|
+
context.agentsMd = await fs6__namespace.readFile(path6__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
4656
4728
|
} catch {
|
|
4657
4729
|
}
|
|
4658
4730
|
try {
|
|
4659
|
-
context.configYaml = await
|
|
4731
|
+
context.configYaml = await fs6__namespace.readFile(path6__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
4660
4732
|
} catch {
|
|
4661
4733
|
}
|
|
4662
4734
|
try {
|
|
4663
|
-
context.devStandards = await
|
|
4735
|
+
context.devStandards = await fs6__namespace.readFile(path6__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
4664
4736
|
} catch {
|
|
4665
4737
|
}
|
|
4666
4738
|
return context;
|
|
@@ -4822,6 +4894,78 @@ ${content}`;
|
|
|
4822
4894
|
|
|
4823
4895
|
// src/commands/opsx.ts
|
|
4824
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
|
+
}
|
|
4825
4969
|
async function handleOpsx(command, args, ctx) {
|
|
4826
4970
|
const step = command.replace("opsx:", "");
|
|
4827
4971
|
const workflow = new WorkflowEngine();
|
|
@@ -4836,7 +4980,7 @@ async function handleOpsx(command, args, ctx) {
|
|
|
4836
4980
|
case "apply":
|
|
4837
4981
|
return handleApply(workflow);
|
|
4838
4982
|
case "archive":
|
|
4839
|
-
return handleArchive(workflow, args);
|
|
4983
|
+
return handleArchive(workflow, args, ctx);
|
|
4840
4984
|
case "propose":
|
|
4841
4985
|
return handlePropose(workflow, args);
|
|
4842
4986
|
case "status":
|
|
@@ -4851,12 +4995,50 @@ async function handleOpsx(command, args, ctx) {
|
|
|
4851
4995
|
return handleNext(workflow);
|
|
4852
4996
|
case "auto":
|
|
4853
4997
|
return handleAutoSchedule(args);
|
|
4998
|
+
case "test":
|
|
4999
|
+
return handleRegressionTest(ctx);
|
|
4854
5000
|
default:
|
|
4855
5001
|
return {
|
|
4856
5002
|
output: chalk9__default.default.red(`\u672A\u77E5\u7684OpenSpec\u547D\u4EE4: /${command}`)
|
|
4857
5003
|
};
|
|
4858
5004
|
}
|
|
4859
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
|
+
}
|
|
4860
5042
|
function handleAutoSchedule(args) {
|
|
4861
5043
|
const action = args[0]?.toLowerCase();
|
|
4862
5044
|
if (!action) {
|
|
@@ -4986,13 +5168,70 @@ async function handleArchive(workflow, args, ctx) {
|
|
|
4986
5168
|
${generateConfirmationPrompt(confirmation.point)}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /opsx:confirm code-review \u786E\u8BA4\u540E\u5F52\u6863")
|
|
4987
5169
|
};
|
|
4988
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("");
|
|
4989
5188
|
const changeId = state.id;
|
|
4990
5189
|
const summary = args.join(" ") || "\u5B8C\u6210\u53D8\u66F4";
|
|
4991
5190
|
await workflow.archive(summary);
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
\u53D8\u66F4ID: ${changeId}`) + chalk9__default.default.cyan("\n\n\u5F52\u6863\u6587\u6863\u5DF2\u751F\u6210\u5230 openspec/spec/ \u76EE\u5F55")
|
|
4995
|
-
};
|
|
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
|
+
}
|
|
4996
5235
|
}
|
|
4997
5236
|
async function handlePropose(workflow, args, ctx) {
|
|
4998
5237
|
const state = workflow.getState();
|
|
@@ -5181,9 +5420,27 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
|
|
|
5181
5420
|
throw e;
|
|
5182
5421
|
}
|
|
5183
5422
|
}
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5423
|
+
|
|
5424
|
+
// src/commands/runner.ts
|
|
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();
|
|
5187
5444
|
async function runSlashCommand(command, args, ctx) {
|
|
5188
5445
|
const normalizedCommand = normalizeCommand(command);
|
|
5189
5446
|
switch (normalizedCommand) {
|
|
@@ -5230,11 +5487,11 @@ function normalizeCommand(command) {
|
|
|
5230
5487
|
}
|
|
5231
5488
|
async function handleFileReference(filePath, ctx) {
|
|
5232
5489
|
const cwd = ctx.options.workingDirectory;
|
|
5233
|
-
const absolutePath =
|
|
5490
|
+
const absolutePath = path6__namespace.isAbsolute(filePath) ? filePath : path6__namespace.join(cwd, filePath);
|
|
5234
5491
|
try {
|
|
5235
|
-
const stats = await
|
|
5492
|
+
const stats = await fs6__namespace.stat(absolutePath);
|
|
5236
5493
|
if (stats.isDirectory()) {
|
|
5237
|
-
const files = await
|
|
5494
|
+
const files = await fs6__namespace.readdir(absolutePath);
|
|
5238
5495
|
return {
|
|
5239
5496
|
output: chalk9__default.default.cyan(`\u{1F4C1} ${filePath}/`) + chalk9__default.default.gray(`
|
|
5240
5497
|
${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.default.gray(`
|
|
@@ -5242,7 +5499,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.defaul
|
|
|
5242
5499
|
contextUsed: 0
|
|
5243
5500
|
};
|
|
5244
5501
|
}
|
|
5245
|
-
const content = await
|
|
5502
|
+
const content = await fs6__namespace.readFile(absolutePath, "utf-8");
|
|
5246
5503
|
const lines = content.split("\n");
|
|
5247
5504
|
ctx.contextManager.addMessage({
|
|
5248
5505
|
role: "user",
|
|
@@ -5278,7 +5535,7 @@ async function executeShell(command, ctx) {
|
|
|
5278
5535
|
\u547D\u4EE4 "${command}" \u53EF\u80FD\u4F1A\u5220\u9664\u91CD\u8981\u6587\u4EF6`) + chalk9__default.default.gray("\n\u4F7F\u7528 yolo \u6A21\u5F0F\u5F3A\u5236\u6267\u884C")
|
|
5279
5536
|
};
|
|
5280
5537
|
}
|
|
5281
|
-
return new Promise((
|
|
5538
|
+
return new Promise((resolve5) => {
|
|
5282
5539
|
const shell = child_process.spawn(command, [], {
|
|
5283
5540
|
shell: true,
|
|
5284
5541
|
cwd: ctx.options.workingDirectory
|
|
@@ -5303,11 +5560,11 @@ async function executeShell(command, ctx) {
|
|
|
5303
5560
|
output += chalk9__default.default.red(`
|
|
5304
5561
|
\u9000\u51FA\u7801: ${code}`);
|
|
5305
5562
|
}
|
|
5306
|
-
|
|
5563
|
+
resolve5({ output: output || chalk9__default.default.gray("(\u65E0\u8F93\u51FA)") });
|
|
5307
5564
|
});
|
|
5308
5565
|
setTimeout(() => {
|
|
5309
5566
|
shell.kill();
|
|
5310
|
-
|
|
5567
|
+
resolve5({
|
|
5311
5568
|
output: chalk9__default.default.yellow("\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0C\u5DF2\u7EC8\u6B62")
|
|
5312
5569
|
});
|
|
5313
5570
|
}, 6e4);
|
|
@@ -5533,7 +5790,7 @@ var ContextManager = class {
|
|
|
5533
5790
|
}
|
|
5534
5791
|
async initialize(projectPath) {
|
|
5535
5792
|
this.projectPath = projectPath;
|
|
5536
|
-
this.persistPath =
|
|
5793
|
+
this.persistPath = path6__namespace.join(projectPath, ".sf-cli", "cache", "context", "context.json");
|
|
5537
5794
|
await this.loadPersistedContext();
|
|
5538
5795
|
}
|
|
5539
5796
|
/**
|
|
@@ -5774,15 +6031,15 @@ ${summary}`,
|
|
|
5774
6031
|
*/
|
|
5775
6032
|
async persist() {
|
|
5776
6033
|
if (!this.persistPath) return;
|
|
5777
|
-
const dir =
|
|
5778
|
-
await
|
|
6034
|
+
const dir = path6__namespace.dirname(this.persistPath);
|
|
6035
|
+
await fs6__namespace.mkdir(dir, { recursive: true });
|
|
5779
6036
|
const data = {
|
|
5780
6037
|
messages: this.state.messages,
|
|
5781
6038
|
totalTokens: this.state.totalTokens,
|
|
5782
6039
|
limit: this.state.limit,
|
|
5783
6040
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5784
6041
|
};
|
|
5785
|
-
await
|
|
6042
|
+
await fs6__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
|
|
5786
6043
|
}
|
|
5787
6044
|
/**
|
|
5788
6045
|
* 加载持久化的上下文
|
|
@@ -5790,7 +6047,7 @@ ${summary}`,
|
|
|
5790
6047
|
async loadPersistedContext() {
|
|
5791
6048
|
if (!this.persistPath) return;
|
|
5792
6049
|
try {
|
|
5793
|
-
const content = await
|
|
6050
|
+
const content = await fs6__namespace.readFile(this.persistPath, "utf-8");
|
|
5794
6051
|
const data = JSON.parse(content);
|
|
5795
6052
|
this.state.messages = data.messages || [];
|
|
5796
6053
|
this.state.totalTokens = data.totalTokens || 0;
|
|
@@ -5960,17 +6217,17 @@ var Completer = class {
|
|
|
5960
6217
|
prefix = filePath;
|
|
5961
6218
|
} else {
|
|
5962
6219
|
const dir = filePath.slice(0, lastSlash);
|
|
5963
|
-
dirPath =
|
|
6220
|
+
dirPath = path6__namespace.resolve(this.workingDirectory, dir);
|
|
5964
6221
|
prefix = filePath.slice(lastSlash + 1);
|
|
5965
6222
|
}
|
|
5966
6223
|
try {
|
|
5967
|
-
const entries = await
|
|
6224
|
+
const entries = await fs6__namespace.readdir(dirPath, { withFileTypes: true });
|
|
5968
6225
|
const matches = [];
|
|
5969
6226
|
for (const entry of entries) {
|
|
5970
6227
|
if (entry.name.startsWith(prefix)) {
|
|
5971
6228
|
const isDir = entry.isDirectory();
|
|
5972
6229
|
matches.push({
|
|
5973
|
-
text: "@" +
|
|
6230
|
+
text: "@" + path6__namespace.relative(this.workingDirectory, path6__namespace.join(dirPath, entry.name)).replace(/\\/g, "/") + (isDir ? "/" : ""),
|
|
5974
6231
|
displayText: entry.name + (isDir ? "/" : ""),
|
|
5975
6232
|
description: isDir ? "\u76EE\u5F55" : "\u6587\u4EF6",
|
|
5976
6233
|
type: isDir ? "directory" : "file"
|
|
@@ -6042,10 +6299,10 @@ var Completer = class {
|
|
|
6042
6299
|
|
|
6043
6300
|
// src/cli/repl.ts
|
|
6044
6301
|
async function startInteractiveMode(options) {
|
|
6045
|
-
const historyFile =
|
|
6302
|
+
const historyFile = path6__namespace.join(options.workingDirectory, ".sf-cli", "history.json");
|
|
6046
6303
|
let history = [];
|
|
6047
6304
|
try {
|
|
6048
|
-
const historyData = await
|
|
6305
|
+
const historyData = await fs6__namespace.readFile(historyFile, "utf-8");
|
|
6049
6306
|
history = JSON.parse(historyData);
|
|
6050
6307
|
} catch {
|
|
6051
6308
|
}
|
|
@@ -6079,11 +6336,11 @@ async function startInteractiveMode(options) {
|
|
|
6079
6336
|
const workflowEngine = new WorkflowEngine();
|
|
6080
6337
|
const normsManager = new NormsManager({
|
|
6081
6338
|
projectPath: options.workingDirectory,
|
|
6082
|
-
normsDir:
|
|
6339
|
+
normsDir: path6__namespace.join(options.workingDirectory, ".sf-cli", "norms")
|
|
6083
6340
|
});
|
|
6084
6341
|
await configManager.load(options.workingDirectory);
|
|
6085
6342
|
await contextManager.initialize(options.workingDirectory);
|
|
6086
|
-
const statsDir =
|
|
6343
|
+
const statsDir = path6__namespace.join(options.workingDirectory, ".sf-cli");
|
|
6087
6344
|
await modelService.initialize(statsDir);
|
|
6088
6345
|
await workflowEngine.initialize(options.workingDirectory);
|
|
6089
6346
|
await normsManager.initialize();
|
|
@@ -6114,8 +6371,8 @@ async function startInteractiveMode(options) {
|
|
|
6114
6371
|
}
|
|
6115
6372
|
const saveHistory = async () => {
|
|
6116
6373
|
try {
|
|
6117
|
-
await
|
|
6118
|
-
await
|
|
6374
|
+
await fs6__namespace.mkdir(path6__namespace.dirname(historyFile), { recursive: true });
|
|
6375
|
+
await fs6__namespace.writeFile(historyFile, JSON.stringify(history.slice(-500)));
|
|
6119
6376
|
} catch {
|
|
6120
6377
|
}
|
|
6121
6378
|
};
|
|
@@ -6187,9 +6444,29 @@ async function startInteractiveMode(options) {
|
|
|
6187
6444
|
rl.prompt();
|
|
6188
6445
|
}
|
|
6189
6446
|
}
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6447
|
+
|
|
6448
|
+
// src/cli/index.ts
|
|
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;
|
|
6193
6470
|
var NAME = "sf-cli";
|
|
6194
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");
|
|
6195
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) => {
|