@nick848/sf-cli 1.0.0 → 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/README.md +61 -0
- package/dist/cli/index.js +398 -166
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +254 -245
- package/dist/index.d.ts +254 -245
- package/dist/index.js +215 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +287 -94
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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';
|
|
@@ -11,7 +13,10 @@ import { v4 } from 'uuid';
|
|
|
11
13
|
import { prompt } from 'enquirer';
|
|
12
14
|
import { spawn } from 'child_process';
|
|
13
15
|
|
|
14
|
-
//
|
|
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();
|
|
15
20
|
var DEFAULT_CONFIG = {
|
|
16
21
|
model: "GLM-5",
|
|
17
22
|
apiKey: "",
|
|
@@ -24,7 +29,7 @@ var IV_LENGTH = 16;
|
|
|
24
29
|
var KEY_DIR = ".sf-cli";
|
|
25
30
|
var KEY_FILE = ".key";
|
|
26
31
|
function getOrCreateEncryptionKey() {
|
|
27
|
-
const keyPath =
|
|
32
|
+
const keyPath = path5.join(os.homedir(), KEY_DIR, KEY_FILE);
|
|
28
33
|
try {
|
|
29
34
|
if (fsSync.existsSync(keyPath)) {
|
|
30
35
|
const keyBase64 = fsSync.readFileSync(keyPath, "utf-8").trim();
|
|
@@ -34,7 +39,7 @@ function getOrCreateEncryptionKey() {
|
|
|
34
39
|
}
|
|
35
40
|
const key = crypto.randomBytes(32);
|
|
36
41
|
try {
|
|
37
|
-
const keyDir =
|
|
42
|
+
const keyDir = path5.dirname(keyPath);
|
|
38
43
|
if (!fsSync.existsSync(keyDir)) {
|
|
39
44
|
fsSync.mkdirSync(keyDir, { recursive: true, mode: 448 });
|
|
40
45
|
}
|
|
@@ -65,7 +70,7 @@ var ConfigManager = class {
|
|
|
65
70
|
}
|
|
66
71
|
async load(projectPath) {
|
|
67
72
|
this.projectPath = projectPath;
|
|
68
|
-
this.configPath =
|
|
73
|
+
this.configPath = path5.join(projectPath, ".sf-cli", "config.json");
|
|
69
74
|
try {
|
|
70
75
|
const content = await fs4.readFile(this.configPath, "utf-8");
|
|
71
76
|
const loaded = JSON.parse(content);
|
|
@@ -86,7 +91,7 @@ var ConfigManager = class {
|
|
|
86
91
|
configToSave.apiKey = encrypted;
|
|
87
92
|
configToSave.apiKeyEncrypted = true;
|
|
88
93
|
}
|
|
89
|
-
await fs4.mkdir(
|
|
94
|
+
await fs4.mkdir(path5.dirname(this.configPath), { recursive: true });
|
|
90
95
|
await fs4.writeFile(
|
|
91
96
|
this.configPath,
|
|
92
97
|
JSON.stringify(configToSave, null, 2),
|
|
@@ -345,7 +350,7 @@ var BaseAdapter = class {
|
|
|
345
350
|
* 延迟工具函数
|
|
346
351
|
*/
|
|
347
352
|
delay(ms) {
|
|
348
|
-
return new Promise((
|
|
353
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
349
354
|
}
|
|
350
355
|
};
|
|
351
356
|
|
|
@@ -966,7 +971,7 @@ var ModelService = class {
|
|
|
966
971
|
* 初始化服务
|
|
967
972
|
*/
|
|
968
973
|
async initialize(statsDir) {
|
|
969
|
-
this.statsPath =
|
|
974
|
+
this.statsPath = path5.join(statsDir, "tokens");
|
|
970
975
|
await this.loadStats();
|
|
971
976
|
const model = this.configManager.get("model");
|
|
972
977
|
const apiKey = this.configManager.get("apiKey");
|
|
@@ -1139,8 +1144,8 @@ var ModelService = class {
|
|
|
1139
1144
|
async loadStats() {
|
|
1140
1145
|
if (!this.statsPath) return;
|
|
1141
1146
|
try {
|
|
1142
|
-
const dailyPath =
|
|
1143
|
-
const totalPath =
|
|
1147
|
+
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
1148
|
+
const totalPath = path5.join(this.statsPath, "total.json");
|
|
1144
1149
|
const [daily, total] = await Promise.all([
|
|
1145
1150
|
fs4.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
1146
1151
|
fs4.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
@@ -1157,8 +1162,8 @@ var ModelService = class {
|
|
|
1157
1162
|
if (!this.statsPath) return;
|
|
1158
1163
|
try {
|
|
1159
1164
|
await fs4.mkdir(this.statsPath, { recursive: true });
|
|
1160
|
-
const dailyPath =
|
|
1161
|
-
const totalPath =
|
|
1165
|
+
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
1166
|
+
const totalPath = path5.join(this.statsPath, "total.json");
|
|
1162
1167
|
await Promise.all([
|
|
1163
1168
|
fs4.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
1164
1169
|
fs4.writeFile(totalPath, JSON.stringify({
|
|
@@ -2832,15 +2837,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
2832
2837
|
devStandards: ""
|
|
2833
2838
|
};
|
|
2834
2839
|
try {
|
|
2835
|
-
context.agentsMd = await fs4.readFile(
|
|
2840
|
+
context.agentsMd = await fs4.readFile(path5.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
2836
2841
|
} catch {
|
|
2837
2842
|
}
|
|
2838
2843
|
try {
|
|
2839
|
-
context.configYaml = await fs4.readFile(
|
|
2844
|
+
context.configYaml = await fs4.readFile(path5.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
2840
2845
|
} catch {
|
|
2841
2846
|
}
|
|
2842
2847
|
try {
|
|
2843
|
-
context.devStandards = await fs4.readFile(
|
|
2848
|
+
context.devStandards = await fs4.readFile(path5.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
2844
2849
|
} catch {
|
|
2845
2850
|
}
|
|
2846
2851
|
return context;
|
|
@@ -3776,7 +3781,7 @@ var WorkflowEngine = class {
|
|
|
3776
3781
|
*/
|
|
3777
3782
|
async initialize(projectPath) {
|
|
3778
3783
|
this.projectPath = projectPath;
|
|
3779
|
-
this.openspecPath =
|
|
3784
|
+
this.openspecPath = path5.join(projectPath, "openspec");
|
|
3780
3785
|
await this.ensureDirectories();
|
|
3781
3786
|
await this.loadProjectContext();
|
|
3782
3787
|
await this.restoreState();
|
|
@@ -3786,19 +3791,19 @@ var WorkflowEngine = class {
|
|
|
3786
3791
|
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
3787
3792
|
*/
|
|
3788
3793
|
async loadProjectContext() {
|
|
3789
|
-
const agentsMdPath =
|
|
3794
|
+
const agentsMdPath = path5.join(this.projectPath, "AGENTS.md");
|
|
3790
3795
|
try {
|
|
3791
3796
|
this.projectContext = await fs4.readFile(agentsMdPath, "utf-8");
|
|
3792
3797
|
} catch {
|
|
3793
3798
|
this.projectContext = "";
|
|
3794
3799
|
}
|
|
3795
|
-
const configPath =
|
|
3800
|
+
const configPath = path5.join(this.openspecPath, "config.yaml");
|
|
3796
3801
|
try {
|
|
3797
3802
|
this.projectConfig = await fs4.readFile(configPath, "utf-8");
|
|
3798
3803
|
} catch {
|
|
3799
3804
|
this.projectConfig = "";
|
|
3800
3805
|
}
|
|
3801
|
-
const devstandedPath =
|
|
3806
|
+
const devstandedPath = path5.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
3802
3807
|
try {
|
|
3803
3808
|
this.devStandards = await fs4.readFile(devstandedPath, "utf-8");
|
|
3804
3809
|
} catch {
|
|
@@ -4086,10 +4091,10 @@ var WorkflowEngine = class {
|
|
|
4086
4091
|
await this.createSpecDocument(summary);
|
|
4087
4092
|
await this.updateChangeRecord("archived");
|
|
4088
4093
|
await this.saveState();
|
|
4089
|
-
const changesDir =
|
|
4090
|
-
const archiveDir =
|
|
4091
|
-
const changeFile =
|
|
4092
|
-
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`);
|
|
4093
4098
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
4094
4099
|
await fs4.rename(changeFile, archiveFile).catch(() => {
|
|
4095
4100
|
});
|
|
@@ -4113,14 +4118,14 @@ var WorkflowEngine = class {
|
|
|
4113
4118
|
}
|
|
4114
4119
|
// ==================== 私有方法 ====================
|
|
4115
4120
|
async ensureDirectories() {
|
|
4116
|
-
const changesDir =
|
|
4117
|
-
const archiveDir =
|
|
4118
|
-
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");
|
|
4119
4124
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
4120
4125
|
await fs4.mkdir(specDir, { recursive: true });
|
|
4121
4126
|
}
|
|
4122
4127
|
async restoreState() {
|
|
4123
|
-
const statePath =
|
|
4128
|
+
const statePath = path5.join(this.openspecPath, ".workflow-state.json");
|
|
4124
4129
|
try {
|
|
4125
4130
|
const content = await fs4.readFile(statePath, "utf-8");
|
|
4126
4131
|
this.state = JSON.parse(content, (key, value) => {
|
|
@@ -4140,11 +4145,11 @@ var WorkflowEngine = class {
|
|
|
4140
4145
|
}
|
|
4141
4146
|
}
|
|
4142
4147
|
async saveState() {
|
|
4143
|
-
const statePath =
|
|
4148
|
+
const statePath = path5.join(this.openspecPath, ".workflow-state.json");
|
|
4144
4149
|
await fs4.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
4145
4150
|
}
|
|
4146
4151
|
async restoreSnapshots() {
|
|
4147
|
-
const snapshotsPath =
|
|
4152
|
+
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
4148
4153
|
try {
|
|
4149
4154
|
const content = await fs4.readFile(snapshotsPath, "utf-8");
|
|
4150
4155
|
const data = JSON.parse(content, (key, value) => {
|
|
@@ -4161,7 +4166,7 @@ var WorkflowEngine = class {
|
|
|
4161
4166
|
}
|
|
4162
4167
|
}
|
|
4163
4168
|
async saveSnapshots() {
|
|
4164
|
-
const snapshotsPath =
|
|
4169
|
+
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
4165
4170
|
const data = Array.from(this.snapshots.values());
|
|
4166
4171
|
await fs4.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
4167
4172
|
}
|
|
@@ -4183,7 +4188,7 @@ var WorkflowEngine = class {
|
|
|
4183
4188
|
}
|
|
4184
4189
|
async createChangeRecord() {
|
|
4185
4190
|
if (!this.state) return;
|
|
4186
|
-
const changePath =
|
|
4191
|
+
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4187
4192
|
await fs4.writeFile(changePath, this.formatChangeRecord());
|
|
4188
4193
|
}
|
|
4189
4194
|
async updateChangeRecord(status) {
|
|
@@ -4191,7 +4196,7 @@ var WorkflowEngine = class {
|
|
|
4191
4196
|
if (status) {
|
|
4192
4197
|
this.state.status = status;
|
|
4193
4198
|
}
|
|
4194
|
-
const changePath =
|
|
4199
|
+
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
4195
4200
|
await fs4.writeFile(changePath, this.formatChangeRecord());
|
|
4196
4201
|
}
|
|
4197
4202
|
formatChangeRecord() {
|
|
@@ -4231,7 +4236,7 @@ ${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
|
4231
4236
|
}
|
|
4232
4237
|
async createSpecDocument(summary) {
|
|
4233
4238
|
if (!this.state) return;
|
|
4234
|
-
const specPath =
|
|
4239
|
+
const specPath = path5.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
4235
4240
|
const content = `# Spec: ${this.state.title}
|
|
4236
4241
|
|
|
4237
4242
|
> \u53D8\u66F4ID: ${this.state.id}
|
|
@@ -4329,7 +4334,7 @@ var NormsManager = class {
|
|
|
4329
4334
|
* 从单个文件学习规范
|
|
4330
4335
|
*/
|
|
4331
4336
|
async learnFromFile(filePath, content) {
|
|
4332
|
-
const ext =
|
|
4337
|
+
const ext = path5.extname(filePath);
|
|
4333
4338
|
const patterns = this.extractPatterns(filePath, content, ext);
|
|
4334
4339
|
for (const pattern of patterns) {
|
|
4335
4340
|
await this.recordOccurrence({
|
|
@@ -4936,12 +4941,12 @@ ${response}`;
|
|
|
4936
4941
|
for (const entry of entries) {
|
|
4937
4942
|
if (entry.isDirectory()) {
|
|
4938
4943
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
4939
|
-
await scan(
|
|
4944
|
+
await scan(path5.join(dir, entry.name));
|
|
4940
4945
|
}
|
|
4941
4946
|
} else if (entry.isFile()) {
|
|
4942
|
-
const ext =
|
|
4947
|
+
const ext = path5.extname(entry.name);
|
|
4943
4948
|
if (SCAN_EXTENSIONS.includes(ext)) {
|
|
4944
|
-
files.push(
|
|
4949
|
+
files.push(path5.join(dir, entry.name));
|
|
4945
4950
|
}
|
|
4946
4951
|
}
|
|
4947
4952
|
}
|
|
@@ -4976,13 +4981,13 @@ ${response}`;
|
|
|
4976
4981
|
*/
|
|
4977
4982
|
async ensureNormsDir() {
|
|
4978
4983
|
await fs4.mkdir(this.config.normsDir, { recursive: true });
|
|
4979
|
-
await fs4.mkdir(
|
|
4984
|
+
await fs4.mkdir(path5.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
4980
4985
|
}
|
|
4981
4986
|
/**
|
|
4982
4987
|
* 加载已有规范
|
|
4983
4988
|
*/
|
|
4984
4989
|
async loadStandards() {
|
|
4985
|
-
const standardsPath =
|
|
4990
|
+
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
4986
4991
|
try {
|
|
4987
4992
|
const content = await fs4.readFile(standardsPath, "utf-8");
|
|
4988
4993
|
const data = JSON.parse(content);
|
|
@@ -5000,7 +5005,7 @@ ${response}`;
|
|
|
5000
5005
|
* 加载权重数据
|
|
5001
5006
|
*/
|
|
5002
5007
|
async loadWeights() {
|
|
5003
|
-
const weightsPath =
|
|
5008
|
+
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
5004
5009
|
try {
|
|
5005
5010
|
const content = await fs4.readFile(weightsPath, "utf-8");
|
|
5006
5011
|
const data = JSON.parse(content);
|
|
@@ -5017,7 +5022,7 @@ ${response}`;
|
|
|
5017
5022
|
* 保存规范
|
|
5018
5023
|
*/
|
|
5019
5024
|
async saveStandards() {
|
|
5020
|
-
const standardsPath =
|
|
5025
|
+
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
5021
5026
|
await fs4.writeFile(
|
|
5022
5027
|
standardsPath,
|
|
5023
5028
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
@@ -5028,7 +5033,7 @@ ${response}`;
|
|
|
5028
5033
|
* 保存权重
|
|
5029
5034
|
*/
|
|
5030
5035
|
async saveWeights() {
|
|
5031
|
-
const weightsPath =
|
|
5036
|
+
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
5032
5037
|
await fs4.writeFile(
|
|
5033
5038
|
weightsPath,
|
|
5034
5039
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
@@ -5056,7 +5061,7 @@ ${response}`;
|
|
|
5056
5061
|
* 保存周报
|
|
5057
5062
|
*/
|
|
5058
5063
|
async saveWeeklyReport(weekId, report) {
|
|
5059
|
-
const reportPath =
|
|
5064
|
+
const reportPath = path5.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
5060
5065
|
await fs4.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
5061
5066
|
}
|
|
5062
5067
|
/**
|
|
@@ -5065,7 +5070,7 @@ ${response}`;
|
|
|
5065
5070
|
async updateDevStandedMd() {
|
|
5066
5071
|
const standards = this.getEffectiveStandards();
|
|
5067
5072
|
const content = this.generateDevStandedMd(standards);
|
|
5068
|
-
const mdPath =
|
|
5073
|
+
const mdPath = path5.join(this.config.normsDir, "devstanded.md");
|
|
5069
5074
|
await fs4.writeFile(mdPath, content, "utf-8");
|
|
5070
5075
|
}
|
|
5071
5076
|
/**
|
|
@@ -5213,7 +5218,7 @@ var ContextManager = class {
|
|
|
5213
5218
|
}
|
|
5214
5219
|
async initialize(projectPath) {
|
|
5215
5220
|
this.projectPath = projectPath;
|
|
5216
|
-
this.persistPath =
|
|
5221
|
+
this.persistPath = path5.join(projectPath, ".sf-cli", "cache", "context", "context.json");
|
|
5217
5222
|
await this.loadPersistedContext();
|
|
5218
5223
|
}
|
|
5219
5224
|
/**
|
|
@@ -5454,7 +5459,7 @@ ${summary}`,
|
|
|
5454
5459
|
*/
|
|
5455
5460
|
async persist() {
|
|
5456
5461
|
if (!this.persistPath) return;
|
|
5457
|
-
const dir =
|
|
5462
|
+
const dir = path5.dirname(this.persistPath);
|
|
5458
5463
|
await fs4.mkdir(dir, { recursive: true });
|
|
5459
5464
|
const data = {
|
|
5460
5465
|
messages: this.state.messages,
|
|
@@ -5589,8 +5594,8 @@ var CommandParser = class {
|
|
|
5589
5594
|
};
|
|
5590
5595
|
}
|
|
5591
5596
|
parseAtPath(input) {
|
|
5592
|
-
const
|
|
5593
|
-
if (!
|
|
5597
|
+
const path14 = input.slice(1).trim();
|
|
5598
|
+
if (!path14) {
|
|
5594
5599
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
5595
5600
|
}
|
|
5596
5601
|
return {
|
|
@@ -5598,7 +5603,7 @@ var CommandParser = class {
|
|
|
5598
5603
|
command: {
|
|
5599
5604
|
type: "at" /* AT */,
|
|
5600
5605
|
raw: input,
|
|
5601
|
-
path:
|
|
5606
|
+
path: path14
|
|
5602
5607
|
}
|
|
5603
5608
|
};
|
|
5604
5609
|
}
|
|
@@ -5654,9 +5659,9 @@ async function initProject(options = {}, workingDir) {
|
|
|
5654
5659
|
output: chalk9.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
5655
5660
|
};
|
|
5656
5661
|
}
|
|
5657
|
-
const sfCliDir =
|
|
5658
|
-
const openspecDir =
|
|
5659
|
-
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");
|
|
5660
5665
|
try {
|
|
5661
5666
|
const agentsExists = await fileExists(agentsMdPath);
|
|
5662
5667
|
if (agentsExists && !options.force) {
|
|
@@ -5716,7 +5721,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
5716
5721
|
"logs"
|
|
5717
5722
|
];
|
|
5718
5723
|
for (const dir of dirs) {
|
|
5719
|
-
await fs4.mkdir(
|
|
5724
|
+
await fs4.mkdir(path5.join(basePath, dir), { recursive: true });
|
|
5720
5725
|
}
|
|
5721
5726
|
const config = {
|
|
5722
5727
|
version: "1.0.0",
|
|
@@ -5725,36 +5730,36 @@ async function createSfCliDirectory(basePath) {
|
|
|
5725
5730
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5726
5731
|
};
|
|
5727
5732
|
await fs4.writeFile(
|
|
5728
|
-
|
|
5733
|
+
path5.join(basePath, "config.json"),
|
|
5729
5734
|
JSON.stringify(config, null, 2),
|
|
5730
5735
|
"utf-8"
|
|
5731
5736
|
);
|
|
5732
5737
|
await fs4.writeFile(
|
|
5733
|
-
|
|
5738
|
+
path5.join(basePath, "agents", "registry.json"),
|
|
5734
5739
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
5735
5740
|
"utf-8"
|
|
5736
5741
|
);
|
|
5737
5742
|
await fs4.writeFile(
|
|
5738
|
-
|
|
5743
|
+
path5.join(basePath, "skills", "registry.json"),
|
|
5739
5744
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
5740
5745
|
"utf-8"
|
|
5741
5746
|
);
|
|
5742
5747
|
await fs4.writeFile(
|
|
5743
|
-
|
|
5748
|
+
path5.join(basePath, "health", "health.md"),
|
|
5744
5749
|
generateHealthTemplate(),
|
|
5745
5750
|
"utf-8"
|
|
5746
5751
|
);
|
|
5747
5752
|
}
|
|
5748
5753
|
async function createOpenSpecDirectory(basePath) {
|
|
5749
|
-
const changesDir =
|
|
5750
|
-
const archiveDir =
|
|
5751
|
-
const specDir =
|
|
5754
|
+
const changesDir = path5.join(basePath, "changes");
|
|
5755
|
+
const archiveDir = path5.join(changesDir, "archive");
|
|
5756
|
+
const specDir = path5.join(basePath, "spec");
|
|
5752
5757
|
await fs4.mkdir(archiveDir, { recursive: true });
|
|
5753
5758
|
await fs4.mkdir(specDir, { recursive: true });
|
|
5754
5759
|
}
|
|
5755
5760
|
async function analyzeProject(cwd) {
|
|
5756
5761
|
const result = {
|
|
5757
|
-
name:
|
|
5762
|
+
name: path5.basename(cwd),
|
|
5758
5763
|
type: "unknown",
|
|
5759
5764
|
framework: null,
|
|
5760
5765
|
techStack: [],
|
|
@@ -5773,7 +5778,7 @@ async function analyzeProject(cwd) {
|
|
|
5773
5778
|
hasEslint: false,
|
|
5774
5779
|
hasPrettier: false
|
|
5775
5780
|
};
|
|
5776
|
-
const pkgPath =
|
|
5781
|
+
const pkgPath = path5.join(cwd, "package.json");
|
|
5777
5782
|
try {
|
|
5778
5783
|
const pkgContent = await fs4.readFile(pkgPath, "utf-8");
|
|
5779
5784
|
const pkg = JSON.parse(pkgContent);
|
|
@@ -5894,7 +5899,7 @@ async function findEntryPoints(cwd) {
|
|
|
5894
5899
|
"index.js"
|
|
5895
5900
|
];
|
|
5896
5901
|
for (const entry of possibleEntries) {
|
|
5897
|
-
const fullPath =
|
|
5902
|
+
const fullPath = path5.join(cwd, entry);
|
|
5898
5903
|
if (await fileExists(fullPath)) {
|
|
5899
5904
|
entryPoints.push(entry);
|
|
5900
5905
|
}
|
|
@@ -5902,7 +5907,7 @@ async function findEntryPoints(cwd) {
|
|
|
5902
5907
|
return entryPoints;
|
|
5903
5908
|
}
|
|
5904
5909
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
5905
|
-
const analysisPath =
|
|
5910
|
+
const analysisPath = path5.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
5906
5911
|
await fs4.writeFile(
|
|
5907
5912
|
analysisPath,
|
|
5908
5913
|
JSON.stringify(analysis, null, 2),
|
|
@@ -5910,7 +5915,7 @@ async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
|
5910
5915
|
);
|
|
5911
5916
|
}
|
|
5912
5917
|
async function generateOpenSpecConfig(openspecDir, analysis) {
|
|
5913
|
-
const configPath =
|
|
5918
|
+
const configPath = path5.join(openspecDir, "config.yaml");
|
|
5914
5919
|
const content = `# OpenSpec \u9879\u76EE\u914D\u7F6E
|
|
5915
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
|
|
5916
5921
|
|
|
@@ -6442,8 +6447,10 @@ function createSpinner(message) {
|
|
|
6442
6447
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
6443
6448
|
};
|
|
6444
6449
|
}
|
|
6445
|
-
var
|
|
6446
|
-
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;
|
|
6447
6454
|
async function handleUpdate(args, ctx) {
|
|
6448
6455
|
const options = {
|
|
6449
6456
|
check: args.includes("--check") || args.includes("-c"),
|
|
@@ -6470,6 +6477,9 @@ async function checkForUpdates() {
|
|
|
6470
6477
|
const latestVersion = await getLatestVersion();
|
|
6471
6478
|
if (!latestVersion) {
|
|
6472
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"));
|
|
6473
6483
|
return { output: lines.join("\n") };
|
|
6474
6484
|
}
|
|
6475
6485
|
lines.push(chalk9.gray(` \u5F53\u524D\u7248\u672C: v${CURRENT_VERSION}`));
|
|
@@ -6488,43 +6498,70 @@ async function checkForUpdates() {
|
|
|
6488
6498
|
async function performUpdate(targetVersion) {
|
|
6489
6499
|
const lines = [];
|
|
6490
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}`));
|
|
6491
6503
|
try {
|
|
6492
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
|
+
}
|
|
6493
6514
|
if (latestVersion === CURRENT_VERSION && !targetVersion) {
|
|
6494
|
-
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"));
|
|
6495
6516
|
return { output: lines.join("\n") };
|
|
6496
6517
|
}
|
|
6497
6518
|
const packageSpec = targetVersion ? `${PACKAGE_NAME}@${targetVersion}` : `${PACKAGE_NAME}@latest`;
|
|
6498
6519
|
lines.push(chalk9.gray(` \u5B89\u88C5: ${packageSpec}`));
|
|
6499
|
-
await new Promise((
|
|
6520
|
+
const installResult = await new Promise((resolve4) => {
|
|
6500
6521
|
const proc = spawn("npm", ["install", "-g", packageSpec], {
|
|
6501
6522
|
stdio: "pipe",
|
|
6502
6523
|
shell: true
|
|
6503
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
|
+
});
|
|
6504
6532
|
proc.on("close", (code) => {
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
}
|
|
6533
|
+
resolve4({
|
|
6534
|
+
success: code === 0,
|
|
6535
|
+
output
|
|
6536
|
+
});
|
|
6510
6537
|
});
|
|
6511
6538
|
proc.on("error", (err) => {
|
|
6512
|
-
|
|
6539
|
+
resolve4({
|
|
6540
|
+
success: false,
|
|
6541
|
+
output: err.message
|
|
6542
|
+
});
|
|
6513
6543
|
});
|
|
6514
6544
|
});
|
|
6515
|
-
|
|
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!"));
|
|
6516
6553
|
lines.push(chalk9.gray(` \u65B0\u7248\u672C: v${targetVersion || latestVersion}`));
|
|
6517
6554
|
lines.push(chalk9.gray("\n\u8BF7\u91CD\u542F CLI \u4EE5\u4F7F\u7528\u65B0\u7248\u672C"));
|
|
6518
6555
|
} catch (error) {
|
|
6519
6556
|
lines.push(chalk9.red("\u66F4\u65B0\u5931\u8D25: " + error.message));
|
|
6520
6557
|
lines.push(chalk9.gray("\n\u60A8\u53EF\u4EE5\u5C1D\u8BD5\u624B\u52A8\u66F4\u65B0:"));
|
|
6521
|
-
lines.push(chalk9.
|
|
6558
|
+
lines.push(chalk9.cyan(` npm install -g ${PACKAGE_NAME}@latest`));
|
|
6522
6559
|
}
|
|
6523
6560
|
return { output: lines.join("\n") };
|
|
6524
6561
|
}
|
|
6525
6562
|
async function getLatestVersion() {
|
|
6526
6563
|
try {
|
|
6527
|
-
const result = await new Promise((
|
|
6564
|
+
const result = await new Promise((resolve4, reject) => {
|
|
6528
6565
|
const proc = spawn("npm", ["view", PACKAGE_NAME, "version"], {
|
|
6529
6566
|
stdio: "pipe",
|
|
6530
6567
|
shell: true
|
|
@@ -6534,13 +6571,17 @@ async function getLatestVersion() {
|
|
|
6534
6571
|
output += data.toString();
|
|
6535
6572
|
});
|
|
6536
6573
|
proc.on("close", (code) => {
|
|
6537
|
-
if (code === 0) {
|
|
6538
|
-
|
|
6574
|
+
if (code === 0 && output.trim()) {
|
|
6575
|
+
resolve4(output.trim());
|
|
6539
6576
|
} else {
|
|
6540
6577
|
reject(new Error("Failed to get version"));
|
|
6541
6578
|
}
|
|
6542
6579
|
});
|
|
6543
6580
|
proc.on("error", reject);
|
|
6581
|
+
setTimeout(() => {
|
|
6582
|
+
proc.kill();
|
|
6583
|
+
reject(new Error("Timeout"));
|
|
6584
|
+
}, 1e4);
|
|
6544
6585
|
});
|
|
6545
6586
|
return result || null;
|
|
6546
6587
|
} catch {
|
|
@@ -6622,15 +6663,32 @@ var MAX_FILE_SIZE2 = 1024 * 1024;
|
|
|
6622
6663
|
var COMPLEXITY_THRESHOLD = 6;
|
|
6623
6664
|
async function handleNew(args, ctx) {
|
|
6624
6665
|
const workingDir = ctx.options.workingDirectory;
|
|
6666
|
+
const workflowEngine = ctx.workflowEngine;
|
|
6667
|
+
if (workflowEngine) {
|
|
6668
|
+
const existingState = workflowEngine.getState();
|
|
6669
|
+
if (existingState && existingState.status === "running") {
|
|
6670
|
+
return {
|
|
6671
|
+
output: chalk9.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray(`
|
|
6672
|
+
|
|
6673
|
+
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9.gray(`
|
|
6674
|
+
\u5F53\u524D\u9636\u6BB5: ${existingState.currentStep}`) + chalk9.gray(`
|
|
6675
|
+
|
|
6676
|
+
\u9009\u9879:`) + chalk9.gray(`
|
|
6677
|
+
1. \u7EE7\u7EED\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:${existingState.currentStep}`) + chalk9.gray(`
|
|
6678
|
+
2. \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41: /opsx:cancel`) + chalk9.gray(`
|
|
6679
|
+
3. \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001: /opsx:status`)
|
|
6680
|
+
};
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6625
6683
|
const { requirement, forceComplexity } = parseArgs(args);
|
|
6626
6684
|
if (!requirement) {
|
|
6627
6685
|
return {
|
|
6628
6686
|
output: chalk9.red("\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0") + chalk9.gray("\n\u7528\u6CD5: /new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray("\n\u9009\u9879:") + chalk9.gray("\n --simple \u5F3A\u5236\u4F7F\u7528\u7B80\u5355\u6D41\u7A0B") + chalk9.gray("\n --complex \u5F3A\u5236\u4F7F\u7528\u590D\u6742\u6D41\u7A0B")
|
|
6629
6687
|
};
|
|
6630
6688
|
}
|
|
6631
|
-
return newFeature({ requirement, forceComplexity }, workingDir);
|
|
6689
|
+
return newFeature({ requirement, forceComplexity }, workingDir, workflowEngine);
|
|
6632
6690
|
}
|
|
6633
|
-
async function newFeature(options, workingDir) {
|
|
6691
|
+
async function newFeature(options, workingDir, workflowEngine) {
|
|
6634
6692
|
const cwd = workingDir || process.cwd();
|
|
6635
6693
|
const { requirement, forceComplexity } = options;
|
|
6636
6694
|
try {
|
|
@@ -6648,8 +6706,10 @@ async function newFeature(options, workingDir) {
|
|
|
6648
6706
|
try {
|
|
6649
6707
|
const context = await readProjectContext(cwd);
|
|
6650
6708
|
const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
|
|
6651
|
-
const workflow = new WorkflowEngine();
|
|
6652
|
-
|
|
6709
|
+
const workflow = workflowEngine || new WorkflowEngine();
|
|
6710
|
+
if (!workflowEngine) {
|
|
6711
|
+
await workflow.initialize(cwd);
|
|
6712
|
+
}
|
|
6653
6713
|
const state = await workflow.start(requirement, analysis.score, {
|
|
6654
6714
|
title: extractTitle(requirement)
|
|
6655
6715
|
});
|
|
@@ -6703,13 +6763,13 @@ function parseArgs(args) {
|
|
|
6703
6763
|
}
|
|
6704
6764
|
async function readProjectContext(cwd) {
|
|
6705
6765
|
const defaultContext = {
|
|
6706
|
-
name:
|
|
6766
|
+
name: path5.basename(cwd),
|
|
6707
6767
|
type: "unknown",
|
|
6708
6768
|
framework: null,
|
|
6709
6769
|
techStack: [],
|
|
6710
6770
|
description: ""
|
|
6711
6771
|
};
|
|
6712
|
-
const agentsPath =
|
|
6772
|
+
const agentsPath = path5.join(cwd, "AGENTS.md");
|
|
6713
6773
|
try {
|
|
6714
6774
|
const stats = await fs4.stat(agentsPath);
|
|
6715
6775
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
@@ -6724,7 +6784,7 @@ async function readProjectContext(cwd) {
|
|
|
6724
6784
|
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
6725
6785
|
}
|
|
6726
6786
|
}
|
|
6727
|
-
const configPath =
|
|
6787
|
+
const configPath = path5.join(cwd, "openspec", "config.yaml");
|
|
6728
6788
|
try {
|
|
6729
6789
|
const stats = await fs4.stat(configPath);
|
|
6730
6790
|
if (stats.size > MAX_FILE_SIZE2) {
|
|
@@ -7205,6 +7265,9 @@ ${generateConfirmationPrompt(e.point)}`) + chalk9.cyan(`
|
|
|
7205
7265
|
}
|
|
7206
7266
|
|
|
7207
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;
|
|
7208
7271
|
async function runSlashCommand(command, args, ctx) {
|
|
7209
7272
|
const normalizedCommand = normalizeCommand(command);
|
|
7210
7273
|
switch (normalizedCommand) {
|
|
@@ -7232,6 +7295,11 @@ async function runSlashCommand(command, args, ctx) {
|
|
|
7232
7295
|
case "new":
|
|
7233
7296
|
case "n":
|
|
7234
7297
|
return handleNew(args, ctx);
|
|
7298
|
+
case "version":
|
|
7299
|
+
case "v":
|
|
7300
|
+
return {
|
|
7301
|
+
output: chalk9.cyan(`sf-cli v${VERSION2}`)
|
|
7302
|
+
};
|
|
7235
7303
|
default:
|
|
7236
7304
|
if (normalizedCommand.startsWith("opsx:")) {
|
|
7237
7305
|
return handleOpsx(normalizedCommand, args, ctx);
|
|
@@ -7246,7 +7314,7 @@ function normalizeCommand(command) {
|
|
|
7246
7314
|
}
|
|
7247
7315
|
async function handleFileReference(filePath, ctx) {
|
|
7248
7316
|
const cwd = ctx.options.workingDirectory;
|
|
7249
|
-
const absolutePath =
|
|
7317
|
+
const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.join(cwd, filePath);
|
|
7250
7318
|
try {
|
|
7251
7319
|
const stats = await fs4.stat(absolutePath);
|
|
7252
7320
|
if (stats.isDirectory()) {
|
|
@@ -7294,7 +7362,7 @@ async function executeShell(command, ctx) {
|
|
|
7294
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")
|
|
7295
7363
|
};
|
|
7296
7364
|
}
|
|
7297
|
-
return new Promise((
|
|
7365
|
+
return new Promise((resolve4) => {
|
|
7298
7366
|
const shell = spawn(command, [], {
|
|
7299
7367
|
shell: true,
|
|
7300
7368
|
cwd: ctx.options.workingDirectory
|
|
@@ -7319,11 +7387,11 @@ async function executeShell(command, ctx) {
|
|
|
7319
7387
|
output += chalk9.red(`
|
|
7320
7388
|
\u9000\u51FA\u7801: ${code}`);
|
|
7321
7389
|
}
|
|
7322
|
-
|
|
7390
|
+
resolve4({ output: output || chalk9.gray("(\u65E0\u8F93\u51FA)") });
|
|
7323
7391
|
});
|
|
7324
7392
|
setTimeout(() => {
|
|
7325
7393
|
shell.kill();
|
|
7326
|
-
|
|
7394
|
+
resolve4({
|
|
7327
7395
|
output: chalk9.yellow("\u547D\u4EE4\u6267\u884C\u8D85\u65F6\uFF0C\u5DF2\u7EC8\u6B62")
|
|
7328
7396
|
});
|
|
7329
7397
|
}, 6e4);
|
|
@@ -7342,12 +7410,75 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
7342
7410
|
}
|
|
7343
7411
|
|
|
7344
7412
|
// src/cli/executor.ts
|
|
7413
|
+
var ALLOWED_COMMANDS_WITHOUT_WORKFLOW = [
|
|
7414
|
+
"help",
|
|
7415
|
+
"h",
|
|
7416
|
+
"?",
|
|
7417
|
+
"init",
|
|
7418
|
+
"i",
|
|
7419
|
+
"model",
|
|
7420
|
+
"m",
|
|
7421
|
+
"new",
|
|
7422
|
+
"n",
|
|
7423
|
+
"exit",
|
|
7424
|
+
"e",
|
|
7425
|
+
"q",
|
|
7426
|
+
"quit",
|
|
7427
|
+
"clear",
|
|
7428
|
+
"c",
|
|
7429
|
+
"update",
|
|
7430
|
+
"u",
|
|
7431
|
+
"version",
|
|
7432
|
+
"v"
|
|
7433
|
+
];
|
|
7434
|
+
var STAGE_PERMISSIONS = {
|
|
7435
|
+
"explore": {
|
|
7436
|
+
canRead: true,
|
|
7437
|
+
canWrite: false,
|
|
7438
|
+
canRunShell: false,
|
|
7439
|
+
allowedAgents: ["architect"]
|
|
7440
|
+
},
|
|
7441
|
+
"new": {
|
|
7442
|
+
canRead: true,
|
|
7443
|
+
canWrite: true,
|
|
7444
|
+
canRunShell: false,
|
|
7445
|
+
allowedAgents: ["frontend-dev", "architect"]
|
|
7446
|
+
},
|
|
7447
|
+
"continue": {
|
|
7448
|
+
canRead: true,
|
|
7449
|
+
canWrite: true,
|
|
7450
|
+
canRunShell: true,
|
|
7451
|
+
allowedAgents: ["frontend-dev", "tester"]
|
|
7452
|
+
},
|
|
7453
|
+
"propose": {
|
|
7454
|
+
canRead: true,
|
|
7455
|
+
canWrite: true,
|
|
7456
|
+
canRunShell: true,
|
|
7457
|
+
allowedAgents: ["frontend-dev"]
|
|
7458
|
+
},
|
|
7459
|
+
"apply": {
|
|
7460
|
+
canRead: true,
|
|
7461
|
+
canWrite: true,
|
|
7462
|
+
canRunShell: true,
|
|
7463
|
+
allowedAgents: ["code-reviewer"]
|
|
7464
|
+
},
|
|
7465
|
+
"archive": {
|
|
7466
|
+
canRead: true,
|
|
7467
|
+
canWrite: false,
|
|
7468
|
+
canRunShell: false,
|
|
7469
|
+
allowedAgents: []
|
|
7470
|
+
}
|
|
7471
|
+
};
|
|
7345
7472
|
var CommandExecutor = class {
|
|
7346
7473
|
async execute(parseResult, ctx) {
|
|
7347
7474
|
if (!parseResult.success || !parseResult.command) {
|
|
7348
7475
|
return { output: chalk9.red(`\u9519\u8BEF: ${parseResult.error}`) };
|
|
7349
7476
|
}
|
|
7350
7477
|
const { command } = parseResult;
|
|
7478
|
+
const workflowCheck = this.checkWorkflowPermission(command, ctx);
|
|
7479
|
+
if (!workflowCheck.allowed) {
|
|
7480
|
+
return { output: workflowCheck.message };
|
|
7481
|
+
}
|
|
7351
7482
|
switch (command.type) {
|
|
7352
7483
|
case "slash" /* SLASH */:
|
|
7353
7484
|
return this.executeSlashCommand(command, ctx);
|
|
@@ -7365,13 +7496,75 @@ var CommandExecutor = class {
|
|
|
7365
7496
|
return { output: chalk9.red("\u672A\u77E5\u7684\u547D\u4EE4\u7C7B\u578B") };
|
|
7366
7497
|
}
|
|
7367
7498
|
}
|
|
7499
|
+
/**
|
|
7500
|
+
* 检查工作流权限
|
|
7501
|
+
*/
|
|
7502
|
+
checkWorkflowPermission(command, ctx) {
|
|
7503
|
+
const workflowEngine = ctx.workflowEngine;
|
|
7504
|
+
const workflowState = workflowEngine?.getState();
|
|
7505
|
+
if (!workflowState) {
|
|
7506
|
+
if (command.type === "slash" /* SLASH */) {
|
|
7507
|
+
const cmd = command.command?.toLowerCase();
|
|
7508
|
+
if (!ALLOWED_COMMANDS_WITHOUT_WORKFLOW.includes(cmd)) {
|
|
7509
|
+
return {
|
|
7510
|
+
allowed: false,
|
|
7511
|
+
message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
7512
|
+
};
|
|
7513
|
+
}
|
|
7514
|
+
}
|
|
7515
|
+
if (command.type === "natural" /* NATURAL */) {
|
|
7516
|
+
return {
|
|
7517
|
+
allowed: false,
|
|
7518
|
+
message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
7519
|
+
};
|
|
7520
|
+
}
|
|
7521
|
+
if (command.type === "dollar" /* DOLLAR */) {
|
|
7522
|
+
return {
|
|
7523
|
+
allowed: false,
|
|
7524
|
+
message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
7525
|
+
};
|
|
7526
|
+
}
|
|
7527
|
+
if (command.type === "shell" /* SHELL */) {
|
|
7528
|
+
return {
|
|
7529
|
+
allowed: false,
|
|
7530
|
+
message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
7531
|
+
};
|
|
7532
|
+
}
|
|
7533
|
+
return { allowed: true };
|
|
7534
|
+
}
|
|
7535
|
+
const currentStep = workflowState.currentStep;
|
|
7536
|
+
const permissions = STAGE_PERMISSIONS[currentStep];
|
|
7537
|
+
if (command.type === "at" /* AT */ && !permissions.canRead) {
|
|
7538
|
+
return {
|
|
7539
|
+
allowed: false,
|
|
7540
|
+
message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8BFB\u53D6\u6587\u4EF6`)
|
|
7541
|
+
};
|
|
7542
|
+
}
|
|
7543
|
+
if (command.type === "dollar" /* DOLLAR */) {
|
|
7544
|
+
const agentId = command.agent?.toLowerCase().replace("$", "");
|
|
7545
|
+
if (!permissions.allowedAgents.includes(agentId)) {
|
|
7546
|
+
return {
|
|
7547
|
+
allowed: false,
|
|
7548
|
+
message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8C03\u7528 $${agentId} Agent`) + chalk9.gray(`
|
|
7549
|
+
\u5F53\u524D\u9636\u6BB5\u5141\u8BB8\u7684 Agent: ${permissions.allowedAgents.map((a) => `$${a}`).join(", ") || "\u65E0"}`)
|
|
7550
|
+
};
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7553
|
+
if (command.type === "shell" /* SHELL */ && !permissions.canRunShell) {
|
|
7554
|
+
return {
|
|
7555
|
+
allowed: false,
|
|
7556
|
+
message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u6267\u884C Shell \u547D\u4EE4`)
|
|
7557
|
+
};
|
|
7558
|
+
}
|
|
7559
|
+
return { allowed: true };
|
|
7560
|
+
}
|
|
7368
7561
|
async executeSlashCommand(command, ctx) {
|
|
7369
7562
|
const result = await runSlashCommand(
|
|
7370
7563
|
command.command,
|
|
7371
7564
|
command.args || [],
|
|
7372
7565
|
ctx
|
|
7373
7566
|
);
|
|
7374
|
-
return { output: result.output };
|
|
7567
|
+
return { output: result.output, exit: result.exit };
|
|
7375
7568
|
}
|
|
7376
7569
|
async executeFileReference(command, ctx) {
|
|
7377
7570
|
const result = await handleFileReference(command.path, ctx);
|
|
@@ -7505,7 +7698,7 @@ var Completer = class {
|
|
|
7505
7698
|
prefix = filePath;
|
|
7506
7699
|
} else {
|
|
7507
7700
|
const dir = filePath.slice(0, lastSlash);
|
|
7508
|
-
dirPath =
|
|
7701
|
+
dirPath = path5.resolve(this.workingDirectory, dir);
|
|
7509
7702
|
prefix = filePath.slice(lastSlash + 1);
|
|
7510
7703
|
}
|
|
7511
7704
|
try {
|
|
@@ -7515,7 +7708,7 @@ var Completer = class {
|
|
|
7515
7708
|
if (entry.name.startsWith(prefix)) {
|
|
7516
7709
|
const isDir = entry.isDirectory();
|
|
7517
7710
|
matches.push({
|
|
7518
|
-
text: "@" +
|
|
7711
|
+
text: "@" + path5.relative(this.workingDirectory, path5.join(dirPath, entry.name)).replace(/\\/g, "/") + (isDir ? "/" : ""),
|
|
7519
7712
|
displayText: entry.name + (isDir ? "/" : ""),
|
|
7520
7713
|
description: isDir ? "\u76EE\u5F55" : "\u6587\u4EF6",
|
|
7521
7714
|
type: isDir ? "directory" : "file"
|