@nick848/sf-cli 1.0.8 → 1.0.11
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 +39 -0
- package/dist/cli/index.js +3536 -3451
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +374 -382
- package/dist/index.d.ts +374 -382
- package/dist/index.js +1653 -1701
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1648 -1700
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var chalk9 = require('chalk');
|
|
3
4
|
var fs4 = require('fs/promises');
|
|
4
5
|
var path4 = require('path');
|
|
5
|
-
var chalk9 = require('chalk');
|
|
6
6
|
var fs10 = require('fs');
|
|
7
7
|
var crypto = require('crypto');
|
|
8
8
|
var os = require('os');
|
|
@@ -33,9 +33,9 @@ function _interopNamespace(e) {
|
|
|
33
33
|
return Object.freeze(n);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
36
37
|
var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
|
|
37
38
|
var path4__namespace = /*#__PURE__*/_interopNamespace(path4);
|
|
38
|
-
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
39
39
|
var fs10__namespace = /*#__PURE__*/_interopNamespace(fs10);
|
|
40
40
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
41
41
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
@@ -56,1536 +56,776 @@ var init_cjs_shims = __esm({
|
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
// src/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`;
|
|
73
|
-
prompt2 += ` r - \u8BF7\u6C42\u4FEE\u6539
|
|
74
|
-
`;
|
|
75
|
-
if (!point.required) {
|
|
76
|
-
prompt2 += `
|
|
77
|
-
\u63D0\u793A: \u6B64\u786E\u8BA4\u70B9\u4E3A\u975E\u5FC5\u987B\u9879`;
|
|
59
|
+
// src/commands/new.ts
|
|
60
|
+
var new_exports = {};
|
|
61
|
+
__export(new_exports, {
|
|
62
|
+
clearActiveSession: () => clearActiveSession,
|
|
63
|
+
default: () => new_default,
|
|
64
|
+
getActiveSession: () => getActiveSession,
|
|
65
|
+
handleNew: () => handleNew,
|
|
66
|
+
handleWorkflowInput: () => handleWorkflowInput
|
|
67
|
+
});
|
|
68
|
+
async function handleNew(args, ctx) {
|
|
69
|
+
ctx.options.workingDirectory;
|
|
70
|
+
if (activeSession) {
|
|
71
|
+
return handleActiveSession();
|
|
78
72
|
}
|
|
79
|
-
|
|
73
|
+
const requirement = args.join(" ").trim();
|
|
74
|
+
if (!requirement) {
|
|
75
|
+
return {
|
|
76
|
+
output: chalk9__default.default.red("\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0") + chalk9__default.default.gray("\n\u7528\u6CD5: /new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray("\n\n\u793A\u4F8B:") + chalk9__default.default.gray("\n /new \u5B9E\u73B0\u7528\u6237\u767B\u5F55\u529F\u80FD") + chalk9__default.default.gray("\n /new \u6DFB\u52A0\u6570\u636E\u5BFC\u51FA\u4E3AExcel\u7684\u529F\u80FD")
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
activeSession = {
|
|
80
|
+
id: generateSessionId(),
|
|
81
|
+
requirement,
|
|
82
|
+
refinedRequirement: requirement,
|
|
83
|
+
phase: "context",
|
|
84
|
+
context: null,
|
|
85
|
+
clarityScore: 0,
|
|
86
|
+
clarificationQuestions: [],
|
|
87
|
+
complexity: 0,
|
|
88
|
+
bddScenarios: [],
|
|
89
|
+
specItems: [],
|
|
90
|
+
testFiles: [],
|
|
91
|
+
implFiles: [],
|
|
92
|
+
reviewPassed: false,
|
|
93
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
94
|
+
};
|
|
95
|
+
return executeWorkflow(ctx);
|
|
80
96
|
}
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return `\u5F53\u524D\u9636\u6BB5 ${fromStep} \u4E0D\u652F\u6301\u56DE\u6EDA`;
|
|
97
|
+
function handleActiveSession(ctx) {
|
|
98
|
+
if (!activeSession) {
|
|
99
|
+
return { output: chalk9__default.default.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
|
|
85
100
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
const lines = [];
|
|
102
|
+
lines.push(chalk9__default.default.cyan("\u{1F4CB} \u5F53\u524D\u5DE5\u4F5C\u6D41\u72B6\u6001"));
|
|
103
|
+
lines.push("");
|
|
104
|
+
lines.push(chalk9__default.default.white(`\u9700\u6C42: ${activeSession.requirement.slice(0, 60)}${activeSession.requirement.length > 60 ? "..." : ""}`));
|
|
105
|
+
lines.push(chalk9__default.default.gray(`\u9636\u6BB5: ${getPhaseLabel(activeSession.phase)}`));
|
|
106
|
+
lines.push("");
|
|
107
|
+
if (activeSession.phase === "clarify") {
|
|
108
|
+
lines.push(chalk9__default.default.yellow("\u23F8\uFE0F \u7B49\u5F85\u9700\u6C42\u6F84\u6E05"));
|
|
109
|
+
lines.push(chalk9__default.default.gray('\u8BF7\u56DE\u7B54\u4E0A\u8FF0\u95EE\u9898\uFF0C\u6216\u8F93\u5165 "done" \u8868\u793A\u56DE\u7B54\u5B8C\u6210'));
|
|
110
|
+
} else if (activeSession.phase === "spec") {
|
|
111
|
+
lines.push(chalk9__default.default.yellow("\u23F8\uFE0F \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
|
|
112
|
+
lines.push("");
|
|
113
|
+
lines.push(chalk9__default.default.green(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u7EE7\u7EED\u5DE5\u4F5C\u6D41"));
|
|
114
|
+
lines.push(chalk9__default.default.red(" n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210\u89C4\u683C"));
|
|
115
|
+
lines.push(chalk9__default.default.gray(" c - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41"));
|
|
116
|
+
} else {
|
|
117
|
+
lines.push(chalk9__default.default.yellow("\u5DE5\u4F5C\u6D41\u8FDB\u884C\u4E2D..."));
|
|
118
|
+
lines.push(chalk9__default.default.gray("\u8F93\u5165\u4EFB\u610F\u5185\u5BB9\u7EE7\u7EED"));
|
|
96
119
|
}
|
|
97
|
-
|
|
98
|
-
\u8BF7\u9009\u62E9\u56DE\u6EDA\u76EE\u6807\u9636\u6BB5`;
|
|
99
|
-
return prompt2;
|
|
120
|
+
return { output: lines.join("\n") };
|
|
100
121
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
init_cjs_shims();
|
|
105
|
-
DEFAULT_CONFIRMATION_POINTS = [
|
|
106
|
-
{
|
|
107
|
-
type: "spec-review",
|
|
108
|
-
name: "\u89C4\u683C\u786E\u8BA4",
|
|
109
|
-
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
110
|
-
triggerStep: "explore",
|
|
111
|
-
targetStep: "new",
|
|
112
|
-
required: true
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
type: "spec-review",
|
|
116
|
-
name: "\u89C4\u683C\u786E\u8BA4",
|
|
117
|
-
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
118
|
-
triggerStep: "propose",
|
|
119
|
-
targetStep: "apply",
|
|
120
|
-
required: true
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
type: "architecture",
|
|
124
|
-
name: "\u67B6\u6784\u8C03\u6574\u786E\u8BA4",
|
|
125
|
-
description: "\u8BBE\u8BA1\u65B9\u6848\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u67B6\u6784\u8BBE\u8BA1\u662F\u5426\u5408\u7406",
|
|
126
|
-
triggerStep: "new",
|
|
127
|
-
targetStep: "continue",
|
|
128
|
-
required: true
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
type: "code-review",
|
|
132
|
-
name: "\u4EE3\u7801\u5BA1\u67E5\u786E\u8BA4",
|
|
133
|
-
description: "\u4EE3\u7801\u5BA1\u67E5\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u53EF\u4EE5\u5F52\u6863",
|
|
134
|
-
triggerStep: "apply",
|
|
135
|
-
targetStep: "archive",
|
|
136
|
-
required: true
|
|
137
|
-
}
|
|
138
|
-
];
|
|
139
|
-
exports.ROLLBACK_RULES = {
|
|
140
|
-
"explore": [],
|
|
141
|
-
// 初始阶段不可回滚
|
|
142
|
-
"new": ["explore"],
|
|
143
|
-
// 可回滚到 explore
|
|
144
|
-
"continue": ["new", "explore"],
|
|
145
|
-
// 可回滚到 new 或 explore
|
|
146
|
-
"propose": ["propose"],
|
|
147
|
-
// 可重新生成规格(回滚到自身)
|
|
148
|
-
"apply": ["new", "explore", "propose"],
|
|
149
|
-
// 代码审查未通过,可回滚到 new/explore (复杂) 或 propose (简单)
|
|
150
|
-
"archive": []
|
|
151
|
-
// 已归档不可回滚
|
|
152
|
-
};
|
|
153
|
-
exports.ConfirmationManager = class {
|
|
154
|
-
confirmationPoints;
|
|
155
|
-
confirmations = /* @__PURE__ */ new Map();
|
|
156
|
-
constructor(customPoints) {
|
|
157
|
-
this.confirmationPoints = customPoints || DEFAULT_CONFIRMATION_POINTS;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* 获取指定阶段的确认点
|
|
161
|
-
*/
|
|
162
|
-
getConfirmationPointForTransition(from, to) {
|
|
163
|
-
return this.confirmationPoints.find(
|
|
164
|
-
(point) => point.triggerStep === from && point.targetStep === to
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* 检查是否需要确认
|
|
169
|
-
*/
|
|
170
|
-
needsConfirmation(from, to) {
|
|
171
|
-
const point = this.getConfirmationPointForTransition(from, to);
|
|
172
|
-
return point !== void 0 && point.required;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* 获取确认点详情
|
|
176
|
-
*/
|
|
177
|
-
getConfirmationPoint(type) {
|
|
178
|
-
return this.confirmationPoints.find((point) => point.type === type);
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* 记录确认
|
|
182
|
-
*/
|
|
183
|
-
confirm(type, comment) {
|
|
184
|
-
const status = {
|
|
185
|
-
type,
|
|
186
|
-
confirmed: true,
|
|
187
|
-
confirmedAt: /* @__PURE__ */ new Date(),
|
|
188
|
-
comment
|
|
189
|
-
};
|
|
190
|
-
this.confirmations.set(type, status);
|
|
191
|
-
return status;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* 获取确认状态
|
|
195
|
-
*/
|
|
196
|
-
getConfirmationStatus(type) {
|
|
197
|
-
return this.confirmations.get(type);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* 检查确认点是否已确认
|
|
201
|
-
*/
|
|
202
|
-
isConfirmed(type) {
|
|
203
|
-
const status = this.confirmations.get(type);
|
|
204
|
-
return status?.confirmed ?? false;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* 清除确认状态(用于回滚后)
|
|
208
|
-
*/
|
|
209
|
-
clearConfirmation(type) {
|
|
210
|
-
this.confirmations.delete(type);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* 清除所有确认状态
|
|
214
|
-
*/
|
|
215
|
-
clearAllConfirmations() {
|
|
216
|
-
this.confirmations.clear();
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* 获取所有确认点
|
|
220
|
-
*/
|
|
221
|
-
getAllConfirmationPoints() {
|
|
222
|
-
return [...this.confirmationPoints];
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* 获取指定阶段需要清除的确认点
|
|
226
|
-
*/
|
|
227
|
-
getConfirmationsToClear(targetStep) {
|
|
228
|
-
const types = [];
|
|
229
|
-
for (const point of this.confirmationPoints) {
|
|
230
|
-
const stepOrder = ["explore", "new", "continue", "propose", "apply", "archive"];
|
|
231
|
-
const targetIndex = stepOrder.indexOf(targetStep);
|
|
232
|
-
const triggerIndex = stepOrder.indexOf(point.triggerStep);
|
|
233
|
-
if (triggerIndex >= targetIndex) {
|
|
234
|
-
types.push(point.type);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return types;
|
|
238
|
-
}
|
|
239
|
-
};
|
|
122
|
+
async function executeWorkflow(ctx) {
|
|
123
|
+
if (!activeSession) {
|
|
124
|
+
return { output: chalk9__default.default.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
|
|
240
125
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
async initialize(projectPath) {
|
|
276
|
-
this.projectPath = projectPath;
|
|
277
|
-
this.openspecPath = path4__namespace.join(projectPath, "openspec");
|
|
278
|
-
await this.ensureDirectories();
|
|
279
|
-
await this.loadProjectContext();
|
|
280
|
-
await this.restoreState();
|
|
281
|
-
await this.restoreSnapshots();
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
285
|
-
*/
|
|
286
|
-
async loadProjectContext() {
|
|
287
|
-
const agentsMdPath = path4__namespace.join(this.projectPath, "AGENTS.md");
|
|
288
|
-
try {
|
|
289
|
-
this.projectContext = await fs4__namespace.readFile(agentsMdPath, "utf-8");
|
|
290
|
-
} catch {
|
|
291
|
-
this.projectContext = "";
|
|
292
|
-
}
|
|
293
|
-
const configPath = path4__namespace.join(this.openspecPath, "config.yaml");
|
|
294
|
-
try {
|
|
295
|
-
this.projectConfig = await fs4__namespace.readFile(configPath, "utf-8");
|
|
296
|
-
} catch {
|
|
297
|
-
this.projectConfig = "";
|
|
298
|
-
}
|
|
299
|
-
const devstandedPath = path4__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
300
|
-
try {
|
|
301
|
-
this.devStandards = await fs4__namespace.readFile(devstandedPath, "utf-8");
|
|
302
|
-
} catch {
|
|
303
|
-
this.devStandards = "";
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* 获取项目上下文
|
|
308
|
-
*/
|
|
309
|
-
getProjectContext() {
|
|
310
|
-
return {
|
|
311
|
-
agentsMd: this.projectContext,
|
|
312
|
-
configYaml: this.projectConfig,
|
|
313
|
-
devStandards: this.devStandards
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* 获取规格文件路径
|
|
318
|
-
*/
|
|
319
|
-
getSpecFilePath() {
|
|
320
|
-
if (!this.state) return null;
|
|
321
|
-
return path4__namespace.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* 检查规格文件是否存在
|
|
325
|
-
*/
|
|
326
|
-
async hasSpecFile() {
|
|
327
|
-
const specPath = this.getSpecFilePath();
|
|
328
|
-
if (!specPath) return false;
|
|
329
|
-
try {
|
|
330
|
-
await fs4__namespace.access(specPath);
|
|
331
|
-
return true;
|
|
332
|
-
} catch {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* 启动新工作流
|
|
338
|
-
*/
|
|
339
|
-
async start(requirement, complexity, options) {
|
|
340
|
-
const type = complexity >= 6 ? "complex" : "simple";
|
|
341
|
-
const steps = type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
342
|
-
const initialStep = steps[0];
|
|
343
|
-
const changeId = await this.generateChangeId();
|
|
344
|
-
this.state = {
|
|
345
|
-
id: changeId,
|
|
346
|
-
type,
|
|
347
|
-
currentStep: initialStep,
|
|
348
|
-
steps: steps.map((step, index) => ({
|
|
349
|
-
step,
|
|
350
|
-
status: index === 0 ? "running" : "pending",
|
|
351
|
-
startedAt: index === 0 ? /* @__PURE__ */ new Date() : void 0
|
|
352
|
-
})),
|
|
353
|
-
requirement,
|
|
354
|
-
complexity,
|
|
355
|
-
title: options?.title || requirement.slice(0, 50),
|
|
356
|
-
artifacts: [],
|
|
357
|
-
status: "running",
|
|
358
|
-
createdAt: /* @__PURE__ */ new Date()
|
|
359
|
-
};
|
|
360
|
-
this.snapshots.clear();
|
|
361
|
-
this.confirmationManager.clearAllConfirmations();
|
|
362
|
-
await this.createSnapshot();
|
|
363
|
-
await this.createChangeRecord();
|
|
364
|
-
await this.saveState();
|
|
365
|
-
return this.state;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* 检查转换是否需要确认
|
|
369
|
-
*/
|
|
370
|
-
checkConfirmationNeeded(from, to) {
|
|
371
|
-
const point = this.confirmationManager.getConfirmationPointForTransition(from, to);
|
|
372
|
-
if (!point) {
|
|
373
|
-
return { required: false, confirmed: true };
|
|
374
|
-
}
|
|
375
|
-
const confirmed = this.confirmationManager.isConfirmed(point.type);
|
|
376
|
-
return {
|
|
377
|
-
required: point.required,
|
|
378
|
-
confirmed,
|
|
379
|
-
point
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
/**
|
|
383
|
-
* 确认检查点
|
|
384
|
-
*/
|
|
385
|
-
confirm(type, comment) {
|
|
386
|
-
this.confirmationManager.confirm(type, comment);
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* 获取确认点信息
|
|
390
|
-
*/
|
|
391
|
-
getConfirmationPoint(type) {
|
|
392
|
-
return this.confirmationManager.getConfirmationPoint(type);
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* 获取当前转换需要的确认点
|
|
396
|
-
*/
|
|
397
|
-
getCurrentConfirmationPoint() {
|
|
398
|
-
if (!this.state) return void 0;
|
|
399
|
-
const allowed = this.getAllowedTransitions();
|
|
400
|
-
for (const target of allowed) {
|
|
401
|
-
const point = this.confirmationManager.getConfirmationPointForTransition(
|
|
402
|
-
this.state.currentStep,
|
|
403
|
-
target
|
|
404
|
-
);
|
|
405
|
-
if (point && !this.confirmationManager.isConfirmed(point.type)) {
|
|
406
|
-
return point;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
return void 0;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* 执行状态转换
|
|
413
|
-
*/
|
|
414
|
-
async transition(targetStep, reason) {
|
|
415
|
-
if (!this.state) {
|
|
416
|
-
throw new Error("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41");
|
|
417
|
-
}
|
|
418
|
-
const currentStep = this.state.currentStep;
|
|
419
|
-
const allowedTargets = TRANSITIONS[currentStep];
|
|
420
|
-
if (!allowedTargets.includes(targetStep)) {
|
|
421
|
-
throw new Error(
|
|
422
|
-
`\u65E0\u6548\u7684\u72B6\u6001\u8F6C\u6362: ${currentStep} \u2192 ${targetStep}\u3002\u5141\u8BB8\u7684\u8F6C\u6362: ${allowedTargets.join(", ")}`
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
const confirmationResult = this.checkConfirmationNeeded(currentStep, targetStep);
|
|
426
|
-
if (confirmationResult.required && !confirmationResult.confirmed) {
|
|
427
|
-
throw new exports.ConfirmationRequiredError(
|
|
428
|
-
`\u9700\u8981\u786E\u8BA4: ${confirmationResult.point?.name}`,
|
|
429
|
-
confirmationResult.point
|
|
430
|
-
);
|
|
431
|
-
}
|
|
432
|
-
const currentStepRecord = this.state.steps.find((s) => s.step === currentStep);
|
|
433
|
-
if (currentStepRecord) {
|
|
434
|
-
currentStepRecord.status = "completed";
|
|
435
|
-
currentStepRecord.completedAt = /* @__PURE__ */ new Date();
|
|
126
|
+
const lines = [];
|
|
127
|
+
try {
|
|
128
|
+
if (activeSession.phase === "context") {
|
|
129
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/8: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
|
|
130
|
+
lines.push("");
|
|
131
|
+
activeSession.context = await readProjectContext(ctx.options.workingDirectory);
|
|
132
|
+
lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
|
|
133
|
+
lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
|
|
134
|
+
lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
|
|
135
|
+
lines.push(chalk9__default.default.gray(` \u6280\u672F\u6808: ${activeSession.context.techStack.join(", ") || "\u672A\u8BC6\u522B"}`));
|
|
136
|
+
if (activeSession.context.devStandards) {
|
|
137
|
+
lines.push(chalk9__default.default.green(" \u2713 \u5DF2\u8BFB\u53D6\u5F00\u53D1\u89C4\u8303"));
|
|
138
|
+
}
|
|
139
|
+
activeSession.phase = "clarify";
|
|
140
|
+
}
|
|
141
|
+
if (activeSession.phase === "clarify") {
|
|
142
|
+
lines.push("");
|
|
143
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/8: \u9700\u6C42\u6F84\u6E05 \u2501\u2501\u2501"));
|
|
144
|
+
lines.push("");
|
|
145
|
+
const clarityResult = analyzeRequirementClarity(
|
|
146
|
+
activeSession.requirement,
|
|
147
|
+
activeSession.context
|
|
148
|
+
);
|
|
149
|
+
activeSession.clarityScore = clarityResult.score;
|
|
150
|
+
activeSession.clarificationQuestions = clarityResult.questions;
|
|
151
|
+
lines.push(chalk9__default.default.gray(` \u9700\u6C42\u6E05\u6670\u5EA6: ${Math.round(clarityResult.score * 100)}%`));
|
|
152
|
+
lines.push("");
|
|
153
|
+
if (clarityResult.score < CLARITY_THRESHOLD && clarityResult.questions.length > 0) {
|
|
154
|
+
lines.push(chalk9__default.default.yellow(" \u9700\u6C42\u4E0D\u591F\u660E\u786E\uFF0C\u8BF7\u8865\u5145\u4EE5\u4E0B\u4FE1\u606F\uFF1A"));
|
|
155
|
+
lines.push("");
|
|
156
|
+
const unansweredQuestions = clarityResult.questions.filter((q) => !q.answered);
|
|
157
|
+
for (let i = 0; i < Math.min(unansweredQuestions.length, 3); i++) {
|
|
158
|
+
const q = unansweredQuestions[i];
|
|
159
|
+
lines.push(chalk9__default.default.white(` ${i + 1}. ${q.question}`));
|
|
436
160
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
targetStepRecord.status = "running";
|
|
440
|
-
targetStepRecord.startedAt = /* @__PURE__ */ new Date();
|
|
161
|
+
if (unansweredQuestions.length > 3) {
|
|
162
|
+
lines.push(chalk9__default.default.gray(` ... \u8FD8\u6709 ${unansweredQuestions.length - 3} \u4E2A\u95EE\u9898`));
|
|
441
163
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
await this.saveState();
|
|
446
|
-
await this.updateChangeRecord();
|
|
447
|
-
return {
|
|
448
|
-
from: currentStep,
|
|
449
|
-
to: targetStep,
|
|
450
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
451
|
-
reason
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* 获取可回滚的目标阶段
|
|
456
|
-
*/
|
|
457
|
-
getRollbackTargets() {
|
|
458
|
-
if (!this.state) return [];
|
|
459
|
-
const allTargets = exports.ROLLBACK_RULES[this.state.currentStep] || [];
|
|
460
|
-
const workflowSteps = this.state.type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
461
|
-
return allTargets.filter((target) => workflowSteps.includes(target));
|
|
462
|
-
}
|
|
463
|
-
/**
|
|
464
|
-
* 检查是否可以回滚到指定阶段
|
|
465
|
-
*/
|
|
466
|
-
canRollbackTo(targetStep) {
|
|
467
|
-
const targets = this.getRollbackTargets();
|
|
468
|
-
return targets.includes(targetStep);
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push(chalk9__default.default.gray(' \u8BF7\u8F93\u5165\u56DE\u7B54\uFF0C\u6216\u8F93\u5165 "done" \u8DF3\u8FC7\u6F84\u6E05'));
|
|
166
|
+
return { output: lines.join("\n") };
|
|
469
167
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
...snapshot.state,
|
|
504
|
-
createdAt: previousState.createdAt,
|
|
505
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
506
|
-
};
|
|
507
|
-
const stepOrder = this.state.type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
508
|
-
const targetIndex = stepOrder.indexOf(targetStep);
|
|
509
|
-
for (let i = 0; i < this.state.steps.length; i++) {
|
|
510
|
-
const step = this.state.steps[i];
|
|
511
|
-
const stepIndex = stepOrder.indexOf(step.step);
|
|
512
|
-
if (stepIndex < targetIndex) {
|
|
513
|
-
step.status = "completed";
|
|
514
|
-
} else if (stepIndex === targetIndex) {
|
|
515
|
-
step.status = "running";
|
|
516
|
-
step.startedAt = /* @__PURE__ */ new Date();
|
|
517
|
-
step.completedAt = void 0;
|
|
518
|
-
} else {
|
|
519
|
-
step.status = "pending";
|
|
520
|
-
step.startedAt = void 0;
|
|
521
|
-
step.completedAt = void 0;
|
|
522
|
-
}
|
|
168
|
+
lines.push(chalk9__default.default.green(" \u2713 \u9700\u6C42\u6E05\u6670\uFF0C\u7EE7\u7EED\u4E0B\u4E00\u6B65"));
|
|
169
|
+
activeSession.phase = "analysis";
|
|
170
|
+
}
|
|
171
|
+
if (activeSession.phase === "analysis") {
|
|
172
|
+
lines.push("");
|
|
173
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/8: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
|
|
174
|
+
lines.push("");
|
|
175
|
+
activeSession.complexity = analyzeComplexity(
|
|
176
|
+
activeSession.refinedRequirement,
|
|
177
|
+
activeSession.context
|
|
178
|
+
);
|
|
179
|
+
const complexityBar = generateComplexityBar(activeSession.complexity);
|
|
180
|
+
lines.push(chalk9__default.default.gray(` \u590D\u6742\u5EA6: ${complexityBar} ${activeSession.complexity}/10`));
|
|
181
|
+
if (activeSession.complexity >= COMPLEXITY_THRESHOLD) {
|
|
182
|
+
lines.push(chalk9__default.default.yellow(" \u5224\u5B9A: \u590D\u6742\u9700\u6C42\uFF0C\u5EFA\u8BAE\u67B6\u6784\u5E08\u4ECB\u5165"));
|
|
183
|
+
} else {
|
|
184
|
+
lines.push(chalk9__default.default.green(" \u5224\u5B9A: \u7B80\u5355\u9700\u6C42\uFF0C\u76F4\u63A5\u8FDB\u5165\u89C4\u683C\u62C6\u89E3"));
|
|
185
|
+
}
|
|
186
|
+
activeSession.phase = "bdd";
|
|
187
|
+
}
|
|
188
|
+
if (activeSession.phase === "bdd") {
|
|
189
|
+
lines.push("");
|
|
190
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/8: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
191
|
+
lines.push("");
|
|
192
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
193
|
+
activeSession.refinedRequirement,
|
|
194
|
+
activeSession.context,
|
|
195
|
+
activeSession.clarificationQuestions
|
|
196
|
+
);
|
|
197
|
+
for (const scenario of activeSession.bddScenarios) {
|
|
198
|
+
lines.push(chalk9__default.default.white(` Feature: ${scenario.feature}`));
|
|
199
|
+
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
200
|
+
lines.push(chalk9__default.default.gray(` - ${s.name}`));
|
|
523
201
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
for (const type of confirmationsToClear) {
|
|
527
|
-
this.confirmationManager.clearConfirmation(type);
|
|
202
|
+
if (scenario.scenarios.length > 3) {
|
|
203
|
+
lines.push(chalk9__default.default.gray(` ... \u5171 ${scenario.scenarios.length} \u4E2A\u573A\u666F`));
|
|
528
204
|
}
|
|
529
|
-
await this.saveState();
|
|
530
|
-
await this.updateChangeRecord("rolled-back");
|
|
531
|
-
return {
|
|
532
|
-
success: true,
|
|
533
|
-
from: currentStep,
|
|
534
|
-
to: targetStep,
|
|
535
|
-
message: `\u5DF2\u56DE\u6EDA: ${currentStep} \u2192 ${targetStep}\uFF0C\u539F\u56E0: ${description || reason}`,
|
|
536
|
-
snapshot
|
|
537
|
-
};
|
|
538
205
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
206
|
+
activeSession.phase = "spec";
|
|
207
|
+
}
|
|
208
|
+
if (activeSession.phase === "spec") {
|
|
209
|
+
lines.push("");
|
|
210
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/8: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
211
|
+
lines.push("");
|
|
212
|
+
activeSession.specItems = generateSpecItems(
|
|
213
|
+
activeSession.refinedRequirement,
|
|
214
|
+
activeSession.context,
|
|
215
|
+
activeSession.bddScenarios,
|
|
216
|
+
activeSession.clarificationQuestions
|
|
217
|
+
);
|
|
218
|
+
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
219
|
+
lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
220
|
+
lines.push(chalk9__default.default.gray(` \u8DEF\u5F84: ${specPath}`));
|
|
221
|
+
lines.push("");
|
|
222
|
+
lines.push(chalk9__default.default.cyan(" \u4EFB\u52A1\u6982\u89C8:"));
|
|
223
|
+
for (const item of activeSession.specItems.slice(0, 5)) {
|
|
224
|
+
const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
225
|
+
lines.push(chalk9__default.default.gray(` ${icon} [${item.id}] ${item.title}`));
|
|
226
|
+
}
|
|
227
|
+
if (activeSession.specItems.length > 5) {
|
|
228
|
+
lines.push(chalk9__default.default.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
|
|
229
|
+
}
|
|
230
|
+
lines.push("");
|
|
231
|
+
lines.push(chalk9__default.default.yellow.bold("\u23F8\uFE0F \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
|
|
232
|
+
lines.push("");
|
|
233
|
+
lines.push(chalk9__default.default.green(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u7EE7\u7EED\u5DE5\u4F5C\u6D41"));
|
|
234
|
+
lines.push(chalk9__default.default.red(" n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210\u89C4\u683C"));
|
|
235
|
+
lines.push(chalk9__default.default.gray(" c - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41"));
|
|
236
|
+
return { output: lines.join("\n") };
|
|
237
|
+
}
|
|
238
|
+
if (activeSession.phase === "tdd") {
|
|
239
|
+
lines.push("");
|
|
240
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/8: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
241
|
+
lines.push("");
|
|
242
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
243
|
+
lines.push(chalk9__default.default.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
244
|
+
for (const file of activeSession.testFiles) {
|
|
245
|
+
lines.push(chalk9__default.default.gray(` - ${file}`));
|
|
246
|
+
}
|
|
247
|
+
activeSession.phase = "develop";
|
|
248
|
+
}
|
|
249
|
+
if (activeSession.phase === "develop") {
|
|
250
|
+
lines.push("");
|
|
251
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/8: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
|
|
252
|
+
lines.push("");
|
|
253
|
+
lines.push(chalk9__default.default.yellow(" \u{1F4DD} \u5F00\u53D1\u9636\u6BB5"));
|
|
254
|
+
lines.push(chalk9__default.default.gray(" \u8BF7\u8C03\u7528 $frontend-dev \u6267\u884C\u5F00\u53D1\u4EFB\u52A1"));
|
|
255
|
+
lines.push(chalk9__default.default.gray(" \u6216\u624B\u52A8\u5B9E\u73B0\u4EE3\u7801\u540E\u8F93\u5165 continue \u7EE7\u7EED"));
|
|
256
|
+
return { output: lines.join("\n") };
|
|
257
|
+
}
|
|
258
|
+
if (activeSession.phase === "review") {
|
|
259
|
+
lines.push("");
|
|
260
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/8: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
|
|
261
|
+
lines.push("");
|
|
262
|
+
lines.push(chalk9__default.default.yellow(" \u{1F50D} \u4EE3\u7801\u5BA1\u6838\u9636\u6BB5"));
|
|
263
|
+
lines.push(chalk9__default.default.gray(" \u8BF7\u8C03\u7528 $code-reviewer \u6267\u884C\u4EE3\u7801\u5BA1\u6838"));
|
|
264
|
+
lines.push(chalk9__default.default.gray(" \u6216\u8F93\u5165 review \u5B8C\u6210\u5BA1\u6838"));
|
|
265
|
+
}
|
|
266
|
+
return { output: lines.join("\n") };
|
|
267
|
+
} catch (error) {
|
|
268
|
+
lines.push(chalk9__default.default.red(`\u9519\u8BEF: ${error.message}`));
|
|
269
|
+
return { output: lines.join("\n") };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async function handleWorkflowInput(input, ctx) {
|
|
273
|
+
if (!activeSession) return null;
|
|
274
|
+
const trimmed = input.trim().toLowerCase();
|
|
275
|
+
if (trimmed === "c" || trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
|
|
276
|
+
activeSession = null;
|
|
277
|
+
return {
|
|
278
|
+
output: chalk9__default.default.yellow("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u53D6\u6D88")
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (activeSession.phase === "clarify") {
|
|
282
|
+
if (trimmed === "done" || trimmed === "\u5B8C\u6210" || trimmed === "\u8DF3\u8FC7" || trimmed === "skip") {
|
|
283
|
+
activeSession.refinedRequirement = buildRefinedRequirement(activeSession);
|
|
284
|
+
activeSession.phase = "analysis";
|
|
285
|
+
return executeWorkflow(ctx);
|
|
286
|
+
}
|
|
287
|
+
activeSession.clarificationQuestions.filter((q) => q.answered).length;
|
|
288
|
+
const unansweredQuestions = activeSession.clarificationQuestions.filter((q) => !q.answered);
|
|
289
|
+
if (unansweredQuestions.length > 0) {
|
|
290
|
+
const question = unansweredQuestions[0];
|
|
291
|
+
question.answer = input.trim();
|
|
292
|
+
question.answered = true;
|
|
293
|
+
activeSession.refinedRequirement = buildRefinedRequirement(activeSession);
|
|
294
|
+
const remaining = activeSession.clarificationQuestions.filter((q) => !q.answered);
|
|
295
|
+
if (remaining.length > 0) {
|
|
296
|
+
activeSession.clarityScore = calculateClarityScore(activeSession.clarificationQuestions);
|
|
297
|
+
const lines = [];
|
|
298
|
+
lines.push(chalk9__default.default.green(" \u2713 \u5DF2\u8BB0\u5F55"));
|
|
299
|
+
lines.push("");
|
|
300
|
+
lines.push(chalk9__default.default.yellow(" \u7EE7\u7EED\u56DE\u7B54\uFF1A"));
|
|
301
|
+
for (let i = 0; i < Math.min(remaining.length, 3); i++) {
|
|
302
|
+
const q = remaining[i];
|
|
303
|
+
lines.push(chalk9__default.default.white(` ${i + 1}. ${q.question}`));
|
|
578
304
|
}
|
|
579
|
-
|
|
305
|
+
lines.push("");
|
|
306
|
+
lines.push(chalk9__default.default.gray(' \u8F93\u5165\u56DE\u7B54\uFF0C\u6216 "done" \u8DF3\u8FC7'));
|
|
307
|
+
return { output: lines.join("\n") };
|
|
580
308
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
309
|
+
activeSession.clarityScore = 1;
|
|
310
|
+
activeSession.phase = "analysis";
|
|
311
|
+
return executeWorkflow(ctx);
|
|
312
|
+
}
|
|
313
|
+
activeSession.phase = "analysis";
|
|
314
|
+
return executeWorkflow(ctx);
|
|
315
|
+
}
|
|
316
|
+
if (activeSession.phase === "spec") {
|
|
317
|
+
if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
|
|
318
|
+
activeSession.phase = "tdd";
|
|
319
|
+
return executeWorkflow(ctx);
|
|
320
|
+
}
|
|
321
|
+
if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
|
|
322
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
323
|
+
activeSession.refinedRequirement,
|
|
324
|
+
activeSession.context,
|
|
325
|
+
activeSession.clarificationQuestions
|
|
326
|
+
);
|
|
327
|
+
activeSession.specItems = generateSpecItems(
|
|
328
|
+
activeSession.refinedRequirement,
|
|
329
|
+
activeSession.context,
|
|
330
|
+
activeSession.bddScenarios,
|
|
331
|
+
activeSession.clarificationQuestions
|
|
332
|
+
);
|
|
333
|
+
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
334
|
+
return {
|
|
335
|
+
output: chalk9__default.default.cyan("\u{1F504} \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210") + chalk9__default.default.gray(`
|
|
336
|
+
\u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u518D\u6B21\u91CD\u65B0\u751F\u6210")
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (activeSession.phase === "develop") {
|
|
341
|
+
if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
|
|
342
|
+
activeSession.phase = "review";
|
|
343
|
+
return executeWorkflow(ctx);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (activeSession.phase === "review") {
|
|
347
|
+
if (trimmed === "review" || trimmed === "\u5BA1\u6838" || trimmed === "pass" || trimmed === "\u901A\u8FC7") {
|
|
348
|
+
await archiveWorkflow(ctx.options.workingDirectory);
|
|
349
|
+
const summary = activeSession.refinedRequirement;
|
|
350
|
+
activeSession = null;
|
|
351
|
+
return {
|
|
352
|
+
output: chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5B8C\u6210") + chalk9__default.default.gray(`
|
|
353
|
+
\u9700\u6C42: ${summary.slice(0, 60)}...`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /new <\u9700\u6C42> \u5F00\u59CB\u65B0\u7684\u5DE5\u4F5C\u6D41")
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (trimmed === "fail" || trimmed === "\u5931\u8D25" || trimmed === "reject" || trimmed === "\u62D2\u7EDD") {
|
|
357
|
+
activeSession.phase = "spec";
|
|
358
|
+
return {
|
|
359
|
+
output: chalk9__default.default.yellow("\u21A9\uFE0F \u5BA1\u6838\u672A\u901A\u8FC7\uFF0C\u56DE\u9000\u5230\u89C4\u683C\u9636\u6BB5") + chalk9__default.default.gray("\n\u8BF7\u91CD\u65B0\u786E\u8BA4\u6216\u4FEE\u6539\u89C4\u683C") + chalk9__default.default.yellow("\n\n\u4F7F\u7528 y \u786E\u8BA4\uFF0Cn \u91CD\u65B0\u751F\u6210")
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
function analyzeRequirementClarity(requirement, context) {
|
|
366
|
+
const questions = [];
|
|
367
|
+
let score = 0.5;
|
|
368
|
+
if (requirement.length < 10) {
|
|
369
|
+
score -= 0.2;
|
|
370
|
+
questions.push({
|
|
371
|
+
id: "q-length",
|
|
372
|
+
question: "\u9700\u6C42\u63CF\u8FF0\u8F83\u77ED\uFF0C\u80FD\u5426\u8BE6\u7EC6\u8BF4\u660E\u5177\u4F53\u8981\u5B9E\u73B0\u4EC0\u4E48\u529F\u80FD\uFF1F",
|
|
373
|
+
category: "scope",
|
|
374
|
+
answered: false
|
|
375
|
+
});
|
|
376
|
+
} else if (requirement.length >= 50) {
|
|
377
|
+
score += 0.1;
|
|
378
|
+
}
|
|
379
|
+
const actionKeywords = ["\u5B9E\u73B0", "\u6DFB\u52A0", "\u4FEE\u6539", "\u5220\u9664", "\u4F18\u5316", "\u4FEE\u590D", "\u91CD\u6784", "\u5F00\u53D1", "\u521B\u5EFA", "\u8BBE\u8BA1"];
|
|
380
|
+
const hasAction = actionKeywords.some((kw) => requirement.includes(kw));
|
|
381
|
+
if (!hasAction) {
|
|
382
|
+
score -= 0.15;
|
|
383
|
+
questions.push({
|
|
384
|
+
id: "q-action",
|
|
385
|
+
question: "\u8FD9\u662F\u4E00\u4E2A\u65B0\u529F\u80FD\u3001\u4FEE\u6539\u8FD8\u662F\u4FEE\u590D\uFF1F\u8BF7\u8BF4\u660E\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u3002",
|
|
386
|
+
category: "scope",
|
|
387
|
+
answered: false
|
|
388
|
+
});
|
|
389
|
+
} else {
|
|
390
|
+
score += 0.1;
|
|
391
|
+
}
|
|
392
|
+
if (requirement.match(/界面|页面|组件|按钮|表单|弹窗|布局|样式|UI/)) {
|
|
393
|
+
score += 0.1;
|
|
394
|
+
if (!requirement.match(/在.*页面|在.*位置|显示在|位于/)) {
|
|
395
|
+
questions.push({
|
|
396
|
+
id: "q-ui-position",
|
|
397
|
+
question: "\u8FD9\u4E2A\u529F\u80FD\u5E94\u8BE5\u653E\u5728\u54EA\u4E2A\u9875\u9762\u6216\u4F4D\u7F6E\uFF1F",
|
|
398
|
+
category: "ui",
|
|
399
|
+
answered: false
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
questions.push({
|
|
404
|
+
id: "q-ui-need",
|
|
405
|
+
question: "\u8FD9\u4E2A\u529F\u80FD\u9700\u8981\u7528\u6237\u754C\u9762\u5417\uFF1F\u5982\u679C\u9700\u8981\uFF0C\u5927\u6982\u957F\u4EC0\u4E48\u6837\uFF1F",
|
|
406
|
+
category: "ui",
|
|
407
|
+
answered: false
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
if (requirement.match(/数据|存储|保存|读取|API|接口|数据库|缓存/)) {
|
|
411
|
+
score += 0.1;
|
|
412
|
+
if (!requirement.match(/从.*获取|调用.*接口|读取.*数据|来源/)) {
|
|
413
|
+
questions.push({
|
|
414
|
+
id: "q-data-source",
|
|
415
|
+
question: "\u6570\u636E\u4ECE\u54EA\u91CC\u6765\uFF1F\u662F\u8C03\u7528API\u3001\u672C\u5730\u5B58\u50A8\u8FD8\u662F\u5176\u4ED6\u6765\u6E90\uFF1F",
|
|
416
|
+
category: "data",
|
|
417
|
+
answered: false
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (requirement.match(/点击|输入|选择|拖动|滑动|交互|操作/)) {
|
|
422
|
+
score += 0.1;
|
|
423
|
+
} else {
|
|
424
|
+
if (hasAction && requirement.match(/功能|特性|模块/)) {
|
|
425
|
+
questions.push({
|
|
426
|
+
id: "q-interaction",
|
|
427
|
+
question: "\u7528\u6237\u5982\u4F55\u64CD\u4F5C\u8FD9\u4E2A\u529F\u80FD\uFF1F\u6709\u4EC0\u4E48\u4EA4\u4E92\u6D41\u7A0B\uFF1F",
|
|
428
|
+
category: "interaction",
|
|
429
|
+
answered: false
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (requirement.match(/异常|错误|失败|边界|特殊情况|空值|验证/)) {
|
|
434
|
+
score += 0.15;
|
|
435
|
+
} else {
|
|
436
|
+
if (requirement.match(/功能|输入|表单|数据/)) {
|
|
437
|
+
questions.push({
|
|
438
|
+
id: "q-edge",
|
|
439
|
+
question: "\u6709\u4EC0\u4E48\u7279\u6B8A\u60C5\u51B5\u6216\u8FB9\u754C\u6761\u4EF6\u9700\u8981\u5904\u7406\uFF1F\u6BD4\u5982\u8F93\u5165\u4E3A\u7A7A\u3001\u6570\u636E\u52A0\u8F7D\u5931\u8D25\u7B49\u3002",
|
|
440
|
+
category: "edge",
|
|
441
|
+
answered: false
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (requirement.match(/https?:\/\/|参考|参照|类似/)) {
|
|
446
|
+
score += 0.15;
|
|
447
|
+
}
|
|
448
|
+
if (context.framework) {
|
|
449
|
+
score += 0.05;
|
|
450
|
+
}
|
|
451
|
+
const limitedQuestions = questions.slice(0, 5);
|
|
452
|
+
score = Math.max(0, Math.min(1, score));
|
|
453
|
+
return { score, questions: limitedQuestions };
|
|
454
|
+
}
|
|
455
|
+
function calculateClarityScore(questions) {
|
|
456
|
+
const answered = questions.filter((q) => q.answered).length;
|
|
457
|
+
if (questions.length === 0) return 1;
|
|
458
|
+
return 0.5 + answered / questions.length * 0.5;
|
|
459
|
+
}
|
|
460
|
+
function buildRefinedRequirement(session) {
|
|
461
|
+
const parts = [session.requirement];
|
|
462
|
+
for (const q of session.clarificationQuestions) {
|
|
463
|
+
if (q.answered && q.answer) {
|
|
464
|
+
parts.push(`
|
|
465
|
+
\u3010${getCategoryLabel(q.category)}\u3011${q.answer}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return parts.join("");
|
|
469
|
+
}
|
|
470
|
+
function getCategoryLabel(category) {
|
|
471
|
+
const labels = {
|
|
472
|
+
scope: "\u8303\u56F4",
|
|
473
|
+
ui: "\u754C\u9762",
|
|
474
|
+
data: "\u6570\u636E",
|
|
475
|
+
interaction: "\u4EA4\u4E92",
|
|
476
|
+
edge: "\u8FB9\u754C",
|
|
477
|
+
tech: "\u6280\u672F"
|
|
478
|
+
};
|
|
479
|
+
return labels[category] || category;
|
|
480
|
+
}
|
|
481
|
+
async function readProjectContext(workingDir) {
|
|
482
|
+
const context = {
|
|
483
|
+
name: path4__namespace.basename(workingDir),
|
|
484
|
+
type: "unknown",
|
|
485
|
+
framework: null,
|
|
486
|
+
techStack: [],
|
|
487
|
+
description: "",
|
|
488
|
+
devStandards: "",
|
|
489
|
+
agentsMd: "",
|
|
490
|
+
configYaml: ""
|
|
491
|
+
};
|
|
492
|
+
const agentsPath = path4__namespace.join(workingDir, "AGENTS.md");
|
|
493
|
+
try {
|
|
494
|
+
const stats = await fs4__namespace.stat(agentsPath);
|
|
495
|
+
if (stats.size <= MAX_FILE_SIZE2) {
|
|
496
|
+
context.agentsMd = await fs4__namespace.readFile(agentsPath, "utf-8");
|
|
497
|
+
const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
498
|
+
if (nameMatch) context.name = nameMatch[1];
|
|
499
|
+
const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
|
|
500
|
+
if (typeMatch) context.type = typeMatch[1];
|
|
501
|
+
const frameworkMatch = context.agentsMd.match(/\|\s*技术框架\s*\|\s*([^\s|]+)/);
|
|
502
|
+
if (frameworkMatch && frameworkMatch[1] !== "\u5F85\u8BC6\u522B") {
|
|
503
|
+
context.framework = frameworkMatch[1];
|
|
609
504
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
try {
|
|
632
|
-
const content = await fs4__namespace.readFile(changeFile, "utf-8");
|
|
633
|
-
const parsed = this.parseChangeRecord(content);
|
|
634
|
-
if (parsed && parsed.status === "running") {
|
|
635
|
-
this.state = parsed;
|
|
636
|
-
return true;
|
|
637
|
-
}
|
|
638
|
-
} catch {
|
|
639
|
-
}
|
|
640
|
-
return false;
|
|
505
|
+
}
|
|
506
|
+
} catch {
|
|
507
|
+
}
|
|
508
|
+
const configPath = path4__namespace.join(workingDir, "openspec", "config.yaml");
|
|
509
|
+
try {
|
|
510
|
+
const stats = await fs4__namespace.stat(configPath);
|
|
511
|
+
if (stats.size <= MAX_FILE_SIZE2) {
|
|
512
|
+
context.configYaml = await fs4__namespace.readFile(configPath, "utf-8");
|
|
513
|
+
const nameMatch = context.configYaml.match(/name:\s*(.+)/);
|
|
514
|
+
if (nameMatch) context.name = nameMatch[1].trim();
|
|
515
|
+
const typeMatch = context.configYaml.match(/type:\s*(.+)/);
|
|
516
|
+
if (typeMatch) context.type = typeMatch[1].trim();
|
|
517
|
+
const frameworkMatch = context.configYaml.match(/framework:\s*(.+)/);
|
|
518
|
+
if (frameworkMatch && frameworkMatch[1].trim() !== "null") {
|
|
519
|
+
context.framework = frameworkMatch[1].trim();
|
|
520
|
+
}
|
|
521
|
+
const techStackMatch = context.configYaml.match(/techStack:\s*([\s\S]+?)(?=\n\w)/);
|
|
522
|
+
if (techStackMatch) {
|
|
523
|
+
const techLines = techStackMatch[1].match(/-\s*(.+)/g);
|
|
524
|
+
if (techLines) {
|
|
525
|
+
context.techStack = techLines.map((l) => l.replace(/-\s*/, "").trim());
|
|
641
526
|
}
|
|
642
527
|
}
|
|
643
|
-
/**
|
|
644
|
-
* 获取允许的下一步
|
|
645
|
-
*/
|
|
646
|
-
getAllowedTransitions() {
|
|
647
|
-
if (!this.state) return [];
|
|
648
|
-
return TRANSITIONS[this.state.currentStep] || [];
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* 获取快照历史
|
|
652
|
-
*/
|
|
653
|
-
getSnapshots() {
|
|
654
|
-
return Array.from(this.snapshots.values()).sort(
|
|
655
|
-
(a, b) => a.timestamp.getTime() - b.timestamp.getTime()
|
|
656
|
-
);
|
|
657
|
-
}
|
|
658
|
-
/**
|
|
659
|
-
* 完成当前步骤
|
|
660
|
-
*/
|
|
661
|
-
async completeCurrentStep(artifacts) {
|
|
662
|
-
if (!this.state) return;
|
|
663
|
-
const currentStepRecord = this.state.steps.find(
|
|
664
|
-
(s) => s.step === this.state.currentStep
|
|
665
|
-
);
|
|
666
|
-
if (currentStepRecord) {
|
|
667
|
-
currentStepRecord.status = "completed";
|
|
668
|
-
currentStepRecord.completedAt = /* @__PURE__ */ new Date();
|
|
669
|
-
}
|
|
670
|
-
if (artifacts) {
|
|
671
|
-
this.state.artifacts.push(...artifacts);
|
|
672
|
-
}
|
|
673
|
-
await this.createSnapshot();
|
|
674
|
-
await this.saveState();
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* 归档工作流
|
|
678
|
-
*/
|
|
679
|
-
async archive(summary) {
|
|
680
|
-
if (!this.state) return;
|
|
681
|
-
const changeId = this.state.id;
|
|
682
|
-
for (const step of this.state.steps) {
|
|
683
|
-
if (step.status !== "completed") {
|
|
684
|
-
step.status = "completed";
|
|
685
|
-
step.completedAt = /* @__PURE__ */ new Date();
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
this.state.status = "completed";
|
|
689
|
-
this.state.completedAt = /* @__PURE__ */ new Date();
|
|
690
|
-
await this.createSpecDocument(summary);
|
|
691
|
-
await this.updateChangeRecord("archived");
|
|
692
|
-
await this.saveState();
|
|
693
|
-
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
694
|
-
const archiveDir = path4__namespace.join(changesDir, "archive");
|
|
695
|
-
const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
|
|
696
|
-
const archiveFile = path4__namespace.join(archiveDir, `${changeId}.md`);
|
|
697
|
-
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
698
|
-
await fs4__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
699
|
-
});
|
|
700
|
-
this.state = null;
|
|
701
|
-
this.snapshots.clear();
|
|
702
|
-
this.confirmationManager.clearAllConfirmations();
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* 取消工作流
|
|
706
|
-
*/
|
|
707
|
-
async cancel(reason) {
|
|
708
|
-
if (!this.state) return;
|
|
709
|
-
this.state.status = "cancelled";
|
|
710
|
-
this.state.cancelledAt = /* @__PURE__ */ new Date();
|
|
711
|
-
this.state.cancelReason = reason;
|
|
712
|
-
await this.updateChangeRecord("cancelled");
|
|
713
|
-
await this.saveState();
|
|
714
|
-
this.state = null;
|
|
715
|
-
this.snapshots.clear();
|
|
716
|
-
this.confirmationManager.clearAllConfirmations();
|
|
717
|
-
}
|
|
718
|
-
// ==================== 私有方法 ====================
|
|
719
|
-
async ensureDirectories() {
|
|
720
|
-
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
721
|
-
const archiveDir = path4__namespace.join(changesDir, "archive");
|
|
722
|
-
const specDir = path4__namespace.join(this.openspecPath, "spec");
|
|
723
|
-
const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
|
|
724
|
-
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
725
|
-
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
726
|
-
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
727
|
-
}
|
|
728
|
-
async restoreState() {
|
|
729
|
-
const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
730
|
-
let activeId = null;
|
|
731
|
-
try {
|
|
732
|
-
const activeContent = await fs4__namespace.readFile(activePath, "utf-8");
|
|
733
|
-
const activeData = JSON.parse(activeContent);
|
|
734
|
-
activeId = activeData.activeId;
|
|
735
|
-
} catch {
|
|
736
|
-
}
|
|
737
|
-
if (activeId) {
|
|
738
|
-
const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
739
|
-
try {
|
|
740
|
-
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
741
|
-
this.state = JSON.parse(content, (key, value) => {
|
|
742
|
-
if (key.endsWith("At") && typeof value === "string") {
|
|
743
|
-
return new Date(value);
|
|
744
|
-
}
|
|
745
|
-
return value;
|
|
746
|
-
});
|
|
747
|
-
return;
|
|
748
|
-
} catch {
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
const oldStatePath = path4__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
752
|
-
try {
|
|
753
|
-
const content = await fs4__namespace.readFile(oldStatePath, "utf-8");
|
|
754
|
-
this.state = JSON.parse(content, (key, value) => {
|
|
755
|
-
if (key.endsWith("At") && typeof value === "string") {
|
|
756
|
-
return new Date(value);
|
|
757
|
-
}
|
|
758
|
-
return value;
|
|
759
|
-
});
|
|
760
|
-
if (this.state) {
|
|
761
|
-
await this.saveState();
|
|
762
|
-
await fs4__namespace.unlink(oldStatePath).catch(() => {
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
} catch (e) {
|
|
766
|
-
const err = e;
|
|
767
|
-
if (err.code !== "ENOENT") {
|
|
768
|
-
console.warn("\u8B66\u544A: \u5DE5\u4F5C\u6D41\u72B6\u6001\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u5C06\u91CD\u65B0\u5F00\u59CB");
|
|
769
|
-
}
|
|
770
|
-
this.state = null;
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
async saveState() {
|
|
774
|
-
if (!this.state) return;
|
|
775
|
-
const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
|
|
776
|
-
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
777
|
-
const statePath = path4__namespace.join(statesDir, `${this.state.id}.json`);
|
|
778
|
-
await fs4__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
779
|
-
const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
780
|
-
await fs4__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
781
|
-
}
|
|
782
|
-
async restoreSnapshots() {
|
|
783
|
-
const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
784
|
-
try {
|
|
785
|
-
const content = await fs4__namespace.readFile(snapshotsPath, "utf-8");
|
|
786
|
-
const data = JSON.parse(content, (key, value) => {
|
|
787
|
-
if (key === "timestamp" && typeof value === "string") {
|
|
788
|
-
return new Date(value);
|
|
789
|
-
}
|
|
790
|
-
return value;
|
|
791
|
-
});
|
|
792
|
-
if (Array.isArray(data)) {
|
|
793
|
-
this.snapshots = new Map(data.map((s) => [s.step, s]));
|
|
794
|
-
}
|
|
795
|
-
} catch {
|
|
796
|
-
this.snapshots = /* @__PURE__ */ new Map();
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
async saveSnapshots() {
|
|
800
|
-
const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
801
|
-
const data = Array.from(this.snapshots.values());
|
|
802
|
-
await fs4__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
803
|
-
}
|
|
804
|
-
async createSnapshot() {
|
|
805
|
-
if (!this.state) return;
|
|
806
|
-
const snapshot = {
|
|
807
|
-
step: this.state.currentStep,
|
|
808
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
809
|
-
state: JSON.parse(JSON.stringify(this.state)),
|
|
810
|
-
artifacts: [...this.state.artifacts]
|
|
811
|
-
};
|
|
812
|
-
this.snapshots.set(snapshot.step, snapshot);
|
|
813
|
-
await this.saveSnapshots();
|
|
814
|
-
}
|
|
815
|
-
async generateChangeId() {
|
|
816
|
-
const timestamp = Date.now().toString(36);
|
|
817
|
-
const random = Math.random().toString(36).slice(2, 6);
|
|
818
|
-
return `CHG-${timestamp}-${random}`.toUpperCase();
|
|
819
|
-
}
|
|
820
|
-
async createChangeRecord() {
|
|
821
|
-
if (!this.state) return;
|
|
822
|
-
const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
823
|
-
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
824
|
-
}
|
|
825
|
-
async updateChangeRecord(status) {
|
|
826
|
-
if (!this.state) return;
|
|
827
|
-
if (status) {
|
|
828
|
-
this.state.status = status;
|
|
829
|
-
}
|
|
830
|
-
const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
831
|
-
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
832
|
-
}
|
|
833
|
-
formatChangeRecord() {
|
|
834
|
-
if (!this.state) return "";
|
|
835
|
-
const formatStepTime = (date) => {
|
|
836
|
-
if (!date) return "-";
|
|
837
|
-
if (typeof date === "string") return date;
|
|
838
|
-
return date.toISOString();
|
|
839
|
-
};
|
|
840
|
-
return `# Change: ${this.state.title}
|
|
841
|
-
|
|
842
|
-
id: ${this.state.id}
|
|
843
|
-
title: ${this.state.title}
|
|
844
|
-
status: ${this.state.status}
|
|
845
|
-
created: ${this.state.createdAt.toISOString()}
|
|
846
|
-
${this.state.completedAt ? `completed: ${formatStepTime(this.state.completedAt)}` : ""}
|
|
847
|
-
complexity: ${this.state.complexity}/10
|
|
848
|
-
workflow: ${this.state.type}
|
|
849
|
-
spec: spec/${this.state.id}.md
|
|
850
|
-
|
|
851
|
-
---
|
|
852
|
-
|
|
853
|
-
## \u53D8\u66F4\u6982\u8FF0
|
|
854
|
-
|
|
855
|
-
${this.state.requirement}
|
|
856
|
-
|
|
857
|
-
## \u5DE5\u4F5C\u6D41\u6267\u884C\u8BB0\u5F55
|
|
858
|
-
|
|
859
|
-
| \u9636\u6BB5 | \u5F00\u59CB\u65F6\u95F4 | \u7ED3\u675F\u65F6\u95F4 | \u72B6\u6001 |
|
|
860
|
-
|------|----------|----------|------|
|
|
861
|
-
${this.state.steps.map((s) => `| ${s.step} | ${formatStepTime(s.startedAt)} | ${formatStepTime(s.completedAt)} | ${s.status} |`).join("\n")}
|
|
862
|
-
|
|
863
|
-
## \u4EA7\u7269\u6587\u4EF6
|
|
864
|
-
|
|
865
|
-
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
866
|
-
`;
|
|
867
|
-
}
|
|
868
|
-
async createSpecDocument(summary) {
|
|
869
|
-
if (!this.state) return;
|
|
870
|
-
const specPath = path4__namespace.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
871
|
-
const content = `# Spec: ${this.state.title}
|
|
872
|
-
|
|
873
|
-
> \u53D8\u66F4ID: ${this.state.id}
|
|
874
|
-
> \u5F52\u6863\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
875
|
-
> \u5DE5\u4F5C\u6D41\u7C7B\u578B: ${this.state.type}
|
|
876
|
-
|
|
877
|
-
---
|
|
878
|
-
|
|
879
|
-
## \u6982\u8FF0
|
|
880
|
-
|
|
881
|
-
${this.state.requirement}
|
|
882
|
-
|
|
883
|
-
## \u5B9E\u73B0\u603B\u7ED3
|
|
884
|
-
|
|
885
|
-
${summary}
|
|
886
|
-
|
|
887
|
-
## \u9A8C\u6536\u7ED3\u679C
|
|
888
|
-
|
|
889
|
-
${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.step}`).join("\n")}
|
|
890
|
-
|
|
891
|
-
## \u76F8\u5173\u6587\u4EF6
|
|
892
|
-
|
|
893
|
-
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
894
|
-
`;
|
|
895
|
-
await fs4__namespace.writeFile(specPath, content);
|
|
896
|
-
}
|
|
897
|
-
};
|
|
898
|
-
exports.ConfirmationRequiredError = class extends Error {
|
|
899
|
-
constructor(message, point) {
|
|
900
|
-
super(message);
|
|
901
|
-
this.point = point;
|
|
902
|
-
this.name = "ConfirmationRequiredError";
|
|
903
|
-
}
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
|
|
908
|
-
// src/commands/new.ts
|
|
909
|
-
var new_exports = {};
|
|
910
|
-
__export(new_exports, {
|
|
911
|
-
analyzeComplexity: () => analyzeComplexity,
|
|
912
|
-
default: () => new_default,
|
|
913
|
-
extractTitle: () => extractTitle,
|
|
914
|
-
generateSpecContent: () => generateSpecContent,
|
|
915
|
-
handleNew: () => handleNew,
|
|
916
|
-
newFeature: () => newFeature,
|
|
917
|
-
parseArgs: () => parseArgs,
|
|
918
|
-
readProjectContext: () => readProjectContext
|
|
919
|
-
});
|
|
920
|
-
async function handleNew(args, ctx) {
|
|
921
|
-
const workingDir = ctx.options.workingDirectory;
|
|
922
|
-
const workflowEngine = ctx.workflowEngine;
|
|
923
|
-
if (workflowEngine) {
|
|
924
|
-
const existingState = workflowEngine.getState();
|
|
925
|
-
if (existingState && existingState.status === "running") {
|
|
926
|
-
if (existingState.currentStep === "explore" || existingState.currentStep === "propose") {
|
|
927
|
-
const specPath = path4__namespace.join(workingDir, "openspec", "changes", `${existingState.id}-spec.md`);
|
|
928
|
-
if (fs10__namespace.existsSync(specPath)) {
|
|
929
|
-
return {
|
|
930
|
-
output: chalk9__default.default.yellow("\u5F53\u524D\u5DE5\u4F5C\u6D41\u6B63\u5728\u7B49\u5F85\u89C4\u683C\u786E\u8BA4") + chalk9__default.default.gray(`
|
|
931
|
-
|
|
932
|
-
\u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9__default.default.gray(`
|
|
933
|
-
\u53D8\u66F4ID: ${existingState.id}`) + chalk9__default.default.cyan("\n\n\u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210:") + chalk9__default.default.white(`
|
|
934
|
-
${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4\u89C4\u683C\u540E\u7EE7\u7EED:") + chalk9__default.default.gray("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.gray("\n /opsx:status - \u67E5\u770B\u8BE6\u60C5")
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
return {
|
|
939
|
-
output: chalk9__default.default.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.white(`
|
|
940
|
-
|
|
941
|
-
\u{1F4CB} ${existingState.title || existingState.id}`) + chalk9__default.default.gray(`
|
|
942
|
-
\u7C7B\u578B: ${existingState.type} | \u590D\u6742\u5EA6: ${existingState.complexity}/10`) + chalk9__default.default.cyan(`
|
|
943
|
-
|
|
944
|
-
\u8FDB\u5EA6: ${existingState.steps.map((s) => {
|
|
945
|
-
const icon = s.status === "completed" ? "\u2713" : s.status === "running" ? "\u25CF" : "\u25CB";
|
|
946
|
-
return `${icon} ${s.step}`;
|
|
947
|
-
}).join(" \u2192 ")}`) + chalk9__default.default.yellow("\n\n\u53EF\u7528\u547D\u4EE4:") + chalk9__default.default.white("\n /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u8BE6\u60C5") + chalk9__default.default.white("\n /opsx:cancel - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41")
|
|
948
|
-
};
|
|
949
528
|
}
|
|
529
|
+
} catch {
|
|
950
530
|
}
|
|
951
|
-
const
|
|
952
|
-
if (!requirement) {
|
|
953
|
-
return {
|
|
954
|
-
output: chalk9__default.default.red("\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0") + chalk9__default.default.gray("\n\u7528\u6CD5: /new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray("\n\u9009\u9879:") + chalk9__default.default.gray("\n --simple \u5F3A\u5236\u4F7F\u7528\u7B80\u5355\u6D41\u7A0B") + chalk9__default.default.gray("\n --complex \u5F3A\u5236\u4F7F\u7528\u590D\u6742\u6D41\u7A0B")
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
return newFeature({ requirement, forceComplexity }, workingDir, workflowEngine);
|
|
958
|
-
}
|
|
959
|
-
async function newFeature(options, workingDir, workflowEngine) {
|
|
960
|
-
const cwd = workingDir || process.cwd();
|
|
961
|
-
const { requirement, forceComplexity } = options;
|
|
962
|
-
const lines = [];
|
|
531
|
+
const devStandardsPath = path4__namespace.join(workingDir, ".sf-cli", "norms", "devstanded.md");
|
|
963
532
|
try {
|
|
964
|
-
const stats = await fs4__namespace.stat(
|
|
965
|
-
if (
|
|
966
|
-
|
|
967
|
-
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
968
|
-
};
|
|
533
|
+
const stats = await fs4__namespace.stat(devStandardsPath);
|
|
534
|
+
if (stats.size <= MAX_FILE_SIZE2) {
|
|
535
|
+
context.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8");
|
|
969
536
|
}
|
|
970
537
|
} catch {
|
|
971
|
-
return {
|
|
972
|
-
output: chalk9__default.default.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
|
|
973
|
-
};
|
|
974
538
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
const
|
|
983
|
-
|
|
984
|
-
lines.push(chalk9__default.default.gray(` \u6D41\u7A0B\u7C7B\u578B: ${analysis.recommendation === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`));
|
|
985
|
-
for (const factor of analysis.factors) {
|
|
986
|
-
lines.push(chalk9__default.default.gray(` - ${factor}`));
|
|
539
|
+
return context;
|
|
540
|
+
}
|
|
541
|
+
function analyzeComplexity(requirement, context) {
|
|
542
|
+
let score = 3;
|
|
543
|
+
if (requirement.length > 100) score += 1;
|
|
544
|
+
if (requirement.length > 200) score += 1;
|
|
545
|
+
const complexKeywords = ["\u67B6\u6784", "\u91CD\u6784", "\u8FC1\u79FB", "\u96C6\u6210", "\u7CFB\u7EDF", "\u6A21\u5757", "\u5DE5\u4F5C\u6D41", "\u6D41\u7A0B", "\u6743\u9650", "\u5B89\u5168"];
|
|
546
|
+
for (const keyword of complexKeywords) {
|
|
547
|
+
if (requirement.includes(keyword)) score += 1;
|
|
987
548
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
if (!workflowEngine) {
|
|
992
|
-
await workflow.initialize(cwd);
|
|
549
|
+
const simpleKeywords = ["\u4FEE\u590D", "\u8C03\u6574", "\u4F18\u5316", "\u66F4\u65B0", "\u6DFB\u52A0", "\u6837\u5F0F", "\u6587\u672C"];
|
|
550
|
+
for (const keyword of simpleKeywords) {
|
|
551
|
+
if (requirement.includes(keyword)) score -= 0.5;
|
|
993
552
|
}
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
lines.push(chalk9__default.default.gray(` \u5DE5\u4F5C\u6D41: ${state.type}`));
|
|
999
|
-
lines.push("");
|
|
1000
|
-
lines.push(chalk9__default.default.cyan("\u{1F4DD} \u751F\u6210\u89C4\u683C\u62C6\u5206..."));
|
|
1001
|
-
const spec = await generateSpec(requirement, context, analysis, state.id);
|
|
1002
|
-
const specPath = await saveSpecFile(cwd, spec);
|
|
1003
|
-
lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
1004
|
-
lines.push(chalk9__default.default.gray(` \u8DEF\u5F84: ${specPath}`));
|
|
1005
|
-
lines.push("");
|
|
1006
|
-
lines.push(chalk9__default.default.cyan.bold("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
|
|
1007
|
-
lines.push(chalk9__default.default.white(`
|
|
1008
|
-
${spec.summary}`));
|
|
1009
|
-
if (spec.items.length > 0) {
|
|
1010
|
-
lines.push("");
|
|
1011
|
-
lines.push(chalk9__default.default.cyan(" \u4EFB\u52A1\u62C6\u5206:"));
|
|
1012
|
-
for (const item of spec.items) {
|
|
1013
|
-
const priorityIcon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
1014
|
-
lines.push(chalk9__default.default.gray(` ${priorityIcon} [${item.id}] ${item.title}`));
|
|
1015
|
-
}
|
|
553
|
+
const connectors = ["\u548C", "\u4EE5\u53CA", "\u540C\u65F6", "\u53E6\u5916", "\u6B64\u5916", "\u3001"];
|
|
554
|
+
for (const conn of connectors) {
|
|
555
|
+
const count = (requirement.match(new RegExp(conn, "g")) || []).length;
|
|
556
|
+
score += count * 0.3;
|
|
1016
557
|
}
|
|
1017
|
-
if (
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
558
|
+
if (requirement.match(/https?:\/\//)) score += 0.5;
|
|
559
|
+
if (!context.framework) score += 0.5;
|
|
560
|
+
return Math.max(1, Math.min(10, Math.round(score)));
|
|
561
|
+
}
|
|
562
|
+
function generateBDDScenarios(requirement, context, questions) {
|
|
563
|
+
const scenarios = [];
|
|
564
|
+
questions.find((q) => q.category === "ui" && q.answered)?.answer;
|
|
565
|
+
const interactionAnswer = questions.find((q) => q.category === "interaction" && q.answered)?.answer;
|
|
566
|
+
const edgeAnswer = questions.find((q) => q.category === "edge" && q.answered)?.answer;
|
|
567
|
+
const features = extractFeatures(requirement);
|
|
568
|
+
for (const feature of features) {
|
|
569
|
+
const scenario = {
|
|
570
|
+
feature: feature.title,
|
|
571
|
+
description: feature.description,
|
|
572
|
+
scenarios: []
|
|
573
|
+
};
|
|
574
|
+
scenario.scenarios.push({
|
|
575
|
+
name: `\u6B63\u5E38\u6D41\u7A0B: ${feature.title}`,
|
|
576
|
+
given: [`\u7528\u6237\u8FDB\u5165\u76F8\u5173\u9875\u9762`],
|
|
577
|
+
when: [`\u7528\u6237\u6267\u884C "${feature.title}" \u64CD\u4F5C`],
|
|
578
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5E76\u8FD4\u56DE\u9884\u671F\u7ED3\u679C`]
|
|
579
|
+
});
|
|
580
|
+
if (interactionAnswer) {
|
|
581
|
+
scenario.scenarios.push({
|
|
582
|
+
name: `\u4EA4\u4E92\u6D41\u7A0B: \u7528\u6237\u64CD\u4F5C`,
|
|
583
|
+
given: [`\u7528\u6237\u8FDB\u5165\u529F\u80FD\u754C\u9762`],
|
|
584
|
+
when: [`\u7528\u6237\u6309\u7167\u4EE5\u4E0B\u6D41\u7A0B\u64CD\u4F5C: ${interactionAnswer}`],
|
|
585
|
+
then: [`\u7CFB\u7EDF\u54CD\u5E94\u5E76\u5B8C\u6210\u529F\u80FD`]
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (feature.hasInput || edgeAnswer) {
|
|
589
|
+
scenario.scenarios.push({
|
|
590
|
+
name: `\u8FB9\u754C\u60C5\u51B5: \u5F02\u5E38\u5904\u7406`,
|
|
591
|
+
given: [`\u7528\u6237\u8FDB\u5165\u529F\u80FD\u754C\u9762`],
|
|
592
|
+
when: [`\u53D1\u751F\u5F02\u5E38\u60C5\u51B5${edgeAnswer ? `: ${edgeAnswer}` : ""}`],
|
|
593
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5F02\u5E38\u5E76\u7ED9\u51FA\u63D0\u793A`]
|
|
594
|
+
});
|
|
1022
595
|
}
|
|
596
|
+
scenarios.push(scenario);
|
|
1023
597
|
}
|
|
1024
|
-
|
|
1025
|
-
lines.push(chalk9__default.default.yellow.bold("\u23F3 \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
|
|
1026
|
-
lines.push("");
|
|
1027
|
-
lines.push(chalk9__default.default.white(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
|
|
1028
|
-
lines.push(chalk9__default.default.white(" n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210\u89C4\u683C"));
|
|
1029
|
-
lines.push(chalk9__default.default.gray(" \u6216\u8F93\u5165\u547D\u4EE4: /opsx:confirm spec-review"));
|
|
1030
|
-
return { output: lines.join("\n") };
|
|
598
|
+
return scenarios;
|
|
1031
599
|
}
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
};
|
|
1042
|
-
spec.summary = generateSummary(requirement);
|
|
1043
|
-
if (analysis.recommendation === "complex") {
|
|
1044
|
-
spec.items = generateComplexTasks(requirement, context, analysis);
|
|
1045
|
-
spec.architectureNotes = generateArchitectureNotes(requirement, context);
|
|
1046
|
-
} else {
|
|
1047
|
-
spec.items = generateSimpleTasks(requirement);
|
|
600
|
+
function extractFeatures(requirement) {
|
|
601
|
+
const features = [];
|
|
602
|
+
const urlMatch = requirement.match(/https?:\/\/[^\s]+/);
|
|
603
|
+
if (urlMatch) {
|
|
604
|
+
features.push({
|
|
605
|
+
title: "\u53C2\u8003\u754C\u9762\u5206\u6790",
|
|
606
|
+
description: `\u5206\u6790\u53C2\u8003\u754C\u9762 ${urlMatch[0]}`,
|
|
607
|
+
hasInput: false
|
|
608
|
+
});
|
|
1048
609
|
}
|
|
1049
|
-
spec.risks = generateRisks(requirement, context, analysis);
|
|
1050
|
-
spec.suggestions = generateSuggestions(requirement, context, analysis);
|
|
1051
|
-
return spec;
|
|
1052
|
-
}
|
|
1053
|
-
function generateSummary(requirement) {
|
|
1054
|
-
const firstSentence = requirement.split(/[。!?\n]/)[0];
|
|
1055
|
-
return firstSentence.length > 100 ? firstSentence.slice(0, 97) + "..." : firstSentence;
|
|
1056
|
-
}
|
|
1057
|
-
function generateComplexTasks(requirement, context, analysis) {
|
|
1058
|
-
const items = [];
|
|
1059
|
-
let itemId = 1;
|
|
1060
610
|
const featurePatterns = [
|
|
1061
|
-
{ pattern:
|
|
1062
|
-
{ pattern:
|
|
1063
|
-
{ pattern:
|
|
1064
|
-
{ pattern:
|
|
1065
|
-
{ pattern:
|
|
1066
|
-
{ pattern:
|
|
1067
|
-
{ pattern:
|
|
1068
|
-
{ pattern:
|
|
1069
|
-
{ pattern:
|
|
1070
|
-
{ pattern:
|
|
611
|
+
{ pattern: /排盘|计算|算法/, title: "\u6838\u5FC3\u7B97\u6CD5\u5B9E\u73B0", hasInput: true },
|
|
612
|
+
{ pattern: /界面|UI|页面|显示|展示/, title: "\u754C\u9762\u5F00\u53D1", hasInput: true },
|
|
613
|
+
{ pattern: /表格|列表/, title: "\u6570\u636E\u5C55\u793A", hasInput: false },
|
|
614
|
+
{ pattern: /图表|图形|可视化/, title: "\u56FE\u8868\u53EF\u89C6\u5316", hasInput: false },
|
|
615
|
+
{ pattern: /表单|输入/, title: "\u8868\u5355\u5904\u7406", hasInput: true },
|
|
616
|
+
{ pattern: /登录|注册|认证/, title: "\u7528\u6237\u8BA4\u8BC1", hasInput: true },
|
|
617
|
+
{ pattern: /接口|API/, title: "API \u63A5\u53E3", hasInput: false },
|
|
618
|
+
{ pattern: /存储|缓存/, title: "\u6570\u636E\u5B58\u50A8", hasInput: false },
|
|
619
|
+
{ pattern: /导出|下载/, title: "\u5BFC\u51FA\u529F\u80FD", hasInput: false },
|
|
620
|
+
{ pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", hasInput: true }
|
|
1071
621
|
];
|
|
1072
|
-
for (const { pattern, title,
|
|
622
|
+
for (const { pattern, title, hasInput } of featurePatterns) {
|
|
1073
623
|
if (pattern.test(requirement)) {
|
|
1074
|
-
|
|
1075
|
-
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
624
|
+
features.push({
|
|
1076
625
|
title,
|
|
1077
|
-
description: `${title}\u76F8\u5173\
|
|
1078
|
-
|
|
1079
|
-
dependencies: itemId > 1 ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
|
|
1080
|
-
estimatedComplexity: priority === "high" ? 3 : priority === "medium" ? 2 : 1
|
|
626
|
+
description: `${title}\u76F8\u5173\u529F\u80FD`,
|
|
627
|
+
hasInput
|
|
1081
628
|
});
|
|
1082
|
-
itemId++;
|
|
1083
629
|
}
|
|
1084
630
|
}
|
|
1085
|
-
if (
|
|
1086
|
-
|
|
1087
|
-
id: "T001",
|
|
1088
|
-
title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
|
|
1089
|
-
description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
|
|
1090
|
-
priority: "high",
|
|
1091
|
-
dependencies: [],
|
|
1092
|
-
estimatedComplexity: 2
|
|
1093
|
-
});
|
|
1094
|
-
items.push({
|
|
1095
|
-
id: "T002",
|
|
631
|
+
if (features.length === 0) {
|
|
632
|
+
features.push({
|
|
1096
633
|
title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
|
|
1097
634
|
description: requirement,
|
|
1098
|
-
|
|
1099
|
-
dependencies: ["T001"],
|
|
1100
|
-
estimatedComplexity: analysis.score
|
|
1101
|
-
});
|
|
1102
|
-
items.push({
|
|
1103
|
-
id: "T003",
|
|
1104
|
-
title: "\u6D4B\u8BD5\u4E0E\u9A8C\u8BC1",
|
|
1105
|
-
description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027",
|
|
1106
|
-
priority: "medium",
|
|
1107
|
-
dependencies: ["T002"],
|
|
1108
|
-
estimatedComplexity: 2
|
|
635
|
+
hasInput: true
|
|
1109
636
|
});
|
|
1110
637
|
}
|
|
1111
|
-
return
|
|
638
|
+
return features;
|
|
1112
639
|
}
|
|
1113
|
-
function
|
|
640
|
+
function generateSpecItems(requirement, context, bddScenarios, questions) {
|
|
1114
641
|
const items = [];
|
|
1115
|
-
let
|
|
1116
|
-
const
|
|
1117
|
-
const hasReferenceUrl = urlMatch !== null;
|
|
1118
|
-
if (hasReferenceUrl) {
|
|
1119
|
-
items.push({
|
|
1120
|
-
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
1121
|
-
title: "\u53C2\u8003\u754C\u9762\u5206\u6790",
|
|
1122
|
-
description: `\u5206\u6790\u53C2\u8003\u754C\u9762 ${urlMatch[0]} \u7684\u7ED3\u6784\u548C\u4EA4\u4E92\u903B\u8F91`,
|
|
1123
|
-
priority: "high",
|
|
1124
|
-
dependencies: [],
|
|
1125
|
-
estimatedComplexity: 2
|
|
1126
|
-
});
|
|
1127
|
-
itemId++;
|
|
1128
|
-
}
|
|
1129
|
-
const featurePatterns = [
|
|
1130
|
-
{ pattern: /排盘|计算|算法|公式/, title: "\u6838\u5FC3\u7B97\u6CD5\u5B9E\u73B0", desc: "\u5B9E\u73B0\u6838\u5FC3\u8BA1\u7B97\u903B\u8F91" },
|
|
1131
|
-
{ pattern: /界面|UI|页面|显示|展示/, title: "\u754C\u9762\u5F00\u53D1", desc: "\u5F00\u53D1\u7528\u6237\u754C\u9762\u7EC4\u4EF6" },
|
|
1132
|
-
{ pattern: /表格|列表|数据/, title: "\u6570\u636E\u5C55\u793A", desc: "\u5B9E\u73B0\u6570\u636E\u8868\u683C/\u5217\u8868\u7EC4\u4EF6" },
|
|
1133
|
-
{ pattern: /图表|图形|可视化/, title: "\u56FE\u8868\u53EF\u89C6\u5316", desc: "\u5B9E\u73B0\u56FE\u8868\u5C55\u793A\u529F\u80FD" },
|
|
1134
|
-
{ pattern: /表单|输入|提交/, title: "\u8868\u5355\u5904\u7406", desc: "\u5B9E\u73B0\u8868\u5355\u8F93\u5165\u548C\u9A8C\u8BC1" },
|
|
1135
|
-
{ pattern: /登录|注册|认证/, title: "\u7528\u6237\u8BA4\u8BC1", desc: "\u5B9E\u73B0\u7528\u6237\u8BA4\u8BC1\u529F\u80FD" },
|
|
1136
|
-
{ pattern: /接口|API|请求/, title: "API \u63A5\u53E3", desc: "\u5F00\u53D1\u540E\u7AEF\u63A5\u53E3\u5BF9\u63A5" },
|
|
1137
|
-
{ pattern: /存储|缓存|持久化/, title: "\u6570\u636E\u5B58\u50A8", desc: "\u5B9E\u73B0\u6570\u636E\u5B58\u50A8\u529F\u80FD" },
|
|
1138
|
-
{ pattern: /导出|下载|打印/, title: "\u5BFC\u51FA\u529F\u80FD", desc: "\u5B9E\u73B0\u6570\u636E\u5BFC\u51FA\u529F\u80FD" },
|
|
1139
|
-
{ pattern: /配置|设置|选项/, title: "\u914D\u7F6E\u7BA1\u7406", desc: "\u5B9E\u73B0\u914D\u7F6E\u9009\u9879\u529F\u80FD" }
|
|
1140
|
-
];
|
|
1141
|
-
const matchedFeatures = [];
|
|
1142
|
-
for (const { pattern, title, desc } of featurePatterns) {
|
|
1143
|
-
if (pattern.test(requirement)) {
|
|
1144
|
-
matchedFeatures.push({ title, desc });
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
for (const feature of matchedFeatures) {
|
|
1148
|
-
items.push({
|
|
1149
|
-
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
1150
|
-
title: feature.title,
|
|
1151
|
-
description: feature.desc,
|
|
1152
|
-
priority: "high",
|
|
1153
|
-
dependencies: hasReferenceUrl && itemId === 2 ? ["T001"] : itemId > (hasReferenceUrl ? 2 : 1) ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
|
|
1154
|
-
estimatedComplexity: 3
|
|
1155
|
-
});
|
|
1156
|
-
itemId++;
|
|
1157
|
-
}
|
|
1158
|
-
if (items.length === 0 || hasReferenceUrl && items.length === 1) {
|
|
1159
|
-
const requirementParts = requirement.split(/[,,。.!!??;;]/).filter((p) => p.trim());
|
|
1160
|
-
for (const part of requirementParts) {
|
|
1161
|
-
if (part.trim() && !part.includes("http")) {
|
|
1162
|
-
items.push({
|
|
1163
|
-
id: `T${itemId.toString().padStart(3, "0")}`,
|
|
1164
|
-
title: `\u5B9E\u73B0: ${part.trim().slice(0, 20)}${part.trim().length > 20 ? "..." : ""}`,
|
|
1165
|
-
description: part.trim(),
|
|
1166
|
-
priority: "high",
|
|
1167
|
-
dependencies: itemId > (hasReferenceUrl ? 2 : 1) ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
|
|
1168
|
-
estimatedComplexity: 2
|
|
1169
|
-
});
|
|
1170
|
-
itemId++;
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
if (items.length === 0) {
|
|
1175
|
-
items.push({
|
|
1176
|
-
id: "T001",
|
|
1177
|
-
title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
|
|
1178
|
-
description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
|
|
1179
|
-
priority: "high",
|
|
1180
|
-
dependencies: [],
|
|
1181
|
-
estimatedComplexity: 2
|
|
1182
|
-
});
|
|
642
|
+
let id = 1;
|
|
643
|
+
for (const scenario of bddScenarios) {
|
|
1183
644
|
items.push({
|
|
1184
|
-
id: "
|
|
1185
|
-
title:
|
|
1186
|
-
description:
|
|
1187
|
-
priority: "high",
|
|
1188
|
-
|
|
1189
|
-
|
|
645
|
+
id: `T${id.toString().padStart(3, "0")}`,
|
|
646
|
+
title: scenario.feature,
|
|
647
|
+
description: scenario.description,
|
|
648
|
+
priority: id <= 2 ? "high" : "medium",
|
|
649
|
+
files: [],
|
|
650
|
+
tests: []
|
|
1190
651
|
});
|
|
652
|
+
id++;
|
|
1191
653
|
}
|
|
1192
654
|
items.push({
|
|
1193
|
-
id: `T${
|
|
1194
|
-
title: "\u6D4B\u8BD5
|
|
1195
|
-
description: "\
|
|
655
|
+
id: `T${id.toString().padStart(3, "0")}`,
|
|
656
|
+
title: "\u5355\u5143\u6D4B\u8BD5",
|
|
657
|
+
description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\u786E\u4FDD\u529F\u80FD\u6B63\u786E\u6027",
|
|
1196
658
|
priority: "medium",
|
|
1197
|
-
|
|
1198
|
-
|
|
659
|
+
files: [],
|
|
660
|
+
tests: []
|
|
1199
661
|
});
|
|
1200
662
|
return items;
|
|
1201
663
|
}
|
|
1202
|
-
function
|
|
1203
|
-
const
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
if (requirement.includes("\u6A21\u5757") || requirement.includes("\u7EC4\u4EF6")) {
|
|
1208
|
-
notes.push("\u5EFA\u8BAE\u91C7\u7528\u6A21\u5757\u5316\u8BBE\u8BA1\uFF0C\u4FDD\u6301\u7EC4\u4EF6\u804C\u8D23\u5355\u4E00");
|
|
1209
|
-
}
|
|
1210
|
-
if (requirement.includes("API") || requirement.includes("\u63A5\u53E3")) {
|
|
1211
|
-
notes.push("API \u8BBE\u8BA1\u9700\u8003\u8651\u7248\u672C\u63A7\u5236\u548C\u5411\u540E\u517C\u5BB9");
|
|
1212
|
-
}
|
|
1213
|
-
if (context.structure.srcStructure) {
|
|
1214
|
-
notes.push(`\u73B0\u6709\u6E90\u7801\u7ED3\u6784: ${context.structure.srcStructure}`);
|
|
1215
|
-
}
|
|
1216
|
-
return notes;
|
|
1217
|
-
}
|
|
1218
|
-
function generateRisks(requirement, context, analysis) {
|
|
1219
|
-
const risks = [];
|
|
1220
|
-
if (!context.framework) {
|
|
1221
|
-
risks.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B\uFF0C\u53EF\u80FD\u5F71\u54CD\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u6027");
|
|
1222
|
-
}
|
|
1223
|
-
if (analysis.score >= 7) {
|
|
1224
|
-
risks.push("\u9700\u6C42\u590D\u6742\u5EA6\u8F83\u9AD8\uFF0C\u5EFA\u8BAE\u5206\u9636\u6BB5\u5B9E\u73B0");
|
|
1225
|
-
}
|
|
1226
|
-
if (requirement.includes("\u8FC1\u79FB") || requirement.includes("\u91CD\u6784")) {
|
|
1227
|
-
risks.push("\u6D89\u53CA\u73B0\u6709\u4EE3\u7801\u4FEE\u6539\uFF0C\u9700\u6CE8\u610F\u56DE\u5F52\u6D4B\u8BD5");
|
|
1228
|
-
}
|
|
1229
|
-
if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168")) {
|
|
1230
|
-
risks.push("\u6D89\u53CA\u5B89\u5168\u654F\u611F\u529F\u80FD\uFF0C\u9700\u8981\u989D\u5916\u5BA1\u67E5");
|
|
1231
|
-
}
|
|
1232
|
-
return risks;
|
|
1233
|
-
}
|
|
1234
|
-
function generateSuggestions(requirement, context, analysis) {
|
|
1235
|
-
const suggestions = [];
|
|
1236
|
-
if (context.norms.devStandards) {
|
|
1237
|
-
suggestions.push("\u9879\u76EE\u5DF2\u6709\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u9075\u5FAA\u73B0\u6709\u89C4\u8303");
|
|
1238
|
-
}
|
|
1239
|
-
if (analysis.recommendation === "complex") {
|
|
1240
|
-
suggestions.push("\u590D\u6742\u9700\u6C42\u5EFA\u8BAE\u5148\u8FDB\u884C\u6280\u672F\u8BC4\u5BA1");
|
|
1241
|
-
suggestions.push("\u5EFA\u8BAE\u8C03\u7528 $architect \u83B7\u53D6\u67B6\u6784\u5EFA\u8BAE");
|
|
1242
|
-
}
|
|
1243
|
-
if (context.techStack.length > 0) {
|
|
1244
|
-
suggestions.push(`\u6280\u672F\u6808: ${context.techStack.join(", ")}`);
|
|
1245
|
-
}
|
|
1246
|
-
return suggestions;
|
|
1247
|
-
}
|
|
1248
|
-
async function saveSpecFile(cwd, spec) {
|
|
1249
|
-
const changesDir = path4__namespace.join(cwd, "openspec", "changes");
|
|
1250
|
-
await fs4__namespace.mkdir(changesDir, { recursive: true });
|
|
1251
|
-
const specPath = path4__namespace.join(changesDir, `${spec.changeId}-spec.md`);
|
|
1252
|
-
const content = formatSpecFile(spec);
|
|
664
|
+
async function saveSpecFile(workingDir, session) {
|
|
665
|
+
const specDir = path4__namespace.join(workingDir, "openspec", "changes");
|
|
666
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
667
|
+
const specPath = path4__namespace.join(specDir, `${session.id}-spec.md`);
|
|
668
|
+
const content = formatSpecFile(session);
|
|
1253
669
|
await fs4__namespace.writeFile(specPath, content, "utf-8");
|
|
1254
670
|
return specPath;
|
|
1255
671
|
}
|
|
1256
|
-
function formatSpecFile(
|
|
672
|
+
function formatSpecFile(session) {
|
|
1257
673
|
const lines = [];
|
|
1258
|
-
lines.push(`#
|
|
674
|
+
lines.push(`# \u9700\u6C42\u89C4\u683C: ${session.requirement.slice(0, 50)}`);
|
|
1259
675
|
lines.push("");
|
|
1260
|
-
lines.push(`> \u53D8\u66F4ID: ${
|
|
1261
|
-
lines.push(`> \
|
|
676
|
+
lines.push(`> \u53D8\u66F4ID: ${session.id}`);
|
|
677
|
+
lines.push(`> \u9700\u6C42\u6E05\u6670\u5EA6: ${Math.round(session.clarityScore * 100)}%`);
|
|
678
|
+
lines.push(`> \u590D\u6742\u5EA6: ${session.complexity}/10`);
|
|
679
|
+
lines.push(`> \u751F\u6210\u65F6\u95F4: ${session.createdAt.toISOString()}`);
|
|
1262
680
|
lines.push("");
|
|
1263
681
|
lines.push("---");
|
|
1264
682
|
lines.push("");
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
lines.push(spec.requirement);
|
|
1268
|
-
lines.push("");
|
|
1269
|
-
lines.push("## \u4EFB\u52A1\u62C6\u5206");
|
|
1270
|
-
lines.push("");
|
|
1271
|
-
for (const item of spec.items) {
|
|
1272
|
-
const priorityLabel = item.priority === "high" ? "\u{1F534} \u9AD8" : item.priority === "medium" ? "\u{1F7E1} \u4E2D" : "\u{1F7E2} \u4F4E";
|
|
1273
|
-
lines.push(`### ${item.id}: ${item.title}`);
|
|
1274
|
-
lines.push("");
|
|
1275
|
-
lines.push(`- **\u4F18\u5148\u7EA7**: ${priorityLabel}`);
|
|
1276
|
-
lines.push(`- **\u63CF\u8FF0**: ${item.description}`);
|
|
1277
|
-
lines.push(`- **\u9884\u4F30\u590D\u6742\u5EA6**: ${item.estimatedComplexity}/5`);
|
|
1278
|
-
if (item.dependencies.length > 0) {
|
|
1279
|
-
lines.push(`- **\u4F9D\u8D56**: ${item.dependencies.join(", ")}`);
|
|
1280
|
-
}
|
|
683
|
+
if (session.refinedRequirement !== session.requirement) {
|
|
684
|
+
lines.push("## \u9700\u6C42\u8BE6\u60C5");
|
|
1281
685
|
lines.push("");
|
|
1282
|
-
|
|
1283
|
-
if (spec.architectureNotes.length > 0) {
|
|
1284
|
-
lines.push("## \u67B6\u6784\u8BF4\u660E");
|
|
686
|
+
lines.push(session.refinedRequirement);
|
|
1285
687
|
lines.push("");
|
|
1286
|
-
|
|
1287
|
-
lines.push(`- ${note}`);
|
|
1288
|
-
}
|
|
688
|
+
lines.push("---");
|
|
1289
689
|
lines.push("");
|
|
1290
690
|
}
|
|
1291
|
-
if (
|
|
1292
|
-
lines.push("## \
|
|
691
|
+
if (session.clarificationQuestions.some((q) => q.answered)) {
|
|
692
|
+
lines.push("## \u9700\u6C42\u6F84\u6E05");
|
|
1293
693
|
lines.push("");
|
|
1294
|
-
for (const
|
|
1295
|
-
|
|
694
|
+
for (const q of session.clarificationQuestions) {
|
|
695
|
+
if (q.answered) {
|
|
696
|
+
lines.push(`**Q: ${q.question}**`);
|
|
697
|
+
lines.push(`A: ${q.answer}`);
|
|
698
|
+
lines.push("");
|
|
699
|
+
}
|
|
1296
700
|
}
|
|
701
|
+
lines.push("---");
|
|
1297
702
|
lines.push("");
|
|
1298
703
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
704
|
+
lines.push("## BDD \u573A\u666F");
|
|
705
|
+
lines.push("");
|
|
706
|
+
for (const scenario of session.bddScenarios) {
|
|
707
|
+
lines.push(`### Feature: ${scenario.feature}`);
|
|
1301
708
|
lines.push("");
|
|
1302
|
-
for (const
|
|
1303
|
-
lines.push(
|
|
709
|
+
for (const s of scenario.scenarios) {
|
|
710
|
+
lines.push(`**Scenario: ${s.name}**`);
|
|
711
|
+
for (const g of s.given) lines.push(` Given ${g}`);
|
|
712
|
+
for (const w of s.when) lines.push(` When ${w}`);
|
|
713
|
+
for (const t of s.then) lines.push(` Then ${t}`);
|
|
714
|
+
lines.push("");
|
|
1304
715
|
}
|
|
1305
|
-
lines.push("");
|
|
1306
716
|
}
|
|
1307
|
-
lines.push("
|
|
717
|
+
lines.push("## \u4EFB\u52A1\u5217\u8868");
|
|
1308
718
|
lines.push("");
|
|
1309
|
-
|
|
719
|
+
for (const item of session.specItems) {
|
|
720
|
+
const priority = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
721
|
+
lines.push(`- [ ] ${priority} [${item.id}] ${item.title}`);
|
|
722
|
+
}
|
|
1310
723
|
lines.push("");
|
|
1311
|
-
lines.push("
|
|
1312
|
-
lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
|
|
724
|
+
lines.push("---");
|
|
1313
725
|
lines.push("");
|
|
1314
|
-
lines.push("**\u786E\u8BA4\
|
|
726
|
+
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
1315
727
|
return lines.join("\n");
|
|
1316
728
|
}
|
|
1317
|
-
function
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
return {
|
|
1330
|
-
requirement: filteredArgs.join(" ").trim(),
|
|
1331
|
-
forceComplexity
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
async function readProjectContext(cwd) {
|
|
1335
|
-
const defaultContext = {
|
|
1336
|
-
name: path4__namespace.basename(cwd),
|
|
1337
|
-
type: "unknown",
|
|
1338
|
-
framework: null,
|
|
1339
|
-
techStack: [],
|
|
1340
|
-
description: "",
|
|
1341
|
-
structure: {
|
|
1342
|
-
directories: [],
|
|
1343
|
-
keyFiles: [],
|
|
1344
|
-
srcStructure: ""
|
|
1345
|
-
},
|
|
1346
|
-
norms: {
|
|
1347
|
-
devStandards: "",
|
|
1348
|
-
patterns: "",
|
|
1349
|
-
weights: ""
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
const [agentsContext, configContext, normsContext, structureContext] = await Promise.all([
|
|
1353
|
-
readAgentsMd(cwd),
|
|
1354
|
-
readConfigYaml(cwd),
|
|
1355
|
-
readNorms(cwd),
|
|
1356
|
-
analyzeStructure(cwd)
|
|
1357
|
-
]);
|
|
1358
|
-
return {
|
|
1359
|
-
...defaultContext,
|
|
1360
|
-
...agentsContext,
|
|
1361
|
-
...configContext,
|
|
1362
|
-
norms: normsContext,
|
|
1363
|
-
structure: structureContext
|
|
1364
|
-
};
|
|
729
|
+
async function generateTests(workingDir, session) {
|
|
730
|
+
const testDir = path4__namespace.join(workingDir, "tests");
|
|
731
|
+
await fs4__namespace.mkdir(testDir, { recursive: true });
|
|
732
|
+
const testFiles = [];
|
|
733
|
+
for (const scenario of session.bddScenarios) {
|
|
734
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
735
|
+
const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
|
|
736
|
+
const content = generateTestFile(scenario);
|
|
737
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
738
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
739
|
+
}
|
|
740
|
+
return testFiles;
|
|
1365
741
|
}
|
|
1366
|
-
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
|
|
1380
|
-
}
|
|
1381
|
-
return {};
|
|
742
|
+
function generateTestFile(scenario) {
|
|
743
|
+
const lines = [];
|
|
744
|
+
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
745
|
+
lines.push("");
|
|
746
|
+
lines.push(`describe('${scenario.feature}', () => {`);
|
|
747
|
+
for (const s of scenario.scenarios) {
|
|
748
|
+
lines.push(` it('${s.name}', () => {`);
|
|
749
|
+
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
750
|
+
lines.push(` // When: ${s.when.join(", ")}`);
|
|
751
|
+
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
752
|
+
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
753
|
+
lines.push(` });`);
|
|
754
|
+
lines.push("");
|
|
1382
755
|
}
|
|
756
|
+
lines.push(`});`);
|
|
757
|
+
return lines.join("\n");
|
|
1383
758
|
}
|
|
1384
|
-
function
|
|
1385
|
-
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
const content = await fs4__namespace.readFile(configPath, "utf-8");
|
|
1417
|
-
return parseConfigYaml(content);
|
|
1418
|
-
} catch (e) {
|
|
1419
|
-
const err = e;
|
|
1420
|
-
if (err.code !== "ENOENT") {
|
|
1421
|
-
console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
|
|
1422
|
-
}
|
|
1423
|
-
return {};
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
function parseConfigYaml(content) {
|
|
1427
|
-
const context = {};
|
|
1428
|
-
const nameMatch = content.match(/name:\s*(.+)/);
|
|
1429
|
-
if (nameMatch) {
|
|
1430
|
-
context.name = nameMatch[1].trim();
|
|
1431
|
-
}
|
|
1432
|
-
const typeMatch = content.match(/type:\s*(.+)/);
|
|
1433
|
-
if (typeMatch) {
|
|
1434
|
-
context.type = typeMatch[1].trim();
|
|
1435
|
-
}
|
|
1436
|
-
const frameworkMatch = content.match(/framework:\s*(.+)/);
|
|
1437
|
-
if (frameworkMatch && frameworkMatch[1].trim() !== "null") {
|
|
1438
|
-
context.framework = frameworkMatch[1].trim();
|
|
1439
|
-
}
|
|
1440
|
-
return context;
|
|
1441
|
-
}
|
|
1442
|
-
async function readNorms(cwd) {
|
|
1443
|
-
const normsDir = path4__namespace.join(cwd, ".sf-cli", "norms");
|
|
1444
|
-
const norms = {
|
|
1445
|
-
devStandards: "",
|
|
1446
|
-
patterns: "",
|
|
1447
|
-
weights: ""
|
|
1448
|
-
};
|
|
1449
|
-
try {
|
|
1450
|
-
const devStandardsPath = path4__namespace.join(normsDir, "devstanded.md");
|
|
1451
|
-
norms.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8").catch(() => "");
|
|
1452
|
-
} catch {
|
|
1453
|
-
}
|
|
1454
|
-
try {
|
|
1455
|
-
const patternsPath = path4__namespace.join(normsDir, "patterns.json");
|
|
1456
|
-
norms.patterns = await fs4__namespace.readFile(patternsPath, "utf-8").catch(() => "");
|
|
1457
|
-
} catch {
|
|
1458
|
-
}
|
|
1459
|
-
try {
|
|
1460
|
-
const weightsPath = path4__namespace.join(normsDir, "weights.json");
|
|
1461
|
-
norms.weights = await fs4__namespace.readFile(weightsPath, "utf-8").catch(() => "");
|
|
1462
|
-
} catch {
|
|
1463
|
-
}
|
|
1464
|
-
return norms;
|
|
759
|
+
async function archiveWorkflow(workingDir) {
|
|
760
|
+
if (!activeSession) return;
|
|
761
|
+
const archiveDir = path4__namespace.join(workingDir, "openspec", "spec");
|
|
762
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
763
|
+
const archivePath = path4__namespace.join(archiveDir, `${activeSession.id}.md`);
|
|
764
|
+
const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
|
|
765
|
+
|
|
766
|
+
> \u5F52\u6863\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
767
|
+
> \u9700\u6C42\u6E05\u6670\u5EA6: ${Math.round(activeSession.clarityScore * 100)}%
|
|
768
|
+
> \u590D\u6742\u5EA6: ${activeSession.complexity}/10
|
|
769
|
+
|
|
770
|
+
## \u9700\u6C42\u8BE6\u60C5
|
|
771
|
+
|
|
772
|
+
${activeSession.refinedRequirement}
|
|
773
|
+
|
|
774
|
+
## \u5B8C\u6210\u60C5\u51B5
|
|
775
|
+
|
|
776
|
+
- [x] \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6
|
|
777
|
+
- [x] \u9700\u6C42\u6F84\u6E05
|
|
778
|
+
- [x] \u590D\u6742\u5EA6\u8BC4\u4F30
|
|
779
|
+
- [x] BDD \u573A\u666F\u62C6\u89E3
|
|
780
|
+
- [x] OpenSpec \u89C4\u683C
|
|
781
|
+
- [x] TDD \u6D4B\u8BD5\u751F\u6210
|
|
782
|
+
- [x] \u5F00\u53D1\u5B9E\u73B0
|
|
783
|
+
- [x] \u4EE3\u7801\u5BA1\u6838
|
|
784
|
+
|
|
785
|
+
## \u6D4B\u8BD5\u6587\u4EF6
|
|
786
|
+
|
|
787
|
+
${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
|
|
788
|
+
`;
|
|
789
|
+
await fs4__namespace.writeFile(archivePath, content, "utf-8");
|
|
1465
790
|
}
|
|
1466
|
-
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
srcStructure: ""
|
|
1471
|
-
};
|
|
1472
|
-
try {
|
|
1473
|
-
const entries = await fs4__namespace.readdir(cwd, { withFileTypes: true });
|
|
1474
|
-
for (const entry of entries) {
|
|
1475
|
-
if (entry.isDirectory() && !["node_modules", "dist", ".git", "build"].includes(entry.name)) {
|
|
1476
|
-
structure.directories.push(entry.name);
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
|
-
const keyFiles = [
|
|
1480
|
-
"package.json",
|
|
1481
|
-
"tsconfig.json",
|
|
1482
|
-
"AGENTS.md",
|
|
1483
|
-
"README.md"
|
|
1484
|
-
];
|
|
1485
|
-
for (const file of keyFiles) {
|
|
1486
|
-
const filePath = path4__namespace.join(cwd, file);
|
|
1487
|
-
try {
|
|
1488
|
-
await fs4__namespace.access(filePath);
|
|
1489
|
-
structure.keyFiles.push(file);
|
|
1490
|
-
} catch {
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
const srcDir = path4__namespace.join(cwd, "src");
|
|
1494
|
-
try {
|
|
1495
|
-
const srcEntries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
|
|
1496
|
-
structure.srcStructure = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name).join("/");
|
|
1497
|
-
} catch {
|
|
1498
|
-
}
|
|
1499
|
-
} catch (e) {
|
|
1500
|
-
}
|
|
1501
|
-
return { structure };
|
|
791
|
+
function generateSessionId() {
|
|
792
|
+
const timestamp = Date.now().toString(36);
|
|
793
|
+
const random = Math.random().toString(36).slice(2, 6);
|
|
794
|
+
return `WF-${timestamp}-${random}`.toUpperCase();
|
|
1502
795
|
}
|
|
1503
|
-
function
|
|
1504
|
-
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1507
|
-
score += 1;
|
|
1508
|
-
factors.push("\u9700\u6C42\u63CF\u8FF0\u8F83\u957F");
|
|
1509
|
-
}
|
|
1510
|
-
const complexKeywords = ["\u67B6\u6784", "\u91CD\u6784", "\u8FC1\u79FB", "\u96C6\u6210", "\u7CFB\u7EDF", "\u6A21\u5757", "\u5DE5\u4F5C\u6D41", "\u6D41\u7A0B"];
|
|
1511
|
-
const negationWords = ["\u4E0D\u9700\u8981", "\u4E0D\u7528", "\u65E0\u9700", "\u907F\u514D"];
|
|
1512
|
-
for (const keyword of complexKeywords) {
|
|
1513
|
-
if (requirement.includes(keyword)) {
|
|
1514
|
-
const keywordIndex = requirement.indexOf(keyword);
|
|
1515
|
-
const contextStart = Math.max(0, keywordIndex - 10);
|
|
1516
|
-
const contextText = requirement.slice(contextStart, keywordIndex);
|
|
1517
|
-
const hasNegation = negationWords.some((neg) => contextText.includes(neg));
|
|
1518
|
-
if (!hasNegation) {
|
|
1519
|
-
score += 1;
|
|
1520
|
-
factors.push(`\u6D89\u53CA"${keyword}"`);
|
|
1521
|
-
}
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
const simpleKeywords = ["\u4FEE\u590D", "\u8C03\u6574", "\u4F18\u5316", "\u66F4\u65B0", "\u4FEE\u6539", "\u6DFB\u52A0", "\u6837\u5F0F"];
|
|
1525
|
-
for (const keyword of simpleKeywords) {
|
|
1526
|
-
if (requirement.includes(keyword)) {
|
|
1527
|
-
score -= 0.5;
|
|
1528
|
-
factors.push(`\u7B80\u5355\u4FEE\u6539"${keyword}"`);
|
|
1529
|
-
}
|
|
1530
|
-
}
|
|
1531
|
-
const featureCount = (requirement.match(/和|以及|同时|另外|此外/g) || []).length;
|
|
1532
|
-
if (featureCount > 0) {
|
|
1533
|
-
score += featureCount * 0.5;
|
|
1534
|
-
factors.push(`\u6D89\u53CA\u591A\u4E2A\u529F\u80FD\u70B9 (${featureCount + 1}\u4E2A)`);
|
|
1535
|
-
}
|
|
1536
|
-
if (!context.framework) {
|
|
1537
|
-
score += 0.5;
|
|
1538
|
-
factors.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B");
|
|
1539
|
-
}
|
|
1540
|
-
if (requirement.includes("\u6570\u636E") || requirement.includes("\u63A5\u53E3") || requirement.includes("API")) {
|
|
1541
|
-
score += 1;
|
|
1542
|
-
factors.push("\u6D89\u53CA\u6570\u636E\u4EA4\u4E92");
|
|
1543
|
-
}
|
|
1544
|
-
if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168") || requirement.includes("\u8BA4\u8BC1")) {
|
|
1545
|
-
score += 1.5;
|
|
1546
|
-
factors.push("\u6D89\u53CA\u6743\u9650\u6216\u5B89\u5168");
|
|
1547
|
-
}
|
|
1548
|
-
score = Math.max(1, Math.min(10, Math.round(score)));
|
|
1549
|
-
return {
|
|
1550
|
-
score,
|
|
1551
|
-
factors: factors.length > 0 ? factors : ["\u9700\u6C42\u76F8\u5BF9\u7B80\u5355"],
|
|
1552
|
-
recommendation: score >= COMPLEXITY_THRESHOLD ? "complex" : "simple"
|
|
1553
|
-
};
|
|
796
|
+
function generateComplexityBar(score) {
|
|
797
|
+
const filled = Math.round(score / 2);
|
|
798
|
+
const empty = 5 - filled;
|
|
799
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(empty);
|
|
1554
800
|
}
|
|
1555
|
-
function
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
801
|
+
function getPhaseLabel(phase) {
|
|
802
|
+
const labels = {
|
|
803
|
+
context: "\u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6",
|
|
804
|
+
clarify: "\u9700\u6C42\u6F84\u6E05",
|
|
805
|
+
analysis: "\u590D\u6742\u5EA6\u8BC4\u4F30",
|
|
806
|
+
bdd: "BDD \u573A\u666F\u62C6\u89E3",
|
|
807
|
+
spec: "OpenSpec \u89C4\u683C",
|
|
808
|
+
tdd: "TDD \u6D4B\u8BD5\u751F\u6210",
|
|
809
|
+
develop: "\u5F00\u53D1\u5B9E\u73B0",
|
|
810
|
+
review: "\u4EE3\u7801\u5BA1\u6838"
|
|
1560
811
|
};
|
|
812
|
+
return labels[phase];
|
|
1561
813
|
}
|
|
1562
|
-
function
|
|
1563
|
-
|
|
1564
|
-
if (firstSentence.length <= 50) {
|
|
1565
|
-
return firstSentence;
|
|
1566
|
-
}
|
|
1567
|
-
return requirement.slice(0, 47) + "...";
|
|
814
|
+
function getActiveSession() {
|
|
815
|
+
return activeSession;
|
|
1568
816
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
changeId,
|
|
1572
|
-
requirement,
|
|
1573
|
-
summary: extractTitle(requirement),
|
|
1574
|
-
items: analysis.recommendation === "complex" ? generateComplexTasks(requirement, context, analysis) : generateSimpleTasks(requirement),
|
|
1575
|
-
architectureNotes: generateArchitectureNotes(requirement, context),
|
|
1576
|
-
risks: generateRisks(requirement, context, analysis),
|
|
1577
|
-
suggestions: generateSuggestions(requirement, context, analysis)
|
|
1578
|
-
};
|
|
1579
|
-
return formatSpecFile(spec);
|
|
817
|
+
function clearActiveSession() {
|
|
818
|
+
activeSession = null;
|
|
1580
819
|
}
|
|
1581
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, new_default;
|
|
820
|
+
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1582
821
|
var init_new = __esm({
|
|
1583
822
|
"src/commands/new.ts"() {
|
|
1584
823
|
init_cjs_shims();
|
|
1585
|
-
init_workflow();
|
|
1586
824
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
1587
825
|
COMPLEXITY_THRESHOLD = 6;
|
|
1588
|
-
|
|
826
|
+
CLARITY_THRESHOLD = 0.6;
|
|
827
|
+
activeSession = null;
|
|
828
|
+
new_default = handleNew;
|
|
1589
829
|
}
|
|
1590
830
|
});
|
|
1591
831
|
|
|
@@ -5111,118 +4351,956 @@ var AgentScheduler = class {
|
|
|
5111
4351
|
message: `\u53EF\u624B\u52A8\u8C03\u7528 $${rule.agent} \u6267\u884C\u4EFB\u52A1`
|
|
5112
4352
|
};
|
|
5113
4353
|
}
|
|
5114
|
-
return this.executeAgent(rule, state, options?.context);
|
|
4354
|
+
return this.executeAgent(rule, state, options?.context);
|
|
4355
|
+
}
|
|
4356
|
+
/**
|
|
4357
|
+
* 执行 Agent
|
|
4358
|
+
*/
|
|
4359
|
+
async executeAgent(config, state, additionalContext) {
|
|
4360
|
+
const agent = getAgentDefinition(config.agent);
|
|
4361
|
+
if (!agent) {
|
|
4362
|
+
return {
|
|
4363
|
+
scheduled: false,
|
|
4364
|
+
agent: config.agent,
|
|
4365
|
+
strategy: "auto",
|
|
4366
|
+
message: `Agent ${config.agent} \u4E0D\u5B58\u5728`
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
const context = {
|
|
4370
|
+
taskId: state.id,
|
|
4371
|
+
workflowId: state.id,
|
|
4372
|
+
workflowStep: state.currentStep,
|
|
4373
|
+
requirement: state.requirement,
|
|
4374
|
+
files: state.artifacts,
|
|
4375
|
+
context: additionalContext?.context,
|
|
4376
|
+
norms: additionalContext?.norms,
|
|
4377
|
+
previousOutput: additionalContext?.previousOutput
|
|
4378
|
+
};
|
|
4379
|
+
try {
|
|
4380
|
+
const result = await this.executor.execute({
|
|
4381
|
+
agentId: config.agent,
|
|
4382
|
+
context
|
|
4383
|
+
});
|
|
4384
|
+
const scheduleResult = {
|
|
4385
|
+
scheduled: true,
|
|
4386
|
+
agent: config.agent,
|
|
4387
|
+
strategy: "auto",
|
|
4388
|
+
result,
|
|
4389
|
+
message: result.success ? `$${config.agent} \u6267\u884C\u5B8C\u6210` : `$${config.agent} \u6267\u884C\u5931\u8D25: ${result.error}`
|
|
4390
|
+
};
|
|
4391
|
+
this.lastScheduleResult = scheduleResult;
|
|
4392
|
+
if (this.onSchedule) {
|
|
4393
|
+
this.onSchedule(scheduleResult);
|
|
4394
|
+
}
|
|
4395
|
+
return scheduleResult;
|
|
4396
|
+
} catch (error) {
|
|
4397
|
+
const err = error;
|
|
4398
|
+
return {
|
|
4399
|
+
scheduled: false,
|
|
4400
|
+
agent: config.agent,
|
|
4401
|
+
strategy: "auto",
|
|
4402
|
+
message: `$${config.agent} \u6267\u884C\u5F02\u5E38: ${err.message}`
|
|
4403
|
+
};
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
/**
|
|
4407
|
+
* 获取阶段的调度配置
|
|
4408
|
+
*/
|
|
4409
|
+
getScheduleConfig(step) {
|
|
4410
|
+
return SCHEDULE_RULES[step];
|
|
4411
|
+
}
|
|
4412
|
+
/**
|
|
4413
|
+
* 获取阶段的推荐 Agent
|
|
4414
|
+
*/
|
|
4415
|
+
getRecommendedAgent(step) {
|
|
4416
|
+
const rule = SCHEDULE_RULES[step];
|
|
4417
|
+
return rule?.agent;
|
|
4418
|
+
}
|
|
4419
|
+
/**
|
|
4420
|
+
* 获取所有调度规则
|
|
4421
|
+
*/
|
|
4422
|
+
getAllScheduleRules() {
|
|
4423
|
+
return { ...SCHEDULE_RULES };
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* 获取上次调度结果
|
|
4427
|
+
*/
|
|
4428
|
+
getLastScheduleResult() {
|
|
4429
|
+
return this.lastScheduleResult;
|
|
4430
|
+
}
|
|
4431
|
+
/**
|
|
4432
|
+
* 判断阶段是否需要自动调度
|
|
4433
|
+
*/
|
|
4434
|
+
shouldAutoSchedule(step) {
|
|
4435
|
+
const rule = SCHEDULE_RULES[step];
|
|
4436
|
+
return rule?.strategy === "auto";
|
|
4437
|
+
}
|
|
4438
|
+
/**
|
|
4439
|
+
* 获取阶段可用的 Agent 列表
|
|
4440
|
+
*/
|
|
4441
|
+
getAvailableAgents(step) {
|
|
4442
|
+
const agents = getAgentsForWorkflowStep(step);
|
|
4443
|
+
return agents.map((a) => a.id);
|
|
4444
|
+
}
|
|
4445
|
+
};
|
|
4446
|
+
function createAgentScheduler(executor) {
|
|
4447
|
+
return new AgentScheduler(executor);
|
|
4448
|
+
}
|
|
4449
|
+
function getScheduleRuleDescription(step) {
|
|
4450
|
+
const rule = SCHEDULE_RULES[step];
|
|
4451
|
+
if (!rule) {
|
|
4452
|
+
return `\u9636\u6BB5 ${step} \u65E0\u81EA\u52A8\u8C03\u5EA6\u89C4\u5219`;
|
|
4453
|
+
}
|
|
4454
|
+
const agent = getAgentDefinition(rule.agent);
|
|
4455
|
+
const strategyText = {
|
|
4456
|
+
"auto": "\u81EA\u52A8\u6267\u884C",
|
|
4457
|
+
"recommend": "\u63A8\u8350\u6267\u884C",
|
|
4458
|
+
"manual": "\u624B\u52A8\u6267\u884C"
|
|
4459
|
+
};
|
|
4460
|
+
return `${strategyText[rule.strategy]} $${rule.agent} (${agent?.name})`;
|
|
4461
|
+
}
|
|
4462
|
+
|
|
4463
|
+
// src/workflow/index.ts
|
|
4464
|
+
init_cjs_shims();
|
|
4465
|
+
|
|
4466
|
+
// src/workflow/checkpoint.ts
|
|
4467
|
+
init_cjs_shims();
|
|
4468
|
+
var DEFAULT_CONFIRMATION_POINTS = [
|
|
4469
|
+
{
|
|
4470
|
+
type: "spec-review",
|
|
4471
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
4472
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
4473
|
+
triggerStep: "explore",
|
|
4474
|
+
targetStep: "new",
|
|
4475
|
+
required: true
|
|
4476
|
+
},
|
|
4477
|
+
{
|
|
4478
|
+
type: "spec-review",
|
|
4479
|
+
name: "\u89C4\u683C\u786E\u8BA4",
|
|
4480
|
+
description: "\u89C4\u683C\u62C6\u5206\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u89C4\u683C\u6587\u4EF6\u540E\u7EE7\u7EED",
|
|
4481
|
+
triggerStep: "propose",
|
|
4482
|
+
targetStep: "apply",
|
|
4483
|
+
required: true
|
|
4484
|
+
},
|
|
4485
|
+
{
|
|
4486
|
+
type: "architecture",
|
|
4487
|
+
name: "\u67B6\u6784\u8C03\u6574\u786E\u8BA4",
|
|
4488
|
+
description: "\u8BBE\u8BA1\u65B9\u6848\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u67B6\u6784\u8BBE\u8BA1\u662F\u5426\u5408\u7406",
|
|
4489
|
+
triggerStep: "new",
|
|
4490
|
+
targetStep: "continue",
|
|
4491
|
+
required: true
|
|
4492
|
+
},
|
|
4493
|
+
{
|
|
4494
|
+
type: "code-review",
|
|
4495
|
+
name: "\u4EE3\u7801\u5BA1\u67E5\u786E\u8BA4",
|
|
4496
|
+
description: "\u4EE3\u7801\u5BA1\u67E5\u5DF2\u5B8C\u6210\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u53EF\u4EE5\u5F52\u6863",
|
|
4497
|
+
triggerStep: "apply",
|
|
4498
|
+
targetStep: "archive",
|
|
4499
|
+
required: true
|
|
4500
|
+
}
|
|
4501
|
+
];
|
|
4502
|
+
var ROLLBACK_RULES = {
|
|
4503
|
+
"explore": [],
|
|
4504
|
+
// 初始阶段不可回滚
|
|
4505
|
+
"new": ["explore"],
|
|
4506
|
+
// 可回滚到 explore
|
|
4507
|
+
"continue": ["new", "explore"],
|
|
4508
|
+
// 可回滚到 new 或 explore
|
|
4509
|
+
"propose": ["propose"],
|
|
4510
|
+
// 可重新生成规格(回滚到自身)
|
|
4511
|
+
"apply": ["new", "explore", "propose"],
|
|
4512
|
+
// 代码审查未通过,可回滚到 new/explore (复杂) 或 propose (简单)
|
|
4513
|
+
"archive": []
|
|
4514
|
+
// 已归档不可回滚
|
|
4515
|
+
};
|
|
4516
|
+
var ConfirmationManager = class {
|
|
4517
|
+
confirmationPoints;
|
|
4518
|
+
confirmations = /* @__PURE__ */ new Map();
|
|
4519
|
+
constructor(customPoints) {
|
|
4520
|
+
this.confirmationPoints = customPoints || DEFAULT_CONFIRMATION_POINTS;
|
|
4521
|
+
}
|
|
4522
|
+
/**
|
|
4523
|
+
* 获取指定阶段的确认点
|
|
4524
|
+
*/
|
|
4525
|
+
getConfirmationPointForTransition(from, to) {
|
|
4526
|
+
return this.confirmationPoints.find(
|
|
4527
|
+
(point) => point.triggerStep === from && point.targetStep === to
|
|
4528
|
+
);
|
|
4529
|
+
}
|
|
4530
|
+
/**
|
|
4531
|
+
* 检查是否需要确认
|
|
4532
|
+
*/
|
|
4533
|
+
needsConfirmation(from, to) {
|
|
4534
|
+
const point = this.getConfirmationPointForTransition(from, to);
|
|
4535
|
+
return point !== void 0 && point.required;
|
|
4536
|
+
}
|
|
4537
|
+
/**
|
|
4538
|
+
* 获取确认点详情
|
|
4539
|
+
*/
|
|
4540
|
+
getConfirmationPoint(type) {
|
|
4541
|
+
return this.confirmationPoints.find((point) => point.type === type);
|
|
4542
|
+
}
|
|
4543
|
+
/**
|
|
4544
|
+
* 记录确认
|
|
4545
|
+
*/
|
|
4546
|
+
confirm(type, comment) {
|
|
4547
|
+
const status = {
|
|
4548
|
+
type,
|
|
4549
|
+
confirmed: true,
|
|
4550
|
+
confirmedAt: /* @__PURE__ */ new Date(),
|
|
4551
|
+
comment
|
|
4552
|
+
};
|
|
4553
|
+
this.confirmations.set(type, status);
|
|
4554
|
+
return status;
|
|
4555
|
+
}
|
|
4556
|
+
/**
|
|
4557
|
+
* 获取确认状态
|
|
4558
|
+
*/
|
|
4559
|
+
getConfirmationStatus(type) {
|
|
4560
|
+
return this.confirmations.get(type);
|
|
4561
|
+
}
|
|
4562
|
+
/**
|
|
4563
|
+
* 检查确认点是否已确认
|
|
4564
|
+
*/
|
|
4565
|
+
isConfirmed(type) {
|
|
4566
|
+
const status = this.confirmations.get(type);
|
|
4567
|
+
return status?.confirmed ?? false;
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* 清除确认状态(用于回滚后)
|
|
4571
|
+
*/
|
|
4572
|
+
clearConfirmation(type) {
|
|
4573
|
+
this.confirmations.delete(type);
|
|
4574
|
+
}
|
|
4575
|
+
/**
|
|
4576
|
+
* 清除所有确认状态
|
|
4577
|
+
*/
|
|
4578
|
+
clearAllConfirmations() {
|
|
4579
|
+
this.confirmations.clear();
|
|
4580
|
+
}
|
|
4581
|
+
/**
|
|
4582
|
+
* 获取所有确认点
|
|
4583
|
+
*/
|
|
4584
|
+
getAllConfirmationPoints() {
|
|
4585
|
+
return [...this.confirmationPoints];
|
|
4586
|
+
}
|
|
4587
|
+
/**
|
|
4588
|
+
* 获取指定阶段需要清除的确认点
|
|
4589
|
+
*/
|
|
4590
|
+
getConfirmationsToClear(targetStep) {
|
|
4591
|
+
const types = [];
|
|
4592
|
+
for (const point of this.confirmationPoints) {
|
|
4593
|
+
const stepOrder = ["explore", "new", "continue", "propose", "apply", "archive"];
|
|
4594
|
+
const targetIndex = stepOrder.indexOf(targetStep);
|
|
4595
|
+
const triggerIndex = stepOrder.indexOf(point.triggerStep);
|
|
4596
|
+
if (triggerIndex >= targetIndex) {
|
|
4597
|
+
types.push(point.type);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
return types;
|
|
4601
|
+
}
|
|
4602
|
+
};
|
|
4603
|
+
function generateConfirmationPrompt(point) {
|
|
4604
|
+
let prompt2 = `\u{1F4CB} ${point.name}
|
|
4605
|
+
|
|
4606
|
+
`;
|
|
4607
|
+
prompt2 += `${point.description}
|
|
4608
|
+
|
|
4609
|
+
`;
|
|
4610
|
+
prompt2 += `\u786E\u8BA4\u9009\u9879:
|
|
4611
|
+
`;
|
|
4612
|
+
prompt2 += ` y - \u786E\u8BA4\u7EE7\u7EED
|
|
4613
|
+
`;
|
|
4614
|
+
prompt2 += ` n - \u62D2\u7EDD\uFF0C\u6682\u505C\u6D41\u7A0B
|
|
4615
|
+
`;
|
|
4616
|
+
prompt2 += ` r - \u8BF7\u6C42\u4FEE\u6539
|
|
4617
|
+
`;
|
|
4618
|
+
if (!point.required) {
|
|
4619
|
+
prompt2 += `
|
|
4620
|
+
\u63D0\u793A: \u6B64\u786E\u8BA4\u70B9\u4E3A\u975E\u5FC5\u987B\u9879`;
|
|
4621
|
+
}
|
|
4622
|
+
return prompt2;
|
|
4623
|
+
}
|
|
4624
|
+
function generateRollbackPrompt(fromStep) {
|
|
4625
|
+
const allowedTargets = ROLLBACK_RULES[fromStep] || [];
|
|
4626
|
+
if (allowedTargets.length === 0) {
|
|
4627
|
+
return `\u5F53\u524D\u9636\u6BB5 ${fromStep} \u4E0D\u652F\u6301\u56DE\u6EDA`;
|
|
4628
|
+
}
|
|
4629
|
+
let prompt2 = `\u{1F504} \u56DE\u6EDA\u9009\u9879
|
|
4630
|
+
|
|
4631
|
+
`;
|
|
4632
|
+
prompt2 += `\u5F53\u524D\u9636\u6BB5: ${fromStep}
|
|
4633
|
+
`;
|
|
4634
|
+
prompt2 += `\u53EF\u56DE\u6EDA\u5230:
|
|
4635
|
+
`;
|
|
4636
|
+
for (const target of allowedTargets) {
|
|
4637
|
+
prompt2 += ` ${target}
|
|
4638
|
+
`;
|
|
4639
|
+
}
|
|
4640
|
+
prompt2 += `
|
|
4641
|
+
\u8BF7\u9009\u62E9\u56DE\u6EDA\u76EE\u6807\u9636\u6BB5`;
|
|
4642
|
+
return prompt2;
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4645
|
+
// src/workflow/index.ts
|
|
4646
|
+
var TRANSITIONS = {
|
|
4647
|
+
"explore": ["new"],
|
|
4648
|
+
"new": ["continue", "apply"],
|
|
4649
|
+
"continue": ["apply", "continue"],
|
|
4650
|
+
"propose": ["apply"],
|
|
4651
|
+
"apply": ["archive"],
|
|
4652
|
+
"archive": []
|
|
4653
|
+
};
|
|
4654
|
+
var COMPLEX_STEPS = ["explore", "new", "continue", "apply", "archive"];
|
|
4655
|
+
var SIMPLE_STEPS = ["propose", "apply", "archive"];
|
|
4656
|
+
var WorkflowEngine = class {
|
|
4657
|
+
state = null;
|
|
4658
|
+
projectPath = "";
|
|
4659
|
+
openspecPath = "";
|
|
4660
|
+
confirmationManager;
|
|
4661
|
+
snapshots = /* @__PURE__ */ new Map();
|
|
4662
|
+
projectContext = "";
|
|
4663
|
+
// AGENTS.md 内容
|
|
4664
|
+
projectConfig = "";
|
|
4665
|
+
// config.yaml 内容
|
|
4666
|
+
devStandards = "";
|
|
4667
|
+
// devstanded.md 内容
|
|
4668
|
+
constructor() {
|
|
4669
|
+
this.confirmationManager = new ConfirmationManager();
|
|
4670
|
+
}
|
|
4671
|
+
/**
|
|
4672
|
+
* 初始化工作流引擎
|
|
4673
|
+
*/
|
|
4674
|
+
async initialize(projectPath) {
|
|
4675
|
+
this.projectPath = projectPath;
|
|
4676
|
+
this.openspecPath = path4__namespace.join(projectPath, "openspec");
|
|
4677
|
+
await this.ensureDirectories();
|
|
4678
|
+
await this.loadProjectContext();
|
|
4679
|
+
await this.restoreState();
|
|
4680
|
+
await this.restoreSnapshots();
|
|
4681
|
+
}
|
|
4682
|
+
/**
|
|
4683
|
+
* 加载项目上下文(AGENTS.md 和 config.yaml)
|
|
4684
|
+
*/
|
|
4685
|
+
async loadProjectContext() {
|
|
4686
|
+
const agentsMdPath = path4__namespace.join(this.projectPath, "AGENTS.md");
|
|
4687
|
+
try {
|
|
4688
|
+
this.projectContext = await fs4__namespace.readFile(agentsMdPath, "utf-8");
|
|
4689
|
+
} catch {
|
|
4690
|
+
this.projectContext = "";
|
|
4691
|
+
}
|
|
4692
|
+
const configPath = path4__namespace.join(this.openspecPath, "config.yaml");
|
|
4693
|
+
try {
|
|
4694
|
+
this.projectConfig = await fs4__namespace.readFile(configPath, "utf-8");
|
|
4695
|
+
} catch {
|
|
4696
|
+
this.projectConfig = "";
|
|
4697
|
+
}
|
|
4698
|
+
const devstandedPath = path4__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
4699
|
+
try {
|
|
4700
|
+
this.devStandards = await fs4__namespace.readFile(devstandedPath, "utf-8");
|
|
4701
|
+
} catch {
|
|
4702
|
+
this.devStandards = "";
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
/**
|
|
4706
|
+
* 获取项目上下文
|
|
4707
|
+
*/
|
|
4708
|
+
getProjectContext() {
|
|
4709
|
+
return {
|
|
4710
|
+
agentsMd: this.projectContext,
|
|
4711
|
+
configYaml: this.projectConfig,
|
|
4712
|
+
devStandards: this.devStandards
|
|
4713
|
+
};
|
|
4714
|
+
}
|
|
4715
|
+
/**
|
|
4716
|
+
* 获取规格文件路径
|
|
4717
|
+
*/
|
|
4718
|
+
getSpecFilePath() {
|
|
4719
|
+
if (!this.state) return null;
|
|
4720
|
+
return path4__namespace.join(this.openspecPath, "changes", `${this.state.id}-spec.md`);
|
|
4721
|
+
}
|
|
4722
|
+
/**
|
|
4723
|
+
* 检查规格文件是否存在
|
|
4724
|
+
*/
|
|
4725
|
+
async hasSpecFile() {
|
|
4726
|
+
const specPath = this.getSpecFilePath();
|
|
4727
|
+
if (!specPath) return false;
|
|
4728
|
+
try {
|
|
4729
|
+
await fs4__namespace.access(specPath);
|
|
4730
|
+
return true;
|
|
4731
|
+
} catch {
|
|
4732
|
+
return false;
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4735
|
+
/**
|
|
4736
|
+
* 启动新工作流
|
|
4737
|
+
*/
|
|
4738
|
+
async start(requirement, complexity, options) {
|
|
4739
|
+
const type = complexity >= 6 ? "complex" : "simple";
|
|
4740
|
+
const steps = type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
4741
|
+
const initialStep = steps[0];
|
|
4742
|
+
const changeId = await this.generateChangeId();
|
|
4743
|
+
this.state = {
|
|
4744
|
+
id: changeId,
|
|
4745
|
+
type,
|
|
4746
|
+
currentStep: initialStep,
|
|
4747
|
+
steps: steps.map((step, index) => ({
|
|
4748
|
+
step,
|
|
4749
|
+
status: index === 0 ? "running" : "pending",
|
|
4750
|
+
startedAt: index === 0 ? /* @__PURE__ */ new Date() : void 0
|
|
4751
|
+
})),
|
|
4752
|
+
requirement,
|
|
4753
|
+
complexity,
|
|
4754
|
+
title: options?.title || requirement.slice(0, 50),
|
|
4755
|
+
artifacts: [],
|
|
4756
|
+
status: "running",
|
|
4757
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
4758
|
+
};
|
|
4759
|
+
this.snapshots.clear();
|
|
4760
|
+
this.confirmationManager.clearAllConfirmations();
|
|
4761
|
+
await this.createSnapshot();
|
|
4762
|
+
await this.createChangeRecord();
|
|
4763
|
+
await this.saveState();
|
|
4764
|
+
return this.state;
|
|
4765
|
+
}
|
|
4766
|
+
/**
|
|
4767
|
+
* 检查转换是否需要确认
|
|
4768
|
+
*/
|
|
4769
|
+
checkConfirmationNeeded(from, to) {
|
|
4770
|
+
const point = this.confirmationManager.getConfirmationPointForTransition(from, to);
|
|
4771
|
+
if (!point) {
|
|
4772
|
+
return { required: false, confirmed: true };
|
|
4773
|
+
}
|
|
4774
|
+
const confirmed = this.confirmationManager.isConfirmed(point.type);
|
|
4775
|
+
return {
|
|
4776
|
+
required: point.required,
|
|
4777
|
+
confirmed,
|
|
4778
|
+
point
|
|
4779
|
+
};
|
|
4780
|
+
}
|
|
4781
|
+
/**
|
|
4782
|
+
* 确认检查点
|
|
4783
|
+
*/
|
|
4784
|
+
confirm(type, comment) {
|
|
4785
|
+
this.confirmationManager.confirm(type, comment);
|
|
4786
|
+
}
|
|
4787
|
+
/**
|
|
4788
|
+
* 获取确认点信息
|
|
4789
|
+
*/
|
|
4790
|
+
getConfirmationPoint(type) {
|
|
4791
|
+
return this.confirmationManager.getConfirmationPoint(type);
|
|
4792
|
+
}
|
|
4793
|
+
/**
|
|
4794
|
+
* 获取当前转换需要的确认点
|
|
4795
|
+
*/
|
|
4796
|
+
getCurrentConfirmationPoint() {
|
|
4797
|
+
if (!this.state) return void 0;
|
|
4798
|
+
const allowed = this.getAllowedTransitions();
|
|
4799
|
+
for (const target of allowed) {
|
|
4800
|
+
const point = this.confirmationManager.getConfirmationPointForTransition(
|
|
4801
|
+
this.state.currentStep,
|
|
4802
|
+
target
|
|
4803
|
+
);
|
|
4804
|
+
if (point && !this.confirmationManager.isConfirmed(point.type)) {
|
|
4805
|
+
return point;
|
|
4806
|
+
}
|
|
4807
|
+
}
|
|
4808
|
+
return void 0;
|
|
4809
|
+
}
|
|
4810
|
+
/**
|
|
4811
|
+
* 执行状态转换
|
|
4812
|
+
*/
|
|
4813
|
+
async transition(targetStep, reason) {
|
|
4814
|
+
if (!this.state) {
|
|
4815
|
+
throw new Error("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41");
|
|
4816
|
+
}
|
|
4817
|
+
const currentStep = this.state.currentStep;
|
|
4818
|
+
const allowedTargets = TRANSITIONS[currentStep];
|
|
4819
|
+
if (!allowedTargets.includes(targetStep)) {
|
|
4820
|
+
throw new Error(
|
|
4821
|
+
`\u65E0\u6548\u7684\u72B6\u6001\u8F6C\u6362: ${currentStep} \u2192 ${targetStep}\u3002\u5141\u8BB8\u7684\u8F6C\u6362: ${allowedTargets.join(", ")}`
|
|
4822
|
+
);
|
|
4823
|
+
}
|
|
4824
|
+
const confirmationResult = this.checkConfirmationNeeded(currentStep, targetStep);
|
|
4825
|
+
if (confirmationResult.required && !confirmationResult.confirmed) {
|
|
4826
|
+
throw new ConfirmationRequiredError(
|
|
4827
|
+
`\u9700\u8981\u786E\u8BA4: ${confirmationResult.point?.name}`,
|
|
4828
|
+
confirmationResult.point
|
|
4829
|
+
);
|
|
4830
|
+
}
|
|
4831
|
+
const currentStepRecord = this.state.steps.find((s) => s.step === currentStep);
|
|
4832
|
+
if (currentStepRecord) {
|
|
4833
|
+
currentStepRecord.status = "completed";
|
|
4834
|
+
currentStepRecord.completedAt = /* @__PURE__ */ new Date();
|
|
4835
|
+
}
|
|
4836
|
+
const targetStepRecord = this.state.steps.find((s) => s.step === targetStep);
|
|
4837
|
+
if (targetStepRecord) {
|
|
4838
|
+
targetStepRecord.status = "running";
|
|
4839
|
+
targetStepRecord.startedAt = /* @__PURE__ */ new Date();
|
|
4840
|
+
}
|
|
4841
|
+
this.state.currentStep = targetStep;
|
|
4842
|
+
this.state.updatedAt = /* @__PURE__ */ new Date();
|
|
4843
|
+
await this.createSnapshot();
|
|
4844
|
+
await this.saveState();
|
|
4845
|
+
await this.updateChangeRecord();
|
|
4846
|
+
return {
|
|
4847
|
+
from: currentStep,
|
|
4848
|
+
to: targetStep,
|
|
4849
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
4850
|
+
reason
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4853
|
+
/**
|
|
4854
|
+
* 获取可回滚的目标阶段
|
|
4855
|
+
*/
|
|
4856
|
+
getRollbackTargets() {
|
|
4857
|
+
if (!this.state) return [];
|
|
4858
|
+
const allTargets = ROLLBACK_RULES[this.state.currentStep] || [];
|
|
4859
|
+
const workflowSteps = this.state.type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
4860
|
+
return allTargets.filter((target) => workflowSteps.includes(target));
|
|
4861
|
+
}
|
|
4862
|
+
/**
|
|
4863
|
+
* 检查是否可以回滚到指定阶段
|
|
4864
|
+
*/
|
|
4865
|
+
canRollbackTo(targetStep) {
|
|
4866
|
+
const targets = this.getRollbackTargets();
|
|
4867
|
+
return targets.includes(targetStep);
|
|
4868
|
+
}
|
|
4869
|
+
/**
|
|
4870
|
+
* 执行回滚
|
|
4871
|
+
*/
|
|
4872
|
+
async rollback(targetStep, reason, description) {
|
|
4873
|
+
if (!this.state) {
|
|
4874
|
+
return {
|
|
4875
|
+
success: false,
|
|
4876
|
+
from: "explore",
|
|
4877
|
+
to: null,
|
|
4878
|
+
message: "\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41"
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
const currentStep = this.state.currentStep;
|
|
4882
|
+
const allowedTargets = ROLLBACK_RULES[currentStep];
|
|
4883
|
+
if (!allowedTargets || !allowedTargets.includes(targetStep)) {
|
|
4884
|
+
return {
|
|
4885
|
+
success: false,
|
|
4886
|
+
from: currentStep,
|
|
4887
|
+
to: targetStep,
|
|
4888
|
+
message: `\u65E0\u6CD5\u4ECE ${currentStep} \u56DE\u6EDA\u5230 ${targetStep}`
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
const snapshot = this.snapshots.get(targetStep);
|
|
4892
|
+
if (!snapshot) {
|
|
4893
|
+
return {
|
|
4894
|
+
success: false,
|
|
4895
|
+
from: currentStep,
|
|
4896
|
+
to: targetStep,
|
|
4897
|
+
message: `\u627E\u4E0D\u5230\u9636\u6BB5 ${targetStep} \u7684\u5FEB\u7167`
|
|
4898
|
+
};
|
|
4899
|
+
}
|
|
4900
|
+
const previousState = this.state;
|
|
4901
|
+
this.state = {
|
|
4902
|
+
...snapshot.state,
|
|
4903
|
+
createdAt: previousState.createdAt,
|
|
4904
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
4905
|
+
};
|
|
4906
|
+
const stepOrder = this.state.type === "complex" ? COMPLEX_STEPS : SIMPLE_STEPS;
|
|
4907
|
+
const targetIndex = stepOrder.indexOf(targetStep);
|
|
4908
|
+
for (let i = 0; i < this.state.steps.length; i++) {
|
|
4909
|
+
const step = this.state.steps[i];
|
|
4910
|
+
const stepIndex = stepOrder.indexOf(step.step);
|
|
4911
|
+
if (stepIndex < targetIndex) {
|
|
4912
|
+
step.status = "completed";
|
|
4913
|
+
} else if (stepIndex === targetIndex) {
|
|
4914
|
+
step.status = "running";
|
|
4915
|
+
step.startedAt = /* @__PURE__ */ new Date();
|
|
4916
|
+
step.completedAt = void 0;
|
|
4917
|
+
} else {
|
|
4918
|
+
step.status = "pending";
|
|
4919
|
+
step.startedAt = void 0;
|
|
4920
|
+
step.completedAt = void 0;
|
|
4921
|
+
}
|
|
4922
|
+
}
|
|
4923
|
+
this.state.artifacts = snapshot.artifacts;
|
|
4924
|
+
const confirmationsToClear = this.confirmationManager.getConfirmationsToClear(targetStep);
|
|
4925
|
+
for (const type of confirmationsToClear) {
|
|
4926
|
+
this.confirmationManager.clearConfirmation(type);
|
|
4927
|
+
}
|
|
4928
|
+
await this.saveState();
|
|
4929
|
+
await this.updateChangeRecord("rolled-back");
|
|
4930
|
+
return {
|
|
4931
|
+
success: true,
|
|
4932
|
+
from: currentStep,
|
|
4933
|
+
to: targetStep,
|
|
4934
|
+
message: `\u5DF2\u56DE\u6EDA: ${currentStep} \u2192 ${targetStep}\uFF0C\u539F\u56E0: ${description || reason}`,
|
|
4935
|
+
snapshot
|
|
4936
|
+
};
|
|
4937
|
+
}
|
|
4938
|
+
/**
|
|
4939
|
+
* 获取回滚原因描述
|
|
4940
|
+
*/
|
|
4941
|
+
getRollbackReasonDescription(reason) {
|
|
4942
|
+
const descriptions = {
|
|
4943
|
+
"code-review-failed": "\u4EE3\u7801\u5BA1\u67E5\u672A\u901A\u8FC7",
|
|
4944
|
+
"requirement-changed": "\u9700\u6C42\u53D8\u66F4",
|
|
4945
|
+
"design-issue": "\u8BBE\u8BA1\u95EE\u9898",
|
|
4946
|
+
"user-request": "\u7528\u6237\u8BF7\u6C42"
|
|
4947
|
+
};
|
|
4948
|
+
return descriptions[reason];
|
|
4949
|
+
}
|
|
4950
|
+
/**
|
|
4951
|
+
* 获取当前状态
|
|
4952
|
+
*/
|
|
4953
|
+
getState() {
|
|
4954
|
+
return this.state;
|
|
4955
|
+
}
|
|
4956
|
+
/**
|
|
4957
|
+
* 获取所有活跃工作流
|
|
4958
|
+
*/
|
|
4959
|
+
async getAllActiveWorkflows() {
|
|
4960
|
+
const workflows = [];
|
|
4961
|
+
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
4962
|
+
try {
|
|
4963
|
+
const files = await fs4__namespace.readdir(changesDir);
|
|
4964
|
+
for (const file of files) {
|
|
4965
|
+
if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
|
|
4966
|
+
const filePath = path4__namespace.join(changesDir, file);
|
|
4967
|
+
const content = await fs4__namespace.readFile(filePath, "utf-8");
|
|
4968
|
+
const state = this.parseChangeRecord(content);
|
|
4969
|
+
if (state && state.status === "running") {
|
|
4970
|
+
workflows.push(state);
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
} catch {
|
|
4974
|
+
}
|
|
4975
|
+
if (this.state && !workflows.find((w) => w.id === this.state?.id)) {
|
|
4976
|
+
workflows.unshift(this.state);
|
|
4977
|
+
}
|
|
4978
|
+
return workflows;
|
|
5115
4979
|
}
|
|
5116
4980
|
/**
|
|
5117
|
-
*
|
|
4981
|
+
* 解析变更记录
|
|
5118
4982
|
*/
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
4983
|
+
parseChangeRecord(content) {
|
|
4984
|
+
try {
|
|
4985
|
+
const idMatch = content.match(/^id:\s*(.+)$/m);
|
|
4986
|
+
const titleMatch = content.match(/^title:\s*(.+)$/m);
|
|
4987
|
+
const statusMatch = content.match(/^status:\s*(.+)$/m);
|
|
4988
|
+
const complexityMatch = content.match(/^complexity:\s*(\d+)/m);
|
|
4989
|
+
const workflowMatch = content.match(/^workflow:\s*(.+)$/m);
|
|
4990
|
+
const requirementMatch = content.match(/## 变更概述\s*\n+([\s\S]+?)(?=\n##|$)/);
|
|
4991
|
+
if (!idMatch || !titleMatch) return null;
|
|
5122
4992
|
return {
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
4993
|
+
id: idMatch[1].trim(),
|
|
4994
|
+
title: titleMatch[1].trim(),
|
|
4995
|
+
status: statusMatch?.[1].trim() || "running",
|
|
4996
|
+
requirement: requirementMatch?.[1].trim() || "",
|
|
4997
|
+
complexity: parseInt(complexityMatch?.[1] || "5", 10),
|
|
4998
|
+
type: workflowMatch?.[1].trim() || "simple",
|
|
4999
|
+
currentStep: "propose",
|
|
5000
|
+
// 默认值,实际值需要从状态文件读取
|
|
5001
|
+
steps: [],
|
|
5002
|
+
artifacts: [],
|
|
5003
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
5127
5004
|
};
|
|
5005
|
+
} catch {
|
|
5006
|
+
return null;
|
|
5128
5007
|
}
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
};
|
|
5008
|
+
}
|
|
5009
|
+
/**
|
|
5010
|
+
* 切换到指定工作流
|
|
5011
|
+
*/
|
|
5012
|
+
async switchTo(changeId) {
|
|
5013
|
+
if (this.state) {
|
|
5014
|
+
await this.saveState();
|
|
5015
|
+
}
|
|
5016
|
+
const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
|
|
5139
5017
|
try {
|
|
5140
|
-
const
|
|
5141
|
-
|
|
5142
|
-
|
|
5018
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5019
|
+
this.state = JSON.parse(content, (key, value) => {
|
|
5020
|
+
if (key.endsWith("At") && typeof value === "string") {
|
|
5021
|
+
return new Date(value);
|
|
5022
|
+
}
|
|
5023
|
+
return value;
|
|
5143
5024
|
});
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5025
|
+
await this.restoreSnapshots();
|
|
5026
|
+
return true;
|
|
5027
|
+
} catch {
|
|
5028
|
+
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
5029
|
+
const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
|
|
5030
|
+
try {
|
|
5031
|
+
const content = await fs4__namespace.readFile(changeFile, "utf-8");
|
|
5032
|
+
const parsed = this.parseChangeRecord(content);
|
|
5033
|
+
if (parsed && parsed.status === "running") {
|
|
5034
|
+
this.state = parsed;
|
|
5035
|
+
return true;
|
|
5036
|
+
}
|
|
5037
|
+
} catch {
|
|
5154
5038
|
}
|
|
5155
|
-
return
|
|
5156
|
-
} catch (error) {
|
|
5157
|
-
const err = error;
|
|
5158
|
-
return {
|
|
5159
|
-
scheduled: false,
|
|
5160
|
-
agent: config.agent,
|
|
5161
|
-
strategy: "auto",
|
|
5162
|
-
message: `$${config.agent} \u6267\u884C\u5F02\u5E38: ${err.message}`
|
|
5163
|
-
};
|
|
5039
|
+
return false;
|
|
5164
5040
|
}
|
|
5165
5041
|
}
|
|
5166
5042
|
/**
|
|
5167
|
-
*
|
|
5043
|
+
* 获取允许的下一步
|
|
5168
5044
|
*/
|
|
5169
|
-
|
|
5170
|
-
return
|
|
5045
|
+
getAllowedTransitions() {
|
|
5046
|
+
if (!this.state) return [];
|
|
5047
|
+
return TRANSITIONS[this.state.currentStep] || [];
|
|
5171
5048
|
}
|
|
5172
5049
|
/**
|
|
5173
|
-
*
|
|
5050
|
+
* 获取快照历史
|
|
5174
5051
|
*/
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5052
|
+
getSnapshots() {
|
|
5053
|
+
return Array.from(this.snapshots.values()).sort(
|
|
5054
|
+
(a, b) => a.timestamp.getTime() - b.timestamp.getTime()
|
|
5055
|
+
);
|
|
5178
5056
|
}
|
|
5179
5057
|
/**
|
|
5180
|
-
*
|
|
5058
|
+
* 完成当前步骤
|
|
5181
5059
|
*/
|
|
5182
|
-
|
|
5183
|
-
|
|
5060
|
+
async completeCurrentStep(artifacts) {
|
|
5061
|
+
if (!this.state) return;
|
|
5062
|
+
const currentStepRecord = this.state.steps.find(
|
|
5063
|
+
(s) => s.step === this.state.currentStep
|
|
5064
|
+
);
|
|
5065
|
+
if (currentStepRecord) {
|
|
5066
|
+
currentStepRecord.status = "completed";
|
|
5067
|
+
currentStepRecord.completedAt = /* @__PURE__ */ new Date();
|
|
5068
|
+
}
|
|
5069
|
+
if (artifacts) {
|
|
5070
|
+
this.state.artifacts.push(...artifacts);
|
|
5071
|
+
}
|
|
5072
|
+
await this.createSnapshot();
|
|
5073
|
+
await this.saveState();
|
|
5074
|
+
}
|
|
5075
|
+
/**
|
|
5076
|
+
* 归档工作流
|
|
5077
|
+
*/
|
|
5078
|
+
async archive(summary) {
|
|
5079
|
+
if (!this.state) return;
|
|
5080
|
+
const changeId = this.state.id;
|
|
5081
|
+
for (const step of this.state.steps) {
|
|
5082
|
+
if (step.status !== "completed") {
|
|
5083
|
+
step.status = "completed";
|
|
5084
|
+
step.completedAt = /* @__PURE__ */ new Date();
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
this.state.status = "completed";
|
|
5088
|
+
this.state.completedAt = /* @__PURE__ */ new Date();
|
|
5089
|
+
await this.createSpecDocument(summary);
|
|
5090
|
+
await this.updateChangeRecord("archived");
|
|
5091
|
+
await this.saveState();
|
|
5092
|
+
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
5093
|
+
const archiveDir = path4__namespace.join(changesDir, "archive");
|
|
5094
|
+
const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
|
|
5095
|
+
const archiveFile = path4__namespace.join(archiveDir, `${changeId}.md`);
|
|
5096
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5097
|
+
await fs4__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
5098
|
+
});
|
|
5099
|
+
this.state = null;
|
|
5100
|
+
this.snapshots.clear();
|
|
5101
|
+
this.confirmationManager.clearAllConfirmations();
|
|
5184
5102
|
}
|
|
5185
5103
|
/**
|
|
5186
|
-
*
|
|
5104
|
+
* 取消工作流
|
|
5187
5105
|
*/
|
|
5188
|
-
|
|
5189
|
-
|
|
5106
|
+
async cancel(reason) {
|
|
5107
|
+
if (!this.state) return;
|
|
5108
|
+
this.state.status = "cancelled";
|
|
5109
|
+
this.state.cancelledAt = /* @__PURE__ */ new Date();
|
|
5110
|
+
this.state.cancelReason = reason;
|
|
5111
|
+
await this.updateChangeRecord("cancelled");
|
|
5112
|
+
await this.saveState();
|
|
5113
|
+
this.state = null;
|
|
5114
|
+
this.snapshots.clear();
|
|
5115
|
+
this.confirmationManager.clearAllConfirmations();
|
|
5190
5116
|
}
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
const
|
|
5196
|
-
|
|
5117
|
+
// ==================== 私有方法 ====================
|
|
5118
|
+
async ensureDirectories() {
|
|
5119
|
+
const changesDir = path4__namespace.join(this.openspecPath, "changes");
|
|
5120
|
+
const archiveDir = path4__namespace.join(changesDir, "archive");
|
|
5121
|
+
const specDir = path4__namespace.join(this.openspecPath, "spec");
|
|
5122
|
+
const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
|
|
5123
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5124
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
5125
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5126
|
+
}
|
|
5127
|
+
async restoreState() {
|
|
5128
|
+
const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5129
|
+
let activeId = null;
|
|
5130
|
+
try {
|
|
5131
|
+
const activeContent = await fs4__namespace.readFile(activePath, "utf-8");
|
|
5132
|
+
const activeData = JSON.parse(activeContent);
|
|
5133
|
+
activeId = activeData.activeId;
|
|
5134
|
+
} catch {
|
|
5135
|
+
}
|
|
5136
|
+
if (activeId) {
|
|
5137
|
+
const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
5138
|
+
try {
|
|
5139
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5140
|
+
this.state = JSON.parse(content, (key, value) => {
|
|
5141
|
+
if (key.endsWith("At") && typeof value === "string") {
|
|
5142
|
+
return new Date(value);
|
|
5143
|
+
}
|
|
5144
|
+
return value;
|
|
5145
|
+
});
|
|
5146
|
+
return;
|
|
5147
|
+
} catch {
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
const oldStatePath = path4__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
5151
|
+
try {
|
|
5152
|
+
const content = await fs4__namespace.readFile(oldStatePath, "utf-8");
|
|
5153
|
+
this.state = JSON.parse(content, (key, value) => {
|
|
5154
|
+
if (key.endsWith("At") && typeof value === "string") {
|
|
5155
|
+
return new Date(value);
|
|
5156
|
+
}
|
|
5157
|
+
return value;
|
|
5158
|
+
});
|
|
5159
|
+
if (this.state) {
|
|
5160
|
+
await this.saveState();
|
|
5161
|
+
await fs4__namespace.unlink(oldStatePath).catch(() => {
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
} catch (e) {
|
|
5165
|
+
const err = e;
|
|
5166
|
+
if (err.code !== "ENOENT") {
|
|
5167
|
+
console.warn("\u8B66\u544A: \u5DE5\u4F5C\u6D41\u72B6\u6001\u6587\u4EF6\u5DF2\u635F\u574F\uFF0C\u5C06\u91CD\u65B0\u5F00\u59CB");
|
|
5168
|
+
}
|
|
5169
|
+
this.state = null;
|
|
5170
|
+
}
|
|
5197
5171
|
}
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
const
|
|
5203
|
-
|
|
5172
|
+
async saveState() {
|
|
5173
|
+
if (!this.state) return;
|
|
5174
|
+
const statesDir = path4__namespace.join(this.openspecPath, ".workflow-states");
|
|
5175
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5176
|
+
const statePath = path4__namespace.join(statesDir, `${this.state.id}.json`);
|
|
5177
|
+
await fs4__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
5178
|
+
const activePath = path4__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5179
|
+
await fs4__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
5204
5180
|
}
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5181
|
+
async restoreSnapshots() {
|
|
5182
|
+
const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5183
|
+
try {
|
|
5184
|
+
const content = await fs4__namespace.readFile(snapshotsPath, "utf-8");
|
|
5185
|
+
const data = JSON.parse(content, (key, value) => {
|
|
5186
|
+
if (key === "timestamp" && typeof value === "string") {
|
|
5187
|
+
return new Date(value);
|
|
5188
|
+
}
|
|
5189
|
+
return value;
|
|
5190
|
+
});
|
|
5191
|
+
if (Array.isArray(data)) {
|
|
5192
|
+
this.snapshots = new Map(data.map((s) => [s.step, s]));
|
|
5193
|
+
}
|
|
5194
|
+
} catch {
|
|
5195
|
+
this.snapshots = /* @__PURE__ */ new Map();
|
|
5196
|
+
}
|
|
5213
5197
|
}
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5198
|
+
async saveSnapshots() {
|
|
5199
|
+
const snapshotsPath = path4__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5200
|
+
const data = Array.from(this.snapshots.values());
|
|
5201
|
+
await fs4__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
5202
|
+
}
|
|
5203
|
+
async createSnapshot() {
|
|
5204
|
+
if (!this.state) return;
|
|
5205
|
+
const snapshot = {
|
|
5206
|
+
step: this.state.currentStep,
|
|
5207
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
5208
|
+
state: JSON.parse(JSON.stringify(this.state)),
|
|
5209
|
+
artifacts: [...this.state.artifacts]
|
|
5210
|
+
};
|
|
5211
|
+
this.snapshots.set(snapshot.step, snapshot);
|
|
5212
|
+
await this.saveSnapshots();
|
|
5213
|
+
}
|
|
5214
|
+
async generateChangeId() {
|
|
5215
|
+
const timestamp = Date.now().toString(36);
|
|
5216
|
+
const random = Math.random().toString(36).slice(2, 6);
|
|
5217
|
+
return `CHG-${timestamp}-${random}`.toUpperCase();
|
|
5218
|
+
}
|
|
5219
|
+
async createChangeRecord() {
|
|
5220
|
+
if (!this.state) return;
|
|
5221
|
+
const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5222
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5223
|
+
}
|
|
5224
|
+
async updateChangeRecord(status) {
|
|
5225
|
+
if (!this.state) return;
|
|
5226
|
+
if (status) {
|
|
5227
|
+
this.state.status = status;
|
|
5228
|
+
}
|
|
5229
|
+
const changePath = path4__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5230
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5231
|
+
}
|
|
5232
|
+
formatChangeRecord() {
|
|
5233
|
+
if (!this.state) return "";
|
|
5234
|
+
const formatStepTime = (date) => {
|
|
5235
|
+
if (!date) return "-";
|
|
5236
|
+
if (typeof date === "string") return date;
|
|
5237
|
+
return date.toISOString();
|
|
5238
|
+
};
|
|
5239
|
+
return `# Change: ${this.state.title}
|
|
5222
5240
|
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5241
|
+
id: ${this.state.id}
|
|
5242
|
+
title: ${this.state.title}
|
|
5243
|
+
status: ${this.state.status}
|
|
5244
|
+
created: ${this.state.createdAt.toISOString()}
|
|
5245
|
+
${this.state.completedAt ? `completed: ${formatStepTime(this.state.completedAt)}` : ""}
|
|
5246
|
+
complexity: ${this.state.complexity}/10
|
|
5247
|
+
workflow: ${this.state.type}
|
|
5248
|
+
spec: spec/${this.state.id}.md
|
|
5249
|
+
|
|
5250
|
+
---
|
|
5251
|
+
|
|
5252
|
+
## \u53D8\u66F4\u6982\u8FF0
|
|
5253
|
+
|
|
5254
|
+
${this.state.requirement}
|
|
5255
|
+
|
|
5256
|
+
## \u5DE5\u4F5C\u6D41\u6267\u884C\u8BB0\u5F55
|
|
5257
|
+
|
|
5258
|
+
| \u9636\u6BB5 | \u5F00\u59CB\u65F6\u95F4 | \u7ED3\u675F\u65F6\u95F4 | \u72B6\u6001 |
|
|
5259
|
+
|------|----------|----------|------|
|
|
5260
|
+
${this.state.steps.map((s) => `| ${s.step} | ${formatStepTime(s.startedAt)} | ${formatStepTime(s.completedAt)} | ${s.status} |`).join("\n")}
|
|
5261
|
+
|
|
5262
|
+
## \u4EA7\u7269\u6587\u4EF6
|
|
5263
|
+
|
|
5264
|
+
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
5265
|
+
`;
|
|
5266
|
+
}
|
|
5267
|
+
async createSpecDocument(summary) {
|
|
5268
|
+
if (!this.state) return;
|
|
5269
|
+
const specPath = path4__namespace.join(this.openspecPath, "spec", `${this.state.id}.md`);
|
|
5270
|
+
const content = `# Spec: ${this.state.title}
|
|
5271
|
+
|
|
5272
|
+
> \u53D8\u66F4ID: ${this.state.id}
|
|
5273
|
+
> \u5F52\u6863\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
5274
|
+
> \u5DE5\u4F5C\u6D41\u7C7B\u578B: ${this.state.type}
|
|
5275
|
+
|
|
5276
|
+
---
|
|
5277
|
+
|
|
5278
|
+
## \u6982\u8FF0
|
|
5279
|
+
|
|
5280
|
+
${this.state.requirement}
|
|
5281
|
+
|
|
5282
|
+
## \u5B9E\u73B0\u603B\u7ED3
|
|
5283
|
+
|
|
5284
|
+
${summary}
|
|
5285
|
+
|
|
5286
|
+
## \u9A8C\u6536\u7ED3\u679C
|
|
5287
|
+
|
|
5288
|
+
${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.step}`).join("\n")}
|
|
5289
|
+
|
|
5290
|
+
## \u76F8\u5173\u6587\u4EF6
|
|
5291
|
+
|
|
5292
|
+
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
5293
|
+
`;
|
|
5294
|
+
await fs4__namespace.writeFile(specPath, content);
|
|
5295
|
+
}
|
|
5296
|
+
};
|
|
5297
|
+
var ConfirmationRequiredError = class extends Error {
|
|
5298
|
+
constructor(message, point) {
|
|
5299
|
+
super(message);
|
|
5300
|
+
this.point = point;
|
|
5301
|
+
this.name = "ConfirmationRequiredError";
|
|
5302
|
+
}
|
|
5303
|
+
};
|
|
5226
5304
|
|
|
5227
5305
|
// src/norms/index.ts
|
|
5228
5306
|
init_cjs_shims();
|
|
@@ -6584,8 +6662,8 @@ var CommandParser = class {
|
|
|
6584
6662
|
};
|
|
6585
6663
|
}
|
|
6586
6664
|
parseAtPath(input) {
|
|
6587
|
-
const
|
|
6588
|
-
if (!
|
|
6665
|
+
const path14 = input.slice(1).trim();
|
|
6666
|
+
if (!path14) {
|
|
6589
6667
|
return { success: false, error: "\u7F3A\u5C11\u6587\u4EF6\u8DEF\u5F84" };
|
|
6590
6668
|
}
|
|
6591
6669
|
return {
|
|
@@ -6593,7 +6671,7 @@ var CommandParser = class {
|
|
|
6593
6671
|
command: {
|
|
6594
6672
|
type: "at" /* AT */,
|
|
6595
6673
|
raw: input,
|
|
6596
|
-
path:
|
|
6674
|
+
path: path14
|
|
6597
6675
|
}
|
|
6598
6676
|
};
|
|
6599
6677
|
}
|
|
@@ -7703,8 +7781,6 @@ init_new();
|
|
|
7703
7781
|
|
|
7704
7782
|
// src/commands/opsx.ts
|
|
7705
7783
|
init_cjs_shims();
|
|
7706
|
-
init_workflow();
|
|
7707
|
-
init_checkpoint();
|
|
7708
7784
|
var autoScheduleEnabled = true;
|
|
7709
7785
|
var DEFAULT_REGRESSION_CONFIG = {
|
|
7710
7786
|
enabled: true,
|
|
@@ -7780,7 +7856,7 @@ async function runRegressionTest(workingDirectory, config = DEFAULT_REGRESSION_C
|
|
|
7780
7856
|
}
|
|
7781
7857
|
async function handleOpsx(command, args, ctx) {
|
|
7782
7858
|
const step = command.replace("opsx:", "");
|
|
7783
|
-
const workflow = new
|
|
7859
|
+
const workflow = new WorkflowEngine();
|
|
7784
7860
|
await workflow.initialize(ctx.options.workingDirectory);
|
|
7785
7861
|
switch (step) {
|
|
7786
7862
|
case "explore":
|
|
@@ -7935,7 +8011,7 @@ async function handleTransition(workflow, targetStep, ctx) {
|
|
|
7935
8011
|
\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`) + autoScheduleHint + chalk9__default.default.yellow("\n\n\u4F7F\u7528 /opsx:next \u7EE7\u7EED\u4E0B\u4E00\u9636\u6BB5")
|
|
7936
8012
|
};
|
|
7937
8013
|
} catch (e) {
|
|
7938
|
-
if (e instanceof
|
|
8014
|
+
if (e instanceof ConfirmationRequiredError) {
|
|
7939
8015
|
return {
|
|
7940
8016
|
output: chalk9__default.default.yellow("\u26A0 \u9700\u8981\u786E\u8BA4") + chalk9__default.default.white(`
|
|
7941
8017
|
|
|
@@ -8179,10 +8255,10 @@ async function handleRegenerateSpec(workflow, ctx) {
|
|
|
8179
8255
|
const lines = [];
|
|
8180
8256
|
lines.push(chalk9__default.default.cyan("\u{1F504} \u91CD\u65B0\u751F\u6210\u89C4\u683C..."));
|
|
8181
8257
|
lines.push("");
|
|
8182
|
-
const { readProjectContext: readProjectContext2, analyzeComplexity: analyzeComplexity2, generateSpecContent
|
|
8258
|
+
const { readProjectContext: readProjectContext2, analyzeComplexity: analyzeComplexity2, generateSpecContent } = await Promise.resolve().then(() => (init_new(), new_exports));
|
|
8183
8259
|
const context = await readProjectContext2(workingDir);
|
|
8184
8260
|
const analysis = analyzeComplexity2(state.requirement, context);
|
|
8185
|
-
const specContent = await
|
|
8261
|
+
const specContent = await generateSpecContent(state.id, state.requirement, analysis, context);
|
|
8186
8262
|
await fs10__namespace.promises.writeFile(specPath, specContent, "utf-8");
|
|
8187
8263
|
lines.push(chalk9__default.default.green("\u2713 \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210"));
|
|
8188
8264
|
lines.push(chalk9__default.default.gray(`\u8DEF\u5F84: ${specPath}`));
|
|
@@ -8262,7 +8338,7 @@ ${point.name}`));
|
|
|
8262
8338
|
}
|
|
8263
8339
|
return { output: lines.join("\n") };
|
|
8264
8340
|
} catch (e) {
|
|
8265
|
-
if (e instanceof
|
|
8341
|
+
if (e instanceof ConfirmationRequiredError) {
|
|
8266
8342
|
lines.push(chalk9__default.default.yellow("\n\u26A0 \u8FD8\u9700\u8981\u786E\u8BA4:") + chalk9__default.default.white(`
|
|
8267
8343
|
${generateConfirmationPrompt(e.point)}`) + chalk9__default.default.cyan(`
|
|
8268
8344
|
|
|
@@ -8307,7 +8383,7 @@ async function handleNext(workflow, args, ctx) {
|
|
|
8307
8383
|
\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`) + autoScheduleHint
|
|
8308
8384
|
};
|
|
8309
8385
|
} catch (e) {
|
|
8310
|
-
if (e instanceof
|
|
8386
|
+
if (e instanceof ConfirmationRequiredError) {
|
|
8311
8387
|
return {
|
|
8312
8388
|
output: chalk9__default.default.yellow("\u26A0 \u9700\u8981\u786E\u8BA4") + chalk9__default.default.white(`
|
|
8313
8389
|
|
|
@@ -8551,51 +8627,17 @@ async function executeShell(command, ctx) {
|
|
|
8551
8627
|
|
|
8552
8628
|
// src/commands/natural.ts
|
|
8553
8629
|
init_cjs_shims();
|
|
8630
|
+
init_new();
|
|
8554
8631
|
async function handleNaturalLanguage(input, ctx) {
|
|
8555
|
-
|
|
8556
|
-
const
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
const transition = await workflowEngine.transition("apply");
|
|
8565
|
-
return {
|
|
8566
|
-
output: chalk9__default.default.green("\u2713 \u5DF2\u786E\u8BA4\u5E76\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5") + chalk9__default.default.gray(`
|
|
8567
|
-
\u8F6C\u6362: ${transition.from} \u2192 ${transition.to}`) + chalk9__default.default.cyan("\n\n\u4E0B\u4E00\u6B65: \u6267\u884C\u53D8\u66F4") + chalk9__default.default.gray("\n \u53EF\u8C03\u7528 $frontend-dev \u6267\u884C\u4EE3\u7801\u4FEE\u6539"),
|
|
8568
|
-
contextUsed: 0
|
|
8569
|
-
};
|
|
8570
|
-
} catch (e) {
|
|
8571
|
-
return {
|
|
8572
|
-
output: chalk9__default.default.green("\u2713 \u5DF2\u786E\u8BA4") + chalk9__default.default.yellow("\n\n\u4F7F\u7528 /opsx:next \u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"),
|
|
8573
|
-
contextUsed: 0
|
|
8574
|
-
};
|
|
8575
|
-
}
|
|
8576
|
-
}
|
|
8577
|
-
if (trimmedInput === "n" || trimmedInput === "no" || trimmedInput === "\u91CD\u65B0" || trimmedInput === "\u4E0D\u6EE1\u610F") {
|
|
8578
|
-
const state = workflowEngine.getState();
|
|
8579
|
-
if (!state) {
|
|
8580
|
-
return {
|
|
8581
|
-
output: chalk9__default.default.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41"),
|
|
8582
|
-
contextUsed: 0
|
|
8583
|
-
};
|
|
8584
|
-
}
|
|
8585
|
-
const workingDir = ctx.options.workingDirectory;
|
|
8586
|
-
const specPath = path4__namespace.join(workingDir, "openspec", "changes", `${state.id}-spec.md`);
|
|
8587
|
-
workflowEngine.confirm("spec-review", "regenerate");
|
|
8588
|
-
const { readProjectContext: readProjectContext2, analyzeComplexity: analyzeComplexity2, generateSpecContent: generateSpecContent2 } = await Promise.resolve().then(() => (init_new(), new_exports));
|
|
8589
|
-
const context = await readProjectContext2(workingDir);
|
|
8590
|
-
const analysis = analyzeComplexity2(state.requirement, context);
|
|
8591
|
-
const specContent = await generateSpecContent2(state.id, state.requirement, analysis, context);
|
|
8592
|
-
await fs4__namespace.writeFile(specPath, specContent, "utf-8");
|
|
8593
|
-
return {
|
|
8594
|
-
output: chalk9__default.default.cyan("\u{1F504} \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210") + chalk9__default.default.gray(`
|
|
8595
|
-
\u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u68C0\u67E5\u65B0\u7684\u89C4\u683C\u6587\u4EF6:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u518D\u6B21\u91CD\u65B0\u751F\u6210"),
|
|
8596
|
-
contextUsed: 0
|
|
8597
|
-
};
|
|
8598
|
-
}
|
|
8632
|
+
input.trim().toLowerCase();
|
|
8633
|
+
const session = getActiveSession();
|
|
8634
|
+
if (session) {
|
|
8635
|
+
const result = await handleWorkflowInput(input, ctx);
|
|
8636
|
+
if (result) {
|
|
8637
|
+
return {
|
|
8638
|
+
output: result.output,
|
|
8639
|
+
contextUsed: 0
|
|
8640
|
+
};
|
|
8599
8641
|
}
|
|
8600
8642
|
}
|
|
8601
8643
|
ctx.contextManager.addMessage({
|
|
@@ -8610,7 +8652,8 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
8610
8652
|
}
|
|
8611
8653
|
|
|
8612
8654
|
// src/cli/executor.ts
|
|
8613
|
-
|
|
8655
|
+
init_new();
|
|
8656
|
+
var BASIC_COMMANDS = [
|
|
8614
8657
|
"help",
|
|
8615
8658
|
"h",
|
|
8616
8659
|
"?",
|
|
@@ -8629,59 +8672,32 @@ var ALLOWED_COMMANDS_WITHOUT_WORKFLOW = [
|
|
|
8629
8672
|
"update",
|
|
8630
8673
|
"u",
|
|
8631
8674
|
"version",
|
|
8632
|
-
"v"
|
|
8633
|
-
// OpenSpec 工作流管理命令(始终允许)
|
|
8634
|
-
"opsx:status",
|
|
8635
|
-
"opsx:cancel",
|
|
8636
|
-
"opsx:rollback"
|
|
8675
|
+
"v"
|
|
8637
8676
|
];
|
|
8638
|
-
var STAGE_PERMISSIONS = {
|
|
8639
|
-
"explore": {
|
|
8640
|
-
canRead: true,
|
|
8641
|
-
canWrite: false,
|
|
8642
|
-
canRunShell: false,
|
|
8643
|
-
allowedAgents: ["architect"]
|
|
8644
|
-
},
|
|
8645
|
-
"new": {
|
|
8646
|
-
canRead: true,
|
|
8647
|
-
canWrite: true,
|
|
8648
|
-
canRunShell: false,
|
|
8649
|
-
allowedAgents: ["frontend-dev", "architect"]
|
|
8650
|
-
},
|
|
8651
|
-
"continue": {
|
|
8652
|
-
canRead: true,
|
|
8653
|
-
canWrite: true,
|
|
8654
|
-
canRunShell: true,
|
|
8655
|
-
allowedAgents: ["frontend-dev", "tester"]
|
|
8656
|
-
},
|
|
8657
|
-
"propose": {
|
|
8658
|
-
canRead: true,
|
|
8659
|
-
canWrite: true,
|
|
8660
|
-
canRunShell: true,
|
|
8661
|
-
allowedAgents: ["frontend-dev"]
|
|
8662
|
-
},
|
|
8663
|
-
"apply": {
|
|
8664
|
-
canRead: true,
|
|
8665
|
-
canWrite: true,
|
|
8666
|
-
canRunShell: true,
|
|
8667
|
-
allowedAgents: ["code-reviewer"]
|
|
8668
|
-
},
|
|
8669
|
-
"archive": {
|
|
8670
|
-
canRead: true,
|
|
8671
|
-
canWrite: false,
|
|
8672
|
-
canRunShell: false,
|
|
8673
|
-
allowedAgents: []
|
|
8674
|
-
}
|
|
8675
|
-
};
|
|
8676
8677
|
var CommandExecutor = class {
|
|
8677
8678
|
async execute(parseResult, ctx) {
|
|
8678
8679
|
if (!parseResult.success || !parseResult.command) {
|
|
8679
8680
|
return { output: chalk9__default.default.red(`\u9519\u8BEF: ${parseResult.error}`) };
|
|
8680
8681
|
}
|
|
8681
8682
|
const { command } = parseResult;
|
|
8682
|
-
const
|
|
8683
|
-
if (!
|
|
8684
|
-
|
|
8683
|
+
const hasActiveWorkflow = getActiveSession() !== null;
|
|
8684
|
+
if (!hasActiveWorkflow) {
|
|
8685
|
+
if (command.type === "slash" /* SLASH */) {
|
|
8686
|
+
const cmd = command.command?.toLowerCase() || "";
|
|
8687
|
+
if (!BASIC_COMMANDS.includes(cmd)) {
|
|
8688
|
+
return {
|
|
8689
|
+
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8690
|
+
};
|
|
8691
|
+
}
|
|
8692
|
+
} else if (command.type === "dollar" /* DOLLAR */) {
|
|
8693
|
+
return {
|
|
8694
|
+
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8695
|
+
};
|
|
8696
|
+
} else if (command.type === "shell" /* SHELL */) {
|
|
8697
|
+
return {
|
|
8698
|
+
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8699
|
+
};
|
|
8700
|
+
}
|
|
8685
8701
|
}
|
|
8686
8702
|
switch (command.type) {
|
|
8687
8703
|
case "slash" /* SLASH */:
|
|
@@ -8700,98 +8716,30 @@ var CommandExecutor = class {
|
|
|
8700
8716
|
return { output: chalk9__default.default.red("\u672A\u77E5\u7684\u547D\u4EE4\u7C7B\u578B") };
|
|
8701
8717
|
}
|
|
8702
8718
|
}
|
|
8703
|
-
/**
|
|
8704
|
-
* 检查工作流权限
|
|
8705
|
-
*/
|
|
8706
|
-
checkWorkflowPermission(command, ctx) {
|
|
8707
|
-
const workflowEngine = ctx.workflowEngine;
|
|
8708
|
-
const workflowState = workflowEngine?.getState();
|
|
8709
|
-
if (command.type === "slash" /* SLASH */) {
|
|
8710
|
-
const cmd = command.command?.toLowerCase();
|
|
8711
|
-
if (cmd?.startsWith("opsx:")) {
|
|
8712
|
-
return { allowed: true };
|
|
8713
|
-
}
|
|
8714
|
-
}
|
|
8715
|
-
if (!workflowState) {
|
|
8716
|
-
if (command.type === "slash" /* SLASH */) {
|
|
8717
|
-
const cmd = command.command?.toLowerCase();
|
|
8718
|
-
if (!ALLOWED_COMMANDS_WITHOUT_WORKFLOW.includes(cmd)) {
|
|
8719
|
-
return {
|
|
8720
|
-
allowed: false,
|
|
8721
|
-
message: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8722
|
-
};
|
|
8723
|
-
}
|
|
8724
|
-
}
|
|
8725
|
-
if (command.type === "natural" /* NATURAL */) {
|
|
8726
|
-
return {
|
|
8727
|
-
allowed: false,
|
|
8728
|
-
message: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8729
|
-
};
|
|
8730
|
-
}
|
|
8731
|
-
if (command.type === "dollar" /* DOLLAR */) {
|
|
8732
|
-
return {
|
|
8733
|
-
allowed: false,
|
|
8734
|
-
message: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8735
|
-
};
|
|
8736
|
-
}
|
|
8737
|
-
if (command.type === "shell" /* SHELL */) {
|
|
8738
|
-
return {
|
|
8739
|
-
allowed: false,
|
|
8740
|
-
message: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
8741
|
-
};
|
|
8742
|
-
}
|
|
8743
|
-
return { allowed: true };
|
|
8744
|
-
}
|
|
8745
|
-
const currentStep = workflowState.currentStep;
|
|
8746
|
-
const permissions = STAGE_PERMISSIONS[currentStep];
|
|
8747
|
-
if (command.type === "at" /* AT */ && !permissions.canRead) {
|
|
8748
|
-
return {
|
|
8749
|
-
allowed: false,
|
|
8750
|
-
message: chalk9__default.default.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8BFB\u53D6\u6587\u4EF6`)
|
|
8751
|
-
};
|
|
8752
|
-
}
|
|
8753
|
-
if (command.type === "dollar" /* DOLLAR */) {
|
|
8754
|
-
const agentId = command.agent?.toLowerCase().replace("$", "");
|
|
8755
|
-
if (!permissions.allowedAgents.includes(agentId)) {
|
|
8756
|
-
return {
|
|
8757
|
-
allowed: false,
|
|
8758
|
-
message: chalk9__default.default.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8C03\u7528 $${agentId} Agent`) + chalk9__default.default.gray(`
|
|
8759
|
-
\u5F53\u524D\u9636\u6BB5\u5141\u8BB8\u7684 Agent: ${permissions.allowedAgents.map((a) => `$${a}`).join(", ") || "\u65E0"}`)
|
|
8760
|
-
};
|
|
8761
|
-
}
|
|
8762
|
-
}
|
|
8763
|
-
if (command.type === "shell" /* SHELL */ && !permissions.canRunShell) {
|
|
8764
|
-
return {
|
|
8765
|
-
allowed: false,
|
|
8766
|
-
message: chalk9__default.default.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u6267\u884C Shell \u547D\u4EE4`)
|
|
8767
|
-
};
|
|
8768
|
-
}
|
|
8769
|
-
return { allowed: true };
|
|
8770
|
-
}
|
|
8771
8719
|
async executeSlashCommand(command, ctx) {
|
|
8772
8720
|
const result = await runSlashCommand(
|
|
8773
|
-
command.command,
|
|
8721
|
+
command.command || "",
|
|
8774
8722
|
command.args || [],
|
|
8775
8723
|
ctx
|
|
8776
8724
|
);
|
|
8777
8725
|
return { output: result.output, exit: result.exit };
|
|
8778
8726
|
}
|
|
8779
8727
|
async executeFileReference(command, ctx) {
|
|
8780
|
-
const result = await handleFileReference(command.path, ctx);
|
|
8728
|
+
const result = await handleFileReference(command.path || "", ctx);
|
|
8781
8729
|
return {
|
|
8782
8730
|
output: result.output,
|
|
8783
8731
|
contextUsed: result.contextUsed
|
|
8784
8732
|
};
|
|
8785
8733
|
}
|
|
8786
8734
|
async executeAgent(command, ctx) {
|
|
8787
|
-
const result = await runAgent(command.agent, command.args || [], ctx);
|
|
8735
|
+
const result = await runAgent(command.agent || "", command.args || [], ctx);
|
|
8788
8736
|
return {
|
|
8789
8737
|
output: result.output,
|
|
8790
8738
|
contextUsed: result.contextUsed
|
|
8791
8739
|
};
|
|
8792
8740
|
}
|
|
8793
8741
|
async executeShell(command, ctx) {
|
|
8794
|
-
const result = await executeShell(command.shellCommand, ctx);
|
|
8742
|
+
const result = await executeShell(command.shellCommand || "", ctx);
|
|
8795
8743
|
return { output: result.output };
|
|
8796
8744
|
}
|
|
8797
8745
|
async executeNaturalLanguage(command, ctx) {
|
|
@@ -9001,6 +8949,8 @@ exports.CommandParser = CommandParser;
|
|
|
9001
8949
|
exports.CommandType = CommandType;
|
|
9002
8950
|
exports.Completer = Completer;
|
|
9003
8951
|
exports.ConfigManager = ConfigManager;
|
|
8952
|
+
exports.ConfirmationManager = ConfirmationManager;
|
|
8953
|
+
exports.ConfirmationRequiredError = ConfirmationRequiredError;
|
|
9004
8954
|
exports.ContextManager = ContextManager;
|
|
9005
8955
|
exports.FRONTEND_DEV_AGENT = FRONTEND_DEV_AGENT;
|
|
9006
8956
|
exports.FigmaMCPAdapter = FigmaMCPAdapter;
|
|
@@ -9009,8 +8959,10 @@ exports.MCPAdapterBase = MCPAdapterBase;
|
|
|
9009
8959
|
exports.MCPManager = MCPManager;
|
|
9010
8960
|
exports.ModelService = ModelService;
|
|
9011
8961
|
exports.NormsManager = NormsManager;
|
|
8962
|
+
exports.ROLLBACK_RULES = ROLLBACK_RULES;
|
|
9012
8963
|
exports.SCHEDULE_RULES = SCHEDULE_RULES;
|
|
9013
8964
|
exports.TESTER_AGENT = TESTER_AGENT;
|
|
8965
|
+
exports.WorkflowEngine = WorkflowEngine;
|
|
9014
8966
|
exports.createAgentExecutor = createAgentExecutor;
|
|
9015
8967
|
exports.createAgentScheduler = createAgentScheduler;
|
|
9016
8968
|
exports.createMCPManager = createMCPManager;
|