@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/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/workflow/checkpoint.ts
60
- function generateConfirmationPrompt(point) {
61
- let prompt2 = `\u{1F4CB} ${point.name}
62
-
63
- `;
64
- prompt2 += `${point.description}
65
-
66
- `;
67
- prompt2 += `\u786E\u8BA4\u9009\u9879:
68
- `;
69
- prompt2 += ` y - \u786E\u8BA4\u7EE7\u7EED
70
- `;
71
- prompt2 += ` n - \u62D2\u7EDD\uFF0C\u6682\u505C\u6D41\u7A0B
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
- return prompt2;
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 generateRollbackPrompt(fromStep) {
82
- const allowedTargets = exports.ROLLBACK_RULES[fromStep] || [];
83
- if (allowedTargets.length === 0) {
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
- let prompt2 = `\u{1F504} \u56DE\u6EDA\u9009\u9879
87
-
88
- `;
89
- prompt2 += `\u5F53\u524D\u9636\u6BB5: ${fromStep}
90
- `;
91
- prompt2 += `\u53EF\u56DE\u6EDA\u5230:
92
- `;
93
- for (const target of allowedTargets) {
94
- prompt2 += ` ${target}
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
- prompt2 += `
98
- \u8BF7\u9009\u62E9\u56DE\u6EDA\u76EE\u6807\u9636\u6BB5`;
99
- return prompt2;
120
+ return { output: lines.join("\n") };
100
121
  }
101
- var DEFAULT_CONFIRMATION_POINTS; exports.ROLLBACK_RULES = void 0; exports.ConfirmationManager = void 0;
102
- var init_checkpoint = __esm({
103
- "src/workflow/checkpoint.ts"() {
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
- var TRANSITIONS, COMPLEX_STEPS, SIMPLE_STEPS; exports.WorkflowEngine = void 0; exports.ConfirmationRequiredError = void 0;
243
- var init_workflow = __esm({
244
- "src/workflow/index.ts"() {
245
- init_cjs_shims();
246
- init_checkpoint();
247
- TRANSITIONS = {
248
- "explore": ["new"],
249
- "new": ["continue", "apply"],
250
- "continue": ["apply", "continue"],
251
- "propose": ["apply"],
252
- "apply": ["archive"],
253
- "archive": []
254
- };
255
- COMPLEX_STEPS = ["explore", "new", "continue", "apply", "archive"];
256
- SIMPLE_STEPS = ["propose", "apply", "archive"];
257
- exports.WorkflowEngine = class {
258
- state = null;
259
- projectPath = "";
260
- openspecPath = "";
261
- confirmationManager;
262
- snapshots = /* @__PURE__ */ new Map();
263
- projectContext = "";
264
- // AGENTS.md 内容
265
- projectConfig = "";
266
- // config.yaml 内容
267
- devStandards = "";
268
- // devstanded.md 内容
269
- constructor() {
270
- this.confirmationManager = new exports.ConfirmationManager();
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
- const targetStepRecord = this.state.steps.find((s) => s.step === targetStep);
438
- if (targetStepRecord) {
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
- this.state.currentStep = targetStep;
443
- this.state.updatedAt = /* @__PURE__ */ new Date();
444
- await this.createSnapshot();
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
- async rollback(targetStep, reason, description) {
474
- if (!this.state) {
475
- return {
476
- success: false,
477
- from: "explore",
478
- to: null,
479
- message: "\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41"
480
- };
481
- }
482
- const currentStep = this.state.currentStep;
483
- const allowedTargets = exports.ROLLBACK_RULES[currentStep];
484
- if (!allowedTargets || !allowedTargets.includes(targetStep)) {
485
- return {
486
- success: false,
487
- from: currentStep,
488
- to: targetStep,
489
- message: `\u65E0\u6CD5\u4ECE ${currentStep} \u56DE\u6EDA\u5230 ${targetStep}`
490
- };
491
- }
492
- const snapshot = this.snapshots.get(targetStep);
493
- if (!snapshot) {
494
- return {
495
- success: false,
496
- from: currentStep,
497
- to: targetStep,
498
- message: `\u627E\u4E0D\u5230\u9636\u6BB5 ${targetStep} \u7684\u5FEB\u7167`
499
- };
500
- }
501
- const previousState = this.state;
502
- this.state = {
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
- this.state.artifacts = snapshot.artifacts;
525
- const confirmationsToClear = this.confirmationManager.getConfirmationsToClear(targetStep);
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
- getRollbackReasonDescription(reason) {
543
- const descriptions = {
544
- "code-review-failed": "\u4EE3\u7801\u5BA1\u67E5\u672A\u901A\u8FC7",
545
- "requirement-changed": "\u9700\u6C42\u53D8\u66F4",
546
- "design-issue": "\u8BBE\u8BA1\u95EE\u9898",
547
- "user-request": "\u7528\u6237\u8BF7\u6C42"
548
- };
549
- return descriptions[reason];
550
- }
551
- /**
552
- * 获取当前状态
553
- */
554
- getState() {
555
- return this.state;
556
- }
557
- /**
558
- * 获取所有活跃工作流
559
- */
560
- async getAllActiveWorkflows() {
561
- const workflows = [];
562
- const changesDir = path4__namespace.join(this.openspecPath, "changes");
563
- try {
564
- const files = await fs4__namespace.readdir(changesDir);
565
- for (const file of files) {
566
- if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
567
- const filePath = path4__namespace.join(changesDir, file);
568
- const content = await fs4__namespace.readFile(filePath, "utf-8");
569
- const state = this.parseChangeRecord(content);
570
- if (state && state.status === "running") {
571
- workflows.push(state);
572
- }
573
- }
574
- } catch {
575
- }
576
- if (this.state && !workflows.find((w) => w.id === this.state?.id)) {
577
- workflows.unshift(this.state);
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
- return workflows;
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
- parseChangeRecord(content) {
585
- try {
586
- const idMatch = content.match(/^id:\s*(.+)$/m);
587
- const titleMatch = content.match(/^title:\s*(.+)$/m);
588
- const statusMatch = content.match(/^status:\s*(.+)$/m);
589
- const complexityMatch = content.match(/^complexity:\s*(\d+)/m);
590
- const workflowMatch = content.match(/^workflow:\s*(.+)$/m);
591
- const requirementMatch = content.match(/## 变更概述\s*\n+([\s\S]+?)(?=\n##|$)/);
592
- if (!idMatch || !titleMatch) return null;
593
- return {
594
- id: idMatch[1].trim(),
595
- title: titleMatch[1].trim(),
596
- status: statusMatch?.[1].trim() || "running",
597
- requirement: requirementMatch?.[1].trim() || "",
598
- complexity: parseInt(complexityMatch?.[1] || "5", 10),
599
- type: workflowMatch?.[1].trim() || "simple",
600
- currentStep: "propose",
601
- // 默认值,实际值需要从状态文件读取
602
- steps: [],
603
- artifacts: [],
604
- createdAt: /* @__PURE__ */ new Date()
605
- };
606
- } catch {
607
- return null;
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
- async switchTo(changeId) {
614
- if (this.state) {
615
- await this.saveState();
616
- }
617
- const statePath = path4__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
618
- try {
619
- const content = await fs4__namespace.readFile(statePath, "utf-8");
620
- this.state = JSON.parse(content, (key, value) => {
621
- if (key.endsWith("At") && typeof value === "string") {
622
- return new Date(value);
623
- }
624
- return value;
625
- });
626
- await this.restoreSnapshots();
627
- return true;
628
- } catch {
629
- const changesDir = path4__namespace.join(this.openspecPath, "changes");
630
- const changeFile = path4__namespace.join(changesDir, `${changeId}.md`);
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 { requirement, forceComplexity } = parseArgs(args);
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(cwd);
965
- if (!stats.isDirectory()) {
966
- return {
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
- lines.push(chalk9__default.default.cyan("\u{1F50D} \u5206\u6790\u9879\u76EE..."));
976
- const context = await readProjectContext(cwd);
977
- lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${context.name}`));
978
- lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${context.type}`));
979
- lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`));
980
- lines.push("");
981
- lines.push(chalk9__default.default.cyan("\u{1F4CA} \u5206\u6790\u9700\u6C42\u590D\u6742\u5EA6..."));
982
- const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
983
- lines.push(chalk9__default.default.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`));
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
- lines.push("");
989
- lines.push(chalk9__default.default.cyan("\u{1F4CB} \u521D\u59CB\u5316\u5DE5\u4F5C\u6D41..."));
990
- const workflow = workflowEngine || new exports.WorkflowEngine();
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 state = await workflow.start(requirement, analysis.score, {
995
- title: extractTitle(requirement)
996
- });
997
- lines.push(chalk9__default.default.gray(` \u53D8\u66F4ID: ${state.id}`));
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 (spec.risks.length > 0) {
1018
- lines.push("");
1019
- lines.push(chalk9__default.default.yellow(" \u26A0\uFE0F \u98CE\u9669\u63D0\u793A:"));
1020
- for (const risk of spec.risks) {
1021
- lines.push(chalk9__default.default.gray(` - ${risk}`));
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
- lines.push("");
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
- async function generateSpec(requirement, context, analysis, changeId) {
1033
- const spec = {
1034
- changeId,
1035
- requirement,
1036
- summary: "",
1037
- items: [],
1038
- architectureNotes: [],
1039
- risks: [],
1040
- suggestions: []
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: /用户|登录|注册|认证|权限/, title: "\u7528\u6237\u8BA4\u8BC1\u6A21\u5757", priority: "high" },
1062
- { pattern: /数据|存储|缓存|数据库/, title: "\u6570\u636E\u5C42\u5B9E\u73B0", priority: "high" },
1063
- { pattern: /接口|API|请求|响应/, title: "API \u63A5\u53E3\u5F00\u53D1", priority: "high" },
1064
- { pattern: /界面|页面|组件|UI/, title: "\u754C\u9762\u5F00\u53D1", priority: "medium" },
1065
- { pattern: /测试|单测|覆盖/, title: "\u6D4B\u8BD5\u7528\u4F8B\u7F16\u5199", priority: "medium" },
1066
- { pattern: /文档|说明/, title: "\u6587\u6863\u7F16\u5199", priority: "low" },
1067
- { pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", priority: "low" },
1068
- { pattern: /优化|性能/, title: "\u6027\u80FD\u4F18\u5316", priority: "medium" },
1069
- { pattern: /安全|加密/, title: "\u5B89\u5168\u5B9E\u73B0", priority: "high" },
1070
- { pattern: /日志|监控/, title: "\u65E5\u5FD7\u76D1\u63A7", priority: "low" }
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, priority } of featurePatterns) {
622
+ for (const { pattern, title, hasInput } of featurePatterns) {
1073
623
  if (pattern.test(requirement)) {
1074
- items.push({
1075
- id: `T${itemId.toString().padStart(3, "0")}`,
624
+ features.push({
1076
625
  title,
1077
- description: `${title}\u76F8\u5173\u7684\u529F\u80FD\u5B9E\u73B0`,
1078
- priority,
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 (items.length === 0) {
1086
- items.push({
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
- priority: "high",
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 items;
638
+ return features;
1112
639
  }
1113
- function generateSimpleTasks(requirement, context) {
640
+ function generateSpecItems(requirement, context, bddScenarios, questions) {
1114
641
  const items = [];
1115
- let itemId = 1;
1116
- const urlMatch = requirement.match(/https?:\/\/[^\s]+/g);
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: "T002",
1185
- title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
1186
- description: requirement,
1187
- priority: "high",
1188
- dependencies: ["T001"],
1189
- estimatedComplexity: 3
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${itemId.toString().padStart(3, "0")}`,
1194
- title: "\u6D4B\u8BD5\u9A8C\u8BC1",
1195
- description: "\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027\uFF0C\u786E\u4FDD\u7B26\u5408\u9884\u671F",
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
- dependencies: items.length > 0 ? [items[items.length - 1].id] : [],
1198
- estimatedComplexity: 1
659
+ files: [],
660
+ tests: []
1199
661
  });
1200
662
  return items;
1201
663
  }
1202
- function generateArchitectureNotes(requirement, context) {
1203
- const notes = [];
1204
- if (context.framework) {
1205
- notes.push(`\u9879\u76EE\u4F7F\u7528 ${context.framework} \u6846\u67B6\uFF0C\u9700\u9075\u5FAA\u5176\u6700\u4F73\u5B9E\u8DF5`);
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(spec) {
672
+ function formatSpecFile(session) {
1257
673
  const lines = [];
1258
- lines.push(`# Spec: ${spec.summary}`);
674
+ lines.push(`# \u9700\u6C42\u89C4\u683C: ${session.requirement.slice(0, 50)}`);
1259
675
  lines.push("");
1260
- lines.push(`> \u53D8\u66F4ID: ${spec.changeId}`);
1261
- lines.push(`> \u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}`);
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
- lines.push("## \u9700\u6C42\u6982\u8FF0");
1266
- lines.push("");
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
- for (const note of spec.architectureNotes) {
1287
- lines.push(`- ${note}`);
1288
- }
688
+ lines.push("---");
1289
689
  lines.push("");
1290
690
  }
1291
- if (spec.risks.length > 0) {
1292
- lines.push("## \u26A0\uFE0F \u98CE\u9669\u8BC4\u4F30");
691
+ if (session.clarificationQuestions.some((q) => q.answered)) {
692
+ lines.push("## \u9700\u6C42\u6F84\u6E05");
1293
693
  lines.push("");
1294
- for (const risk of spec.risks) {
1295
- lines.push(`- ${risk}`);
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
- if (spec.suggestions.length > 0) {
1300
- lines.push("## \u{1F4A1} \u5EFA\u8BAE");
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 suggestion of spec.suggestions) {
1303
- lines.push(`- ${suggestion}`);
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
- lines.push("## \u786E\u8BA4\u72B6\u6001");
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("- [ ] \u89C4\u683C\u5DF2\u5BA1\u9605");
1312
- lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
724
+ lines.push("---");
1313
725
  lines.push("");
1314
- lines.push("**\u786E\u8BA4\u540E\u6267\u884C**: `/opsx:confirm spec-review`");
726
+ lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
1315
727
  return lines.join("\n");
1316
728
  }
1317
- function parseArgs(args) {
1318
- let forceComplexity;
1319
- const filteredArgs = [];
1320
- for (const arg of args) {
1321
- if (arg === "--simple") {
1322
- forceComplexity = "simple";
1323
- } else if (arg === "--complex") {
1324
- forceComplexity = "complex";
1325
- } else {
1326
- filteredArgs.push(arg);
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
- async function readAgentsMd(cwd) {
1367
- const agentsPath = path4__namespace.join(cwd, "AGENTS.md");
1368
- try {
1369
- const stats = await fs4__namespace.stat(agentsPath);
1370
- if (stats.size > MAX_FILE_SIZE2) {
1371
- console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
1372
- return {};
1373
- }
1374
- const content = await fs4__namespace.readFile(agentsPath, "utf-8");
1375
- return parseAgentsMd(content);
1376
- } catch (e) {
1377
- const err = e;
1378
- if (err.code !== "ENOENT") {
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 parseAgentsMd(content) {
1385
- const context = {};
1386
- const nameMatch = content.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
1387
- if (nameMatch) {
1388
- context.name = nameMatch[1];
1389
- }
1390
- const typeMatch = content.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
1391
- if (typeMatch) {
1392
- context.type = typeMatch[1];
1393
- }
1394
- const frameworkMatch = content.match(/\|\s*技术框架\s*\|\s*([^\s|]+)/);
1395
- if (frameworkMatch && frameworkMatch[1] !== "\u5F85\u8BC6\u522B") {
1396
- context.framework = frameworkMatch[1];
1397
- }
1398
- const descMatch = content.match(/###\s*1\.2\s*项目描述\s*\n+([^\n#]+)/);
1399
- if (descMatch) {
1400
- context.description = descMatch[1].trim();
1401
- }
1402
- const techStackMatch = content.match(/技术栈[::]\s*([^\n]+)/);
1403
- if (techStackMatch) {
1404
- context.techStack = techStackMatch[1].split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
1405
- }
1406
- return context;
1407
- }
1408
- async function readConfigYaml(cwd) {
1409
- const configPath = path4__namespace.join(cwd, "openspec", "config.yaml");
1410
- try {
1411
- const stats = await fs4__namespace.stat(configPath);
1412
- if (stats.size > MAX_FILE_SIZE2) {
1413
- console.warn("\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6");
1414
- return {};
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
- async function analyzeStructure(cwd) {
1467
- const structure = {
1468
- directories: [],
1469
- keyFiles: [],
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 analyzeComplexity(requirement, context) {
1504
- let score = 3;
1505
- const factors = [];
1506
- if (requirement.length > 200) {
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 createForcedAnalysis(type) {
1556
- return {
1557
- score: type === "complex" ? 8 : 3,
1558
- factors: [`\u7528\u6237\u6307\u5B9A${type === "complex" ? "\u590D\u6742" : "\u7B80\u5355"}\u6D41\u7A0B`],
1559
- recommendation: type
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 extractTitle(requirement) {
1563
- const firstSentence = requirement.split(/[。!?\n]/)[0];
1564
- if (firstSentence.length <= 50) {
1565
- return firstSentence;
1566
- }
1567
- return requirement.slice(0, 47) + "...";
814
+ function getActiveSession() {
815
+ return activeSession;
1568
816
  }
1569
- async function generateSpecContent(changeId, requirement, analysis, context) {
1570
- const spec = {
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
- new_default = newFeature;
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
- * 执行 Agent
4981
+ * 解析变更记录
5118
4982
  */
5119
- async executeAgent(config, state, additionalContext) {
5120
- const agent = getAgentDefinition(config.agent);
5121
- if (!agent) {
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
- scheduled: false,
5124
- agent: config.agent,
5125
- strategy: "auto",
5126
- message: `Agent ${config.agent} \u4E0D\u5B58\u5728`
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
- const context = {
5130
- taskId: state.id,
5131
- workflowId: state.id,
5132
- workflowStep: state.currentStep,
5133
- requirement: state.requirement,
5134
- files: state.artifacts,
5135
- context: additionalContext?.context,
5136
- norms: additionalContext?.norms,
5137
- previousOutput: additionalContext?.previousOutput
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 result = await this.executor.execute({
5141
- agentId: config.agent,
5142
- context
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
- const scheduleResult = {
5145
- scheduled: true,
5146
- agent: config.agent,
5147
- strategy: "auto",
5148
- result,
5149
- message: result.success ? `$${config.agent} \u6267\u884C\u5B8C\u6210` : `$${config.agent} \u6267\u884C\u5931\u8D25: ${result.error}`
5150
- };
5151
- this.lastScheduleResult = scheduleResult;
5152
- if (this.onSchedule) {
5153
- this.onSchedule(scheduleResult);
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 scheduleResult;
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
- getScheduleConfig(step) {
5170
- return SCHEDULE_RULES[step];
5045
+ getAllowedTransitions() {
5046
+ if (!this.state) return [];
5047
+ return TRANSITIONS[this.state.currentStep] || [];
5171
5048
  }
5172
5049
  /**
5173
- * 获取阶段的推荐 Agent
5050
+ * 获取快照历史
5174
5051
  */
5175
- getRecommendedAgent(step) {
5176
- const rule = SCHEDULE_RULES[step];
5177
- return rule?.agent;
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
- getAllScheduleRules() {
5183
- return { ...SCHEDULE_RULES };
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
- getLastScheduleResult() {
5189
- return this.lastScheduleResult;
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
- shouldAutoSchedule(step) {
5195
- const rule = SCHEDULE_RULES[step];
5196
- return rule?.strategy === "auto";
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
- * 获取阶段可用的 Agent 列表
5200
- */
5201
- getAvailableAgents(step) {
5202
- const agents = getAgentsForWorkflowStep(step);
5203
- return agents.map((a) => a.id);
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
- function createAgentScheduler(executor) {
5207
- return new AgentScheduler(executor);
5208
- }
5209
- function getScheduleRuleDescription(step) {
5210
- const rule = SCHEDULE_RULES[step];
5211
- if (!rule) {
5212
- return `\u9636\u6BB5 ${step} \u65E0\u81EA\u52A8\u8C03\u5EA6\u89C4\u5219`;
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
- const agent = getAgentDefinition(rule.agent);
5215
- const strategyText = {
5216
- "auto": "\u81EA\u52A8\u6267\u884C",
5217
- "recommend": "\u63A8\u8350\u6267\u884C",
5218
- "manual": "\u624B\u52A8\u6267\u884C"
5219
- };
5220
- return `${strategyText[rule.strategy]} $${rule.agent} (${agent?.name})`;
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
- // src/index.ts
5224
- init_workflow();
5225
- init_checkpoint();
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 path15 = input.slice(1).trim();
6588
- if (!path15) {
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: path15
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 exports.WorkflowEngine();
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 exports.ConfirmationRequiredError) {
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: generateSpecContent2 } = await Promise.resolve().then(() => (init_new(), new_exports));
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 generateSpecContent2(state.id, state.requirement, analysis, context);
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 exports.ConfirmationRequiredError) {
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 exports.ConfirmationRequiredError) {
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
- const trimmedInput = input.trim().toLowerCase();
8556
- const workflowEngine = ctx.workflowEngine;
8557
- const workflowState = workflowEngine?.getState();
8558
- if (workflowState && workflowState.status === "running") {
8559
- const currentStep = workflowState.currentStep;
8560
- if (currentStep === "propose" || currentStep === "explore") {
8561
- if (trimmedInput === "y" || trimmedInput === "yes" || trimmedInput === "\u786E\u8BA4") {
8562
- workflowEngine.confirm("spec-review");
8563
- try {
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
- var ALLOWED_COMMANDS_WITHOUT_WORKFLOW = [
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 workflowCheck = this.checkWorkflowPermission(command, ctx);
8683
- if (!workflowCheck.allowed) {
8684
- return { output: workflowCheck.message };
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;