@nick848/sf-cli 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +202 -167
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +72 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -93
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import * as path5 from 'path';
|
|
2
|
+
import path5__default from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
1
4
|
import * as fs4 from 'fs/promises';
|
|
2
5
|
import * as fsSync from 'fs';
|
|
3
|
-
import * as path4 from 'path';
|
|
4
6
|
import * as crypto from 'crypto';
|
|
5
7
|
import * as os from 'os';
|
|
6
8
|
import { encoding_for_model } from 'tiktoken';
|
|
@@ -10,9 +12,11 @@ import chalk9 from 'chalk';
|
|
|
10
12
|
import { v4 } from 'uuid';
|
|
11
13
|
import { prompt } from 'enquirer';
|
|
12
14
|
import { spawn } from 'child_process';
|
|
13
|
-
import { createRequire } from 'module';
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
+
// node_modules/tsup/assets/esm_shims.js
|
|
17
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
18
|
+
var getDirname = () => path5__default.dirname(getFilename());
|
|
19
|
+
var __dirname$1 = /* @__PURE__ */ getDirname();
|
|
16
20
|
var DEFAULT_CONFIG = {
|
|
17
21
|
model: "GLM-5",
|
|
18
22
|
apiKey: "",
|
|
@@ -25,7 +29,7 @@ var IV_LENGTH = 16;
|
|
|
25
29
|
var KEY_DIR = ".sf-cli";
|
|
26
30
|
var KEY_FILE = ".key";
|
|
27
31
|
function getOrCreateEncryptionKey() {
|
|
28
|
-
const keyPath =
|
|
32
|
+
const keyPath = path5.join(os.homedir(), KEY_DIR, KEY_FILE);
|
|
29
33
|
try {
|
|
30
34
|
if (fsSync.existsSync(keyPath)) {
|
|
31
35
|
const keyBase64 = fsSync.readFileSync(keyPath, "utf-8").trim();
|
|
@@ -35,7 +39,7 @@ function getOrCreateEncryptionKey() {
|
|
|
35
39
|
}
|
|
36
40
|
const key = crypto.randomBytes(32);
|
|
37
41
|
try {
|
|
38
|
-
const keyDir =
|
|
42
|
+
const keyDir = path5.dirname(keyPath);
|
|
39
43
|
if (!fsSync.existsSync(keyDir)) {
|
|
40
44
|
fsSync.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
41
45
|
}
|
|
@@ -66,7 +70,7 @@ var ConfigManager = class {
|
|
|
66
70
|
}
|
|
67
71
|
async load(projectPath) {
|
|
68
72
|
this.projectPath = projectPath;
|
|
69
|
-
this.configPath =
|
|
73
|
+
this.configPath = path5.join(projectPath, ".sf-cli", "config.json");
|
|
70
74
|
try {
|
|
71
75
|
const content = await fs4.readFile(this.configPath, "utf-8");
|
|
72
76
|
const loaded = JSON.parse(content);
|
|
@@ -87,7 +91,7 @@ var ConfigManager = class {
|
|
|
87
91
|
configToSave.apiKey = encrypted;
|
|
88
92
|
configToSave.apiKeyEncrypted = true;
|
|
89
93
|
}
|
|
90
|
-
await fs4.mkdir(
|
|
94
|
+
await fs4.mkdir(path5.dirname(this.configPath), { recursive: true });
|
|
91
95
|
await fs4.writeFile(
|
|
92
96
|
this.configPath,
|
|
93
97
|
JSON.stringify(configToSave, null, 2),
|
|
@@ -346,7 +350,7 @@ var BaseAdapter = class {
|
|
|
346
350
|
* 延迟工具函数
|
|
347
351
|
*/
|
|
348
352
|
delay(ms) {
|
|
349
|
-
return new Promise((
|
|
353
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
350
354
|
}
|
|
351
355
|
};
|
|
352
356
|
|
|
@@ -967,7 +971,7 @@ var ModelService = class {
|
|
|
967
971
|
* 初始化服务
|
|
968
972
|
*/
|
|
969
973
|
async initialize(statsDir) {
|
|
970
|
-
this.statsPath =
|
|
974
|
+
this.statsPath = path5.join(statsDir, "tokens");
|
|
971
975
|
await this.loadStats();
|
|
972
976
|
const model = this.configManager.get("model");
|
|
973
977
|
const apiKey = this.configManager.get("apiKey");
|
|
@@ -1140,8 +1144,8 @@ var ModelService = class {
|
|
|
1140
1144
|
async loadStats() {
|
|
1141
1145
|
if (!this.statsPath) return;
|
|
1142
1146
|
try {
|
|
1143
|
-
const dailyPath =
|
|
1144
|
-
const totalPath =
|
|
1147
|
+
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
1148
|
+
const totalPath = path5.join(this.statsPath, "total.json");
|
|
1145
1149
|
const [daily, total] = await Promise.all([
|
|
1146
1150
|
fs4.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
1147
1151
|
fs4.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
@@ -1158,8 +1162,8 @@ var ModelService = class {
|
|
|
1158
1162
|
if (!this.statsPath) return;
|
|
1159
1163
|
try {
|
|
1160
1164
|
await fs4.mkdir(this.statsPath, { recursive: true });
|
|
1161
|
-
const dailyPath =
|
|
1162
|
-
const totalPath =
|
|
1165
|
+
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
1166
|
+
const totalPath = path5.join(this.statsPath, "total.json");
|
|
1163
1167
|
await Promise.all([
|
|
1164
1168
|
fs4.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
1165
1169
|
fs4.writeFile(totalPath, JSON.stringify({
|
|
@@ -2833,15 +2837,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
2833
2837
|
devStandards: ""
|
|
2834
2838
|
};
|
|
2835
2839
|
try {
|
|
2836
|
-
context.agentsMd = await fs4.readFile(
|
|
2840
|
+
context.agentsMd = await fs4.readFile(path5.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
2837
2841
|
} catch {
|
|
2838
2842
|
}
|
|
2839
2843
|
try {
|
|
2840
|
-
context.configYaml = await fs4.readFile(
|
|
2844
|
+
context.configYaml = await fs4.readFile(path5.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
2841
2845
|
} catch {
|
|
2842
2846
|
}
|
|
2843
2847
|
try {
|
|
2844
|
-
context.devStandards = await fs4.readFile(
|
|
2848
|
+
context.devStandards = await fs4.readFile(path5.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
2845
2849
|
} catch {
|
|
2846
2850
|
}
|
|
2847
2851
|
return context;
|
|
@@ -3777,7 +3781,7 @@ var WorkflowEngine = class {
|
|
|
3777
3781
|
*/
|
|
3778
3782
|
async initialize(projectPath) {
|
|
3779
3783
|
this.projectPath = projectPath;
|
|
3780
|
-
this.openspecPath =
|
|
3784
|
+
this.openspecPath = path5.join(projectPath, "openspec");
|
|
3781
3785
|
await this.ensureDirectories();
|
|
3782
3786
|
await this.loadProjectContext();
|
|
3783
3787
|
await this.restoreState();
|
|
@@ -3787,19 +3791,19 @@ var WorkflowEngine = class {
|
|
|
3787
3791
|
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
3788
3792
|
*/
|
|
3789
3793
|
async loadProjectContext() {
|
|
3790
|
-
const agentsMdPath =
|
|
3794
|
+
const agentsMdPath = path5.join(this.projectPath, "AGENTS.md");
|
|
3791
3795
|
try {
|
|
3792
3796
|
this.projectContext = await fs4.readFile(agentsMdPath, "utf-8");
|
|
3793
3797
|
} catch {
|
|
3794
3798
|
this.projectContext = "";
|
|
3795
3799
|
}
|
|
3796
|
-
const configPath =
|
|
3800
|
+
const configPath = path5.join(this.openspecPath, "config.yaml");
|
|
3797
3801
|
try {
|
|
3798
3802
|
this.projectConfig = await fs4.readFile(configPath, "utf-8");
|
|
3799
3803
|
} catch {
|
|
3800
3804
|
this.projectConfig = "";
|
|
3801
3805
|
}
|
|
3802
|
-
const devstandedPath =
|
|
3806
|
+
const devstandedPath = path5.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
3803
3807
|
try {
|
|
3804
3808
|
this.devStandards = await fs4.readFile(devstandedPath, "utf-8");
|
|
3805
3809
|
} catch {
|
|
@@ -4087,10 +4091,10 @@ var WorkflowEngine = class {
|
|
|
4087
4091
|
await this.createSpecDocument(summary);
|
|
4088
4092
|
await this.updateChangeRecord("archived");
|
|
4089
4093
|
await this.saveState();
|
|
4090
|
-
const changesDir =
|
|
4091
|
-
const archiveDir =
|
|
4092
|
-
const changeFile =
|
|
4093
|
-
const archiveFile =
|
|
4094
|
+
const changesDir = path5.join(this.openspecPath, "changes");
|
|
4095
|
+
const archiveDir = path5.join(changesDir, "archive");
|
|
4096
|
+
const changeFile = path5.join(changesDir, `${changeId}.md`);
|
|
4097
|
+
const archiveFile = path5.join(archiveDir, `${changeId}.md`);
|
|
4094
4098
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
4095
4099
|
await fs4.rename(changeFile, archiveFile).catch(() => {
|
|
4096
4100
|
});
|
|
@@ -4114,14 +4118,14 @@ var WorkflowEngine = class {
|
|
|
4114
4118
|
}
|
|
4115
4119
|
// ==================== 私有方法 ====================
|
|
4116
4120
|
async ensureDirectories() {
|
|
4117
|
-
const changesDir =
|
|
4118
|
-
const archiveDir =
|
|
4119
|
-
const specDir =
|
|
4121
|
+
const changesDir = path5.join(this.openspecPath, "changes");
|
|
4122
|
+
const archiveDir = path5.join(changesDir, "archive");
|
|
4123
|
+
const specDir = path5.join(this.openspecPath, "spec");
|
|
4120
4124
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
4121
4125
|
await fs4.mkdir(specDir, { recursive: true });
|
|
4122
4126
|
}
|
|
4123
4127
|
async restoreState() {
|
|
4124
|
-
const statePath =
|
|
4128
|
+
const statePath = path5.join(this.openspecPath, ".workflow-state.json");
|
|
4125
4129
|
try {
|
|
4126
4130
|
const content = await fs4.readFile(statePath, "utf-8");
|
|
4127
4131
|
this.state = JSON.parse(content, (key, value) => {
|
|
@@ -4141,11 +4145,11 @@ var WorkflowEngine = class {
|
|
|
4141
4145
|
}
|
|
4142
4146
|
}
|
|
4143
4147
|
async saveState() {
|
|
4144
|
-
const statePath =
|
|
4148
|
+
const statePath = path5.join(this.openspecPath, ".workflow-state.json");
|
|
4145
4149
|
await fs4.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
4146
4150
|
}
|
|
4147
4151
|
async restoreSnapshots() {
|
|
4148
|
-
const snapshotsPath =
|
|
4152
|
+
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
4149
4153
|
try {
|
|
4150
4154
|
const content = await fs4.readFile(snapshotsPath, "utf-8");
|
|
4151
4155
|
const data = JSON.parse(content, (key, value) => {
|
|
@@ -4162,7 +4166,7 @@ var WorkflowEngine = class {
|
|
|
4162
4166
|
}
|
|
4163
4167
|
}
|
|
4164
4168
|
async saveSnapshots() {
|
|
4165
|
-
const snapshotsPath =
|
|
4169
|
+
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
4166
4170
|
const data = Array.from(this.snapshots.values());
|
|
4167
4171
|
await fs4.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
4168
4172
|
}
|
|
@@ -4184,7 +4188,7 @@ var WorkflowEngine = class {
|
|
|
4184
4188
|
}
|
|
4185
4189
|
async createChangeRecord() {
|
|
4186
4190
|
if (!this.state) return;
|
|
4187
|
-
const changePath =
|
|
4191
|
+
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4188
4192
|
await fs4.writeFile(changePath, this.formatChangeRecord());
|
|
4189
4193
|
}
|
|
4190
4194
|
async updateChangeRecord(status) {
|
|
@@ -4192,7 +4196,7 @@ var WorkflowEngine = class {
|
|
|
4192
4196
|
if (status) {
|
|
4193
4197
|
this.state.status = status;
|
|
4194
4198
|
}
|
|
4195
|
-
const changePath =
|
|
4199
|
+
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4196
4200
|
await fs4.writeFile(changePath, this.formatChangeRecord());
|
|
4197
4201
|
}
|
|
4198
4202
|
formatChangeRecord() {
|
|
@@ -4232,7 +4236,7 @@ ${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
|
4232
4236
|
}
|
|
4233
4237
|
async createSpecDocument(summary) {
|
|
4234
4238
|
if (!this.state) return;
|
|
4235
|
-
const specPath =
|
|
4239
|
+
const specPath = path5.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
4236
4240
|
const content = `# Spec: ${this.state.title}
|
|
4237
4241
|
|
|
4238
4242
|
> \u53D8\u66F4ID: ${this.state.id}
|
|
@@ -4330,7 +4334,7 @@ var NormsManager = class {
|
|
|
4330
4334
|
* 从单个文件学习规范
|
|
4331
4335
|
*/
|
|
4332
4336
|
async learnFromFile(filePath, content) {
|
|
4333
|
-
const ext =
|
|
4337
|
+
const ext = path5.extname(filePath);
|
|
4334
4338
|
const patterns = this.extractPatterns(filePath, content, ext);
|
|
4335
4339
|
for (const pattern of patterns) {
|
|
4336
4340
|
await this.recordOccurrence({
|
|
@@ -4937,12 +4941,12 @@ ${response}`;
|
|
|
4937
4941
|
for (const entry of entries) {
|
|
4938
4942
|
if (entry.isDirectory()) {
|
|
4939
4943
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
4940
|
-
await scan(
|
|
4944
|
+
await scan(path5.join(dir, entry.name));
|
|
4941
4945
|
}
|
|
4942
4946
|
} else if (entry.isFile()) {
|
|
4943
|
-
const ext =
|
|
4947
|
+
const ext = path5.extname(entry.name);
|
|
4944
4948
|
if (SCAN_EXTENSIONS.includes(ext)) {
|
|
4945
|
-
files.push(
|
|
4949
|
+
files.push(path5.join(dir, entry.name));
|
|
4946
4950
|
}
|
|
4947
4951
|
}
|
|
4948
4952
|
}
|
|
@@ -4977,13 +4981,13 @@ ${response}`;
|
|
|
4977
4981
|
*/
|
|
4978
4982
|
async ensureNormsDir() {
|
|
4979
4983
|
await fs4.mkdir(this.config.normsDir, { recursive: true });
|
|
4980
|
-
await fs4.mkdir(
|
|
4984
|
+
await fs4.mkdir(path5.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
4981
4985
|
}
|
|
4982
4986
|
/**
|
|
4983
4987
|
* 加载已有规范
|
|
4984
4988
|
*/
|
|
4985
4989
|
async loadStandards() {
|
|
4986
|
-
const standardsPath =
|
|
4990
|
+
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
4987
4991
|
try {
|
|
4988
4992
|
const content = await fs4.readFile(standardsPath, "utf-8");
|
|
4989
4993
|
const data = JSON.parse(content);
|
|
@@ -5001,7 +5005,7 @@ ${response}`;
|
|
|
5001
5005
|
* 加载权重数据
|
|
5002
5006
|
*/
|
|
5003
5007
|
async loadWeights() {
|
|
5004
|
-
const weightsPath =
|
|
5008
|
+
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
5005
5009
|
try {
|
|
5006
5010
|
const content = await fs4.readFile(weightsPath, "utf-8");
|
|
5007
5011
|
const data = JSON.parse(content);
|
|
@@ -5018,7 +5022,7 @@ ${response}`;
|
|
|
5018
5022
|
* 保存规范
|
|
5019
5023
|
*/
|
|
5020
5024
|
async saveStandards() {
|
|
5021
|
-
const standardsPath =
|
|
5025
|
+
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
5022
5026
|
await fs4.writeFile(
|
|
5023
5027
|
standardsPath,
|
|
5024
5028
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
@@ -5029,7 +5033,7 @@ ${response}`;
|
|
|
5029
5033
|
* 保存权重
|
|
5030
5034
|
*/
|
|
5031
5035
|
async saveWeights() {
|
|
5032
|
-
const weightsPath =
|
|
5036
|
+
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
5033
5037
|
await fs4.writeFile(
|
|
5034
5038
|
weightsPath,
|
|
5035
5039
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
@@ -5057,7 +5061,7 @@ ${response}`;
|
|
|
5057
5061
|
* 保存周报
|
|
5058
5062
|
*/
|
|
5059
5063
|
async saveWeeklyReport(weekId, report) {
|
|
5060
|
-
const reportPath =
|
|
5064
|
+
const reportPath = path5.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
5061
5065
|
await fs4.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
5062
5066
|
}
|
|
5063
5067
|
/**
|
|
@@ -5066,7 +5070,7 @@ ${response}`;
|
|
|
5066
5070
|
async updateDevStandedMd() {
|
|
5067
5071
|
const standards = this.getEffectiveStandards();
|
|
5068
5072
|
const content = this.generateDevStandedMd(standards);
|
|
5069
|
-
const mdPath =
|
|
5073
|
+
const mdPath = path5.join(this.config.normsDir, "devstanded.md");
|
|
5070
5074
|
await fs4.writeFile(mdPath, content, "utf-8");
|
|
5071
5075
|
}
|
|
5072
5076
|
/**
|
|
@@ -5214,7 +5218,7 @@ var ContextManager = class {
|
|
|
5214
5218
|
}
|
|
5215
5219
|
async initialize(projectPath) {
|
|
5216
5220
|
this.projectPath = projectPath;
|
|
5217
|
-
this.persistPath =
|
|
5221
|
+
this.persistPath = path5.join(projectPath, ".sf-cli", "cache", "context", "context.json");
|
|
5218
5222
|
await this.loadPersistedContext();
|
|
5219
5223
|
}
|
|
5220
5224
|
/**
|
|
@@ -5455,7 +5459,7 @@ ${summary}`,
|
|
|
5455
5459
|
*/
|
|
5456
5460
|
async persist() {
|
|
5457
5461
|
if (!this.persistPath) return;
|
|
5458
|
-
const dir =
|
|
5462
|
+
const dir = path5.dirname(this.persistPath);
|
|
5459
5463
|
await fs4.mkdir(dir, { recursive: true });
|
|
5460
5464
|
const data = {
|
|
5461
5465
|
messages: this.state.messages,
|
|
@@ -5590,8 +5594,8 @@ var CommandParser = class {
|
|
|
5590
5594
|
};
|
|
5591
5595
|
}
|
|
5592
5596
|
parseAtPath(input) {
|
|
5593
|
-
const
|
|
5594
|
-
if (!
|
|
5597
|
+
const path14 = input.slice(1).trim();
|
|
5598
|
+
if (!path14) {
|
|
5595
5599
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
5596
5600
|
}
|
|
5597
5601
|
return {
|
|
@@ -5599,7 +5603,7 @@ var CommandParser = class {
|
|
|
5599
5603
|
command: {
|
|
5600
5604
|
type: "at" /* AT */,
|
|
5601
5605
|
raw: input,
|
|
5602
|
-
path:
|
|
5606
|
+
path: path14
|
|
5603
5607
|
}
|
|
5604
5608
|
};
|
|
5605
5609
|
}
|
|
@@ -5655,9 +5659,9 @@ async function initProject(options = {}, workingDir) {
|
|
|
5655
5659
|
output: chalk9.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
5656
5660
|
};
|
|
5657
5661
|
}
|
|
5658
|
-
const sfCliDir =
|
|
5659
|
-
const openspecDir =
|
|
5660
|
-
const agentsMdPath =
|
|
5662
|
+
const sfCliDir = path5.join(cwd, ".sf-cli");
|
|
5663
|
+
const openspecDir = path5.join(cwd, "openspec");
|
|
5664
|
+
const agentsMdPath = path5.join(cwd, "AGENTS.md");
|
|
5661
5665
|
try {
|
|
5662
5666
|
const agentsExists = await fileExists(agentsMdPath);
|
|
5663
5667
|
if (agentsExists && !options.force) {
|
|
@@ -5717,7 +5721,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
5717
5721
|
"logs"
|
|
5718
5722
|
];
|
|
5719
5723
|
for (const dir of dirs) {
|
|
5720
|
-
await fs4.mkdir(
|
|
5724
|
+
await fs4.mkdir(path5.join(basePath, dir), { recursive: true });
|
|
5721
5725
|
}
|
|
5722
5726
|
const config = {
|
|
5723
5727
|
version: "1.0.0",
|
|
@@ -5726,36 +5730,36 @@ async function createSfCliDirectory(basePath) {
|
|
|
5726
5730
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5727
5731
|
};
|
|
5728
5732
|
await fs4.writeFile(
|
|
5729
|
-
|
|
5733
|
+
path5.join(basePath, "config.json"),
|
|
5730
5734
|
JSON.stringify(config, null, 2),
|
|
5731
5735
|
"utf-8"
|
|
5732
5736
|
);
|
|
5733
5737
|
await fs4.writeFile(
|
|
5734
|
-
|
|
5738
|
+
path5.join(basePath, "agents", "registry.json"),
|
|
5735
5739
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
5736
5740
|
"utf-8"
|
|
5737
5741
|
);
|
|
5738
5742
|
await fs4.writeFile(
|
|
5739
|
-
|
|
5743
|
+
path5.join(basePath, "skills", "registry.json"),
|
|
5740
5744
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
5741
5745
|
"utf-8"
|
|
5742
5746
|
);
|
|
5743
5747
|
await fs4.writeFile(
|
|
5744
|
-
|
|
5748
|
+
path5.join(basePath, "health", "health.md"),
|
|
5745
5749
|
generateHealthTemplate(),
|
|
5746
5750
|
"utf-8"
|
|
5747
5751
|
);
|
|
5748
5752
|
}
|
|
5749
5753
|
async function createOpenSpecDirectory(basePath) {
|
|
5750
|
-
const changesDir =
|
|
5751
|
-
const archiveDir =
|
|
5752
|
-
const specDir =
|
|
5754
|
+
const changesDir = path5.join(basePath, "changes");
|
|
5755
|
+
const archiveDir = path5.join(changesDir, "archive");
|
|
5756
|
+
const specDir = path5.join(basePath, "spec");
|
|
5753
5757
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
5754
5758
|
await fs4.mkdir(specDir, { recursive: true });
|
|
5755
5759
|
}
|
|
5756
5760
|
async function analyzeProject(cwd) {
|
|
5757
5761
|
const result = {
|
|
5758
|
-
name:
|
|
5762
|
+
name: path5.basename(cwd),
|
|
5759
5763
|
type: "unknown",
|
|
5760
5764
|
framework: null,
|
|
5761
5765
|
techStack: [],
|
|
@@ -5774,7 +5778,7 @@ async function analyzeProject(cwd) {
|
|
|
5774
5778
|
hasEslint: false,
|
|
5775
5779
|
hasPrettier: false
|
|
5776
5780
|
};
|
|
5777
|
-
const pkgPath =
|
|
5781
|
+
const pkgPath = path5.join(cwd, "package.json");
|
|
5778
5782
|
try {
|
|
5779
5783
|
const pkgContent = await fs4.readFile(pkgPath, "utf-8");
|
|
5780
5784
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -5895,7 +5899,7 @@ async function findEntryPoints(cwd) {
|
|
|
5895
5899
|
"index.js"
|
|
5896
5900
|
];
|
|
5897
5901
|
for (const entry of possibleEntries) {
|
|
5898
|
-
const fullPath =
|
|
5902
|
+
const fullPath = path5.join(cwd, entry);
|
|
5899
5903
|
if (await fileExists(fullPath)) {
|
|
5900
5904
|
entryPoints.push(entry);
|
|
5901
5905
|
}
|
|
@@ -5903,7 +5907,7 @@ async function findEntryPoints(cwd) {
|
|
|
5903
5907
|
return entryPoints;
|
|
5904
5908
|
}
|
|
5905
5909
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
5906
|
-
const analysisPath =
|
|
5910
|
+
const analysisPath = path5.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
5907
5911
|
await fs4.writeFile(
|
|
5908
5912
|
analysisPath,
|
|
5909
5913
|
JSON.stringify(analysis, null, 2),
|
|
@@ -5911,7 +5915,7 @@ async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
|
5911
5915
|
);
|
|
5912
5916
|
}
|
|
5913
5917
|
async function generateOpenSpecConfig(openspecDir, analysis) {
|
|
5914
|
-
const configPath =
|
|
5918
|
+
const configPath = path5.join(openspecDir, "config.yaml");
|
|
5915
5919
|
const content = `# OpenSpec \u9879\u76EE\u914D\u7F6E
|
|
5916
5920
|
# \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
|
|
5917
5921
|
|
|
@@ -6443,8 +6447,10 @@ function createSpinner(message) {
|
|
|
6443
6447
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
6444
6448
|
};
|
|
6445
6449
|
}
|
|
6446
|
-
var
|
|
6447
|
-
var
|
|
6450
|
+
var packageJsonPath = path5.resolve(__dirname$1, "../../package.json");
|
|
6451
|
+
var packageJson = JSON.parse(fsSync.readFileSync(packageJsonPath, "utf-8"));
|
|
6452
|
+
var CURRENT_VERSION = packageJson.version;
|
|
6453
|
+
var PACKAGE_NAME = packageJson.name;
|
|
6448
6454
|
async function handleUpdate(args, ctx) {
|
|
6449
6455
|
const options = {
|
|
6450
6456
|
check: args.includes("--check") || args.includes("-c"),
|
|
@@ -6471,6 +6477,9 @@ async function checkForUpdates() {
|
|
|
6471
6477
|
const latestVersion = await getLatestVersion();
|
|
6472
6478
|
if (!latestVersion) {
|
|
6473
6479
|
lines.push(chalk9.yellow("\u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
6480
|
+
lines.push(chalk9.gray("\u53EF\u80FD\u539F\u56E0:"));
|
|
6481
|
+
lines.push(chalk9.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
|
|
6482
|
+
lines.push(chalk9.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
|
|
6474
6483
|
return { output: lines.join("\n") };
|
|
6475
6484
|
}
|
|
6476
6485
|
lines.push(chalk9.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
|
|
@@ -6489,43 +6498,70 @@ async function checkForUpdates() {
|
|
|
6489
6498
|
async function performUpdate(targetVersion) {
|
|
6490
6499
|
const lines = [];
|
|
6491
6500
|
lines.push(chalk9.cyan("\u6B63\u5728\u66F4\u65B0 sf-cli..."));
|
|
6501
|
+
lines.push(chalk9.gray(` \u5305\u540D: ${PACKAGE_NAME}`));
|
|
6502
|
+
lines.push(chalk9.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
|
|
6492
6503
|
try {
|
|
6493
6504
|
const latestVersion = await getLatestVersion();
|
|
6505
|
+
if (!latestVersion && !targetVersion) {
|
|
6506
|
+
lines.push(chalk9.yellow("\n\u26A0 \u65E0\u6CD5\u83B7\u53D6\u6700\u65B0\u7248\u672C\u4FE1\u606F"));
|
|
6507
|
+
lines.push(chalk9.gray("\n\u53EF\u80FD\u539F\u56E0:"));
|
|
6508
|
+
lines.push(chalk9.gray(" 1. \u5305\u5C1A\u672A\u53D1\u5E03\u5230 npm"));
|
|
6509
|
+
lines.push(chalk9.gray(" 2. \u7F51\u7EDC\u8FDE\u63A5\u95EE\u9898"));
|
|
6510
|
+
lines.push(chalk9.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
6511
|
+
lines.push(chalk9.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
6512
|
+
return { output: lines.join("\n") };
|
|
6513
|
+
}
|
|
6494
6514
|
if (latestVersion === CURRENT_VERSION && !targetVersion) {
|
|
6495
|
-
lines.push(chalk9.green("\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
|
|
6515
|
+
lines.push(chalk9.green("\n\u2713 \u5DF2\u662F\u6700\u65B0\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0"));
|
|
6496
6516
|
return { output: lines.join("\n") };
|
|
6497
6517
|
}
|
|
6498
6518
|
const packageSpec = targetVersion ? `${PACKAGE_NAME}@${targetVersion}` : `${PACKAGE_NAME}@latest`;
|
|
6499
6519
|
lines.push(chalk9.gray(` \u5B89\u88C5: ${packageSpec}`));
|
|
6500
|
-
await new Promise((
|
|
6520
|
+
const installResult = await new Promise((resolve4) => {
|
|
6501
6521
|
const proc = spawn("npm", ["install", "-g", packageSpec], {
|
|
6502
6522
|
stdio: "pipe",
|
|
6503
6523
|
shell: true
|
|
6504
6524
|
});
|
|
6525
|
+
let output = "";
|
|
6526
|
+
proc.stdout?.on("data", (data) => {
|
|
6527
|
+
output += data.toString();
|
|
6528
|
+
});
|
|
6529
|
+
proc.stderr?.on("data", (data) => {
|
|
6530
|
+
output += data.toString();
|
|
6531
|
+
});
|
|
6505
6532
|
proc.on("close", (code) => {
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
}
|
|
6533
|
+
resolve4({
|
|
6534
|
+
success: code === 0,
|
|
6535
|
+
output
|
|
6536
|
+
});
|
|
6511
6537
|
});
|
|
6512
6538
|
proc.on("error", (err) => {
|
|
6513
|
-
|
|
6539
|
+
resolve4({
|
|
6540
|
+
success: false,
|
|
6541
|
+
output: err.message
|
|
6542
|
+
});
|
|
6514
6543
|
});
|
|
6515
6544
|
});
|
|
6516
|
-
|
|
6545
|
+
if (!installResult.success) {
|
|
6546
|
+
lines.push(chalk9.red("\n\u2717 \u66F4\u65B0\u5931\u8D25"));
|
|
6547
|
+
lines.push(chalk9.gray(installResult.output));
|
|
6548
|
+
lines.push(chalk9.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
6549
|
+
lines.push(chalk9.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
6550
|
+
return { output: lines.join("\n") };
|
|
6551
|
+
}
|
|
6552
|
+
lines.push(chalk9.green("\n\u2713 \u66F4\u65B0\u5B8C\u6210!"));
|
|
6517
6553
|
lines.push(chalk9.gray(` \u65B0\u7248\u672C: v${targetVersion || latestVersion}`));
|
|
6518
6554
|
lines.push(chalk9.gray("\n\u8BF7\u91CD\u542F CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"));
|
|
6519
6555
|
} catch (error) {
|
|
6520
6556
|
lines.push(chalk9.red("\u66F4\u65B0\u5931\u8D25: " + error.message));
|
|
6521
6557
|
lines.push(chalk9.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
6522
|
-
lines.push(chalk9.
|
|
6558
|
+
lines.push(chalk9.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
6523
6559
|
}
|
|
6524
6560
|
return { output: lines.join("\n") };
|
|
6525
6561
|
}
|
|
6526
6562
|
async function getLatestVersion() {
|
|
6527
6563
|
try {
|
|
6528
|
-
const result = await new Promise((
|
|
6564
|
+
const result = await new Promise((resolve4, reject) => {
|
|
6529
6565
|
const proc = spawn("npm", ["view", PACKAGE_NAME, "version"], {
|
|
6530
6566
|
stdio: "pipe",
|
|
6531
6567
|
shell: true
|
|
@@ -6535,13 +6571,17 @@ async function getLatestVersion() {
|
|
|
6535
6571
|
output += data.toString();
|
|
6536
6572
|
});
|
|
6537
6573
|
proc.on("close", (code) => {
|
|
6538
|
-
if (code === 0) {
|
|
6539
|
-
|
|
6574
|
+
if (code === 0 && output.trim()) {
|
|
6575
|
+
resolve4(output.trim());
|
|
6540
6576
|
} else {
|
|
6541
6577
|
reject(new Error("Failed to get version"));
|
|
6542
6578
|
}
|
|
6543
6579
|
});
|
|
6544
6580
|
proc.on("error", reject);
|
|
6581
|
+
setTimeout(() => {
|
|
6582
|
+
proc.kill();
|
|
6583
|
+
reject(new Error("Timeout"));
|
|
6584
|
+
}, 1e4);
|
|
6545
6585
|
});
|
|
6546
6586
|
return result || null;
|
|
6547
6587
|
} catch {
|
|
@@ -6723,13 +6763,13 @@ function parseArgs(args) {
|
|
|
6723
6763
|
}
|
|
6724
6764
|
async function readProjectContext(cwd) {
|
|
6725
6765
|
const defaultContext = {
|
|
6726
|
-
name:
|
|
6766
|
+
name: path5.basename(cwd),
|
|
6727
6767
|
type: "unknown",
|
|
6728
6768
|
framework: null,
|
|
6729
6769
|
techStack: [],
|
|
6730
6770
|
description: ""
|
|
6731
6771
|
};
|
|
6732
|
-
const agentsPath =
|
|
6772
|
+
const agentsPath = path5.join(cwd, "AGENTS.md");
|
|
6733
6773
|
try {
|
|
6734
6774
|
const stats = await fs4.stat(agentsPath);
|
|
6735
6775
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
@@ -6744,7 +6784,7 @@ async function readProjectContext(cwd) {
|
|
|
6744
6784
|
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
6745
6785
|
}
|
|
6746
6786
|
}
|
|
6747
|
-
const configPath =
|
|
6787
|
+
const configPath = path5.join(cwd, "openspec", "config.yaml");
|
|
6748
6788
|
try {
|
|
6749
6789
|
const stats = await fs4.stat(configPath);
|
|
6750
6790
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
@@ -7223,9 +7263,11 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9.cyan(`
|
|
|
7223
7263
|
throw e;
|
|
7224
7264
|
}
|
|
7225
7265
|
}
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
var
|
|
7266
|
+
|
|
7267
|
+
// src/commands/runner.ts
|
|
7268
|
+
var packageJsonPath2 = path5.resolve(__dirname$1, "../../package.json");
|
|
7269
|
+
var packageJson2 = JSON.parse(fsSync.readFileSync(packageJsonPath2, "utf-8"));
|
|
7270
|
+
var VERSION2 = packageJson2.version;
|
|
7229
7271
|
async function runSlashCommand(command, args, ctx) {
|
|
7230
7272
|
const normalizedCommand = normalizeCommand(command);
|
|
7231
7273
|
switch (normalizedCommand) {
|
|
@@ -7272,7 +7314,7 @@ function normalizeCommand(command) {
|
|
|
7272
7314
|
}
|
|
7273
7315
|
async function handleFileReference(filePath, ctx) {
|
|
7274
7316
|
const cwd = ctx.options.workingDirectory;
|
|
7275
|
-
const absolutePath =
|
|
7317
|
+
const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.join(cwd, filePath);
|
|
7276
7318
|
try {
|
|
7277
7319
|
const stats = await fs4.stat(absolutePath);
|
|
7278
7320
|
if (stats.isDirectory()) {
|
|
@@ -7320,7 +7362,7 @@ async function executeShell(command, ctx) {
|
|
|
7320
7362
|
\u547D\u4EE4 "${command}" \u53EF\u80FD\u4F1A\u5220\u9664\u91CD\u8981\u6587\u4EF6`) + chalk9.gray("\n\u4F7F\u7528 yolo \u6A21\u5F0F\u5F3A\u5236\u6267\u884C")
|
|
7321
7363
|
};
|
|
7322
7364
|
}
|
|
7323
|
-
return new Promise((
|
|
7365
|
+
return new Promise((resolve4) => {
|
|
7324
7366
|
const shell = spawn(command, [], {
|
|
7325
7367
|
shell: true,
|
|
7326
7368
|
cwd: ctx.options.workingDirectory
|
|
@@ -7345,11 +7387,11 @@ async function executeShell(command, ctx) {
|
|
|
7345
7387
|
output += chalk9.red(`
|
|
7346
7388
|
\u9000\u51FA\u7801: ${code}`);
|
|
7347
7389
|
}
|
|
7348
|
-
|
|
7390
|
+
resolve4({ output: output || chalk9.gray("(\u65E0\u8F93\u51FA)") });
|
|
7349
7391
|
});
|
|
7350
7392
|
setTimeout(() => {
|
|
7351
7393
|
shell.kill();
|
|
7352
|
-
|
|
7394
|
+
resolve4({
|
|
7353
7395
|
output: chalk9.yellow("\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0C\u5DF2\u7EC8\u6B62")
|
|
7354
7396
|
});
|
|
7355
7397
|
}, 6e4);
|
|
@@ -7656,7 +7698,7 @@ var Completer = class {
|
|
|
7656
7698
|
prefix = filePath;
|
|
7657
7699
|
} else {
|
|
7658
7700
|
const dir = filePath.slice(0, lastSlash);
|
|
7659
|
-
dirPath =
|
|
7701
|
+
dirPath = path5.resolve(this.workingDirectory, dir);
|
|
7660
7702
|
prefix = filePath.slice(lastSlash + 1);
|
|
7661
7703
|
}
|
|
7662
7704
|
try {
|
|
@@ -7666,7 +7708,7 @@ var Completer = class {
|
|
|
7666
7708
|
if (entry.name.startsWith(prefix)) {
|
|
7667
7709
|
const isDir = entry.isDirectory();
|
|
7668
7710
|
matches.push({
|
|
7669
|
-
text: "@" +
|
|
7711
|
+
text: "@" + path5.relative(this.workingDirectory, path5.join(dirPath, entry.name)).replace(/\\/g, "/") + (isDir ? "/" : ""),
|
|
7670
7712
|
displayText: entry.name + (isDir ? "/" : ""),
|
|
7671
7713
|
description: isDir ? "\u76EE\u5F55" : "\u6587\u4EF6",
|
|
7672
7714
|
type: isDir ? "directory" : "file"
|