@nick848/sf-cli 1.0.7 → 1.0.10

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.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as path5 from 'path';
2
2
  import path5__default from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import chalk9 from 'chalk';
4
5
  import * as fs4 from 'fs/promises';
5
6
  import * as fs10 from 'fs';
6
7
  import * as crypto from 'crypto';
@@ -8,15 +9,566 @@ import * as os from 'os';
8
9
  import { encoding_for_model } from 'tiktoken';
9
10
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
10
11
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
11
- import chalk9 from 'chalk';
12
12
  import { v4 } from 'uuid';
13
13
  import { prompt } from 'enquirer';
14
14
  import { spawn } from 'child_process';
15
15
 
16
- // node_modules/tsup/assets/esm_shims.js
17
- var getFilename = () => fileURLToPath(import.meta.url);
18
- var getDirname = () => path5__default.dirname(getFilename());
19
- var __dirname$1 = /* @__PURE__ */ getDirname();
16
+ var __defProp = Object.defineProperty;
17
+ var __getOwnPropNames = Object.getOwnPropertyNames;
18
+ var __esm = (fn, res) => function __init() {
19
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var getFilename, getDirname, __dirname$1;
26
+ var init_esm_shims = __esm({
27
+ "node_modules/tsup/assets/esm_shims.js"() {
28
+ getFilename = () => fileURLToPath(import.meta.url);
29
+ getDirname = () => path5__default.dirname(getFilename());
30
+ __dirname$1 = /* @__PURE__ */ getDirname();
31
+ }
32
+ });
33
+
34
+ // src/commands/new.ts
35
+ var new_exports = {};
36
+ __export(new_exports, {
37
+ clearActiveSession: () => clearActiveSession,
38
+ default: () => new_default,
39
+ getActiveSession: () => getActiveSession,
40
+ handleNew: () => handleNew,
41
+ handleWorkflowInput: () => handleWorkflowInput
42
+ });
43
+ async function handleNew(args, ctx) {
44
+ ctx.options.workingDirectory;
45
+ if (activeSession) {
46
+ return handleActiveSession();
47
+ }
48
+ const requirement = args.join(" ").trim();
49
+ if (!requirement) {
50
+ return {
51
+ output: chalk9.red("\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0") + chalk9.gray("\n\u7528\u6CD5: /new <\u9700\u6C42\u63CF\u8FF0>")
52
+ };
53
+ }
54
+ activeSession = {
55
+ id: generateSessionId(),
56
+ requirement,
57
+ phase: "context",
58
+ context: null,
59
+ complexity: 0,
60
+ bddScenarios: [],
61
+ specItems: [],
62
+ testFiles: [],
63
+ implFiles: [],
64
+ reviewPassed: false,
65
+ createdAt: /* @__PURE__ */ new Date()
66
+ };
67
+ return executeWorkflow(ctx);
68
+ }
69
+ function handleActiveSession(ctx) {
70
+ if (!activeSession) {
71
+ return { output: chalk9.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
72
+ }
73
+ const lines = [];
74
+ lines.push(chalk9.cyan("\u{1F4CB} \u5F53\u524D\u5DE5\u4F5C\u6D41\u72B6\u6001"));
75
+ lines.push("");
76
+ lines.push(chalk9.white(`\u9700\u6C42: ${activeSession.requirement}`));
77
+ lines.push(chalk9.gray(`\u9636\u6BB5: ${getPhaseLabel(activeSession.phase)}`));
78
+ lines.push("");
79
+ if (activeSession.phase === "spec") {
80
+ lines.push(chalk9.yellow("\u23F8\uFE0F \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
81
+ lines.push("");
82
+ lines.push(chalk9.green(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u7EE7\u7EED\u5DE5\u4F5C\u6D41"));
83
+ lines.push(chalk9.red(" n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210\u89C4\u683C"));
84
+ lines.push(chalk9.gray(" c - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41"));
85
+ } else {
86
+ lines.push(chalk9.yellow("\u5DE5\u4F5C\u6D41\u8FDB\u884C\u4E2D..."));
87
+ lines.push(chalk9.gray("\u8F93\u5165\u4EFB\u610F\u5185\u5BB9\u7EE7\u7EED"));
88
+ }
89
+ return { output: lines.join("\n") };
90
+ }
91
+ async function executeWorkflow(ctx) {
92
+ if (!activeSession) {
93
+ return { output: chalk9.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
94
+ }
95
+ const lines = [];
96
+ try {
97
+ if (activeSession.phase === "context") {
98
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/7: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
99
+ lines.push("");
100
+ activeSession.context = await readProjectContext(ctx.options.workingDirectory);
101
+ lines.push(chalk9.gray(` \u9879\u76EE: ${activeSession.context.name}`));
102
+ lines.push(chalk9.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
103
+ lines.push(chalk9.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
104
+ lines.push(chalk9.gray(` \u6280\u672F\u6808: ${activeSession.context.techStack.join(", ") || "\u672A\u8BC6\u522B"}`));
105
+ if (activeSession.context.devStandards) {
106
+ lines.push(chalk9.green(" \u2713 \u5DF2\u8BFB\u53D6\u5F00\u53D1\u89C4\u8303"));
107
+ }
108
+ activeSession.phase = "analysis";
109
+ }
110
+ if (activeSession.phase === "analysis") {
111
+ lines.push("");
112
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/7: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
113
+ lines.push("");
114
+ activeSession.complexity = analyzeComplexity(activeSession.requirement, activeSession.context);
115
+ const complexityBar = generateComplexityBar(activeSession.complexity);
116
+ lines.push(chalk9.gray(` \u590D\u6742\u5EA6: ${complexityBar} ${activeSession.complexity}/10`));
117
+ if (activeSession.complexity >= COMPLEXITY_THRESHOLD) {
118
+ lines.push(chalk9.yellow(" \u5224\u5B9A: \u590D\u6742\u9700\u6C42\uFF0C\u5EFA\u8BAE\u67B6\u6784\u5E08\u4ECB\u5165"));
119
+ } else {
120
+ lines.push(chalk9.green(" \u5224\u5B9A: \u7B80\u5355\u9700\u6C42\uFF0C\u76F4\u63A5\u8FDB\u5165\u89C4\u683C\u62C6\u89E3"));
121
+ }
122
+ activeSession.phase = "bdd";
123
+ }
124
+ if (activeSession.phase === "bdd") {
125
+ lines.push("");
126
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/7: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
127
+ lines.push("");
128
+ activeSession.bddScenarios = generateBDDScenarios(activeSession.requirement, activeSession.context);
129
+ for (const scenario of activeSession.bddScenarios) {
130
+ lines.push(chalk9.white(` Feature: ${scenario.feature}`));
131
+ for (const s of scenario.scenarios.slice(0, 3)) {
132
+ lines.push(chalk9.gray(` - ${s.name}`));
133
+ }
134
+ if (scenario.scenarios.length > 3) {
135
+ lines.push(chalk9.gray(` ... \u5171 ${scenario.scenarios.length} \u4E2A\u573A\u666F`));
136
+ }
137
+ }
138
+ activeSession.phase = "spec";
139
+ }
140
+ if (activeSession.phase === "spec") {
141
+ lines.push("");
142
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/7: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
143
+ lines.push("");
144
+ activeSession.specItems = generateSpecItems(activeSession.requirement, activeSession.context, activeSession.bddScenarios);
145
+ const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
146
+ lines.push(chalk9.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
147
+ lines.push(chalk9.gray(` \u8DEF\u5F84: ${specPath}`));
148
+ lines.push("");
149
+ lines.push(chalk9.cyan(" \u4EFB\u52A1\u6982\u89C8:"));
150
+ for (const item of activeSession.specItems.slice(0, 5)) {
151
+ const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
152
+ lines.push(chalk9.gray(` ${icon} [${item.id}] ${item.title}`));
153
+ }
154
+ if (activeSession.specItems.length > 5) {
155
+ lines.push(chalk9.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
156
+ }
157
+ lines.push("");
158
+ lines.push(chalk9.yellow.bold("\u23F8\uFE0F \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
159
+ lines.push("");
160
+ lines.push(chalk9.green(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u7EE7\u7EED\u5DE5\u4F5C\u6D41"));
161
+ lines.push(chalk9.red(" n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210\u89C4\u683C"));
162
+ lines.push(chalk9.gray(" c - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41"));
163
+ return { output: lines.join("\n") };
164
+ }
165
+ if (activeSession.phase === "tdd") {
166
+ lines.push("");
167
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/7: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
168
+ lines.push("");
169
+ activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
170
+ lines.push(chalk9.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
171
+ for (const file of activeSession.testFiles) {
172
+ lines.push(chalk9.gray(` - ${file}`));
173
+ }
174
+ activeSession.phase = "develop";
175
+ }
176
+ if (activeSession.phase === "develop") {
177
+ lines.push("");
178
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/7: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
179
+ lines.push("");
180
+ lines.push(chalk9.yellow(" \u{1F4DD} \u5F00\u53D1\u9636\u6BB5"));
181
+ lines.push(chalk9.gray(" \u8BF7\u8C03\u7528 $frontend-dev \u6267\u884C\u5F00\u53D1\u4EFB\u52A1"));
182
+ lines.push(chalk9.gray(" \u6216\u624B\u52A8\u5B9E\u73B0\u4EE3\u7801\u540E\u8F93\u5165 continue \u7EE7\u7EED"));
183
+ return { output: lines.join("\n") };
184
+ }
185
+ if (activeSession.phase === "review") {
186
+ lines.push("");
187
+ lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/7: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
188
+ lines.push("");
189
+ lines.push(chalk9.yellow(" \u{1F50D} \u4EE3\u7801\u5BA1\u6838\u9636\u6BB5"));
190
+ lines.push(chalk9.gray(" \u8BF7\u8C03\u7528 $code-reviewer \u6267\u884C\u4EE3\u7801\u5BA1\u6838"));
191
+ lines.push(chalk9.gray(" \u6216\u8F93\u5165 review \u5B8C\u6210\u5BA1\u6838"));
192
+ }
193
+ return { output: lines.join("\n") };
194
+ } catch (error) {
195
+ lines.push(chalk9.red(`\u9519\u8BEF: ${error.message}`));
196
+ return { output: lines.join("\n") };
197
+ }
198
+ }
199
+ async function handleWorkflowInput(input, ctx) {
200
+ if (!activeSession) return null;
201
+ const trimmed = input.trim().toLowerCase();
202
+ if (trimmed === "c" || trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
203
+ activeSession = null;
204
+ return {
205
+ output: chalk9.yellow("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u53D6\u6D88")
206
+ };
207
+ }
208
+ if (activeSession.phase === "spec") {
209
+ if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
210
+ activeSession.phase = "tdd";
211
+ return executeWorkflow(ctx);
212
+ }
213
+ if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
214
+ activeSession.bddScenarios = generateBDDScenarios(activeSession.requirement, activeSession.context);
215
+ activeSession.specItems = generateSpecItems(activeSession.requirement, activeSession.context, activeSession.bddScenarios);
216
+ const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
217
+ return {
218
+ output: chalk9.cyan("\u{1F504} \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210") + chalk9.gray(`
219
+ \u8DEF\u5F84: ${specPath}`) + chalk9.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9.red("\n n - \u518D\u6B21\u91CD\u65B0\u751F\u6210")
220
+ };
221
+ }
222
+ }
223
+ if (activeSession.phase === "develop") {
224
+ if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
225
+ activeSession.phase = "review";
226
+ return executeWorkflow(ctx);
227
+ }
228
+ }
229
+ if (activeSession.phase === "review") {
230
+ if (trimmed === "review" || trimmed === "\u5BA1\u6838" || trimmed === "pass" || trimmed === "\u901A\u8FC7") {
231
+ await archiveWorkflow(ctx.options.workingDirectory);
232
+ const summary = activeSession.requirement;
233
+ activeSession = null;
234
+ return {
235
+ output: chalk9.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5B8C\u6210") + chalk9.gray(`
236
+ \u9700\u6C42: ${summary}`) + chalk9.cyan("\n\n\u4F7F\u7528 /new <\u9700\u6C42> \u5F00\u59CB\u65B0\u7684\u5DE5\u4F5C\u6D41")
237
+ };
238
+ }
239
+ if (trimmed === "fail" || trimmed === "\u5931\u8D25" || trimmed === "reject" || trimmed === "\u62D2\u7EDD") {
240
+ activeSession.phase = "spec";
241
+ return {
242
+ output: chalk9.yellow("\u21A9\uFE0F \u5BA1\u6838\u672A\u901A\u8FC7\uFF0C\u56DE\u9000\u5230\u89C4\u683C\u9636\u6BB5") + chalk9.gray("\n\u8BF7\u91CD\u65B0\u786E\u8BA4\u6216\u4FEE\u6539\u89C4\u683C") + chalk9.yellow("\n\n\u4F7F\u7528 y \u786E\u8BA4\uFF0Cn \u91CD\u65B0\u751F\u6210")
243
+ };
244
+ }
245
+ }
246
+ return null;
247
+ }
248
+ async function readProjectContext(workingDir) {
249
+ const context = {
250
+ name: path5.basename(workingDir),
251
+ type: "unknown",
252
+ framework: null,
253
+ techStack: [],
254
+ description: "",
255
+ devStandards: "",
256
+ agentsMd: "",
257
+ configYaml: ""
258
+ };
259
+ const agentsPath = path5.join(workingDir, "AGENTS.md");
260
+ try {
261
+ const stats = await fs4.stat(agentsPath);
262
+ if (stats.size <= MAX_FILE_SIZE2) {
263
+ context.agentsMd = await fs4.readFile(agentsPath, "utf-8");
264
+ const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
265
+ if (nameMatch) context.name = nameMatch[1];
266
+ const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
267
+ if (typeMatch) context.type = typeMatch[1];
268
+ const frameworkMatch = context.agentsMd.match(/\|\s*技术框架\s*\|\s*([^\s|]+)/);
269
+ if (frameworkMatch && frameworkMatch[1] !== "\u5F85\u8BC6\u522B") {
270
+ context.framework = frameworkMatch[1];
271
+ }
272
+ }
273
+ } catch {
274
+ }
275
+ const configPath = path5.join(workingDir, "openspec", "config.yaml");
276
+ try {
277
+ const stats = await fs4.stat(configPath);
278
+ if (stats.size <= MAX_FILE_SIZE2) {
279
+ context.configYaml = await fs4.readFile(configPath, "utf-8");
280
+ const nameMatch = context.configYaml.match(/name:\s*(.+)/);
281
+ if (nameMatch) context.name = nameMatch[1].trim();
282
+ const typeMatch = context.configYaml.match(/type:\s*(.+)/);
283
+ if (typeMatch) context.type = typeMatch[1].trim();
284
+ const frameworkMatch = context.configYaml.match(/framework:\s*(.+)/);
285
+ if (frameworkMatch && frameworkMatch[1].trim() !== "null") {
286
+ context.framework = frameworkMatch[1].trim();
287
+ }
288
+ const techStackMatch = context.configYaml.match(/techStack:\s*([\s\S]+?)(?=\n\w)/);
289
+ if (techStackMatch) {
290
+ const techLines = techStackMatch[1].match(/-\s*(.+)/g);
291
+ if (techLines) {
292
+ context.techStack = techLines.map((l) => l.replace(/-\s*/, "").trim());
293
+ }
294
+ }
295
+ }
296
+ } catch {
297
+ }
298
+ const devStandardsPath = path5.join(workingDir, ".sf-cli", "norms", "devstanded.md");
299
+ try {
300
+ const stats = await fs4.stat(devStandardsPath);
301
+ if (stats.size <= MAX_FILE_SIZE2) {
302
+ context.devStandards = await fs4.readFile(devStandardsPath, "utf-8");
303
+ }
304
+ } catch {
305
+ }
306
+ return context;
307
+ }
308
+ function analyzeComplexity(requirement, context) {
309
+ let score = 3;
310
+ if (requirement.length > 100) score += 1;
311
+ if (requirement.length > 200) score += 1;
312
+ const complexKeywords = ["\u67B6\u6784", "\u91CD\u6784", "\u8FC1\u79FB", "\u96C6\u6210", "\u7CFB\u7EDF", "\u6A21\u5757", "\u5DE5\u4F5C\u6D41", "\u6D41\u7A0B", "\u6743\u9650", "\u5B89\u5168"];
313
+ for (const keyword of complexKeywords) {
314
+ if (requirement.includes(keyword)) score += 1;
315
+ }
316
+ const simpleKeywords = ["\u4FEE\u590D", "\u8C03\u6574", "\u4F18\u5316", "\u66F4\u65B0", "\u6DFB\u52A0", "\u6837\u5F0F", "\u6587\u672C"];
317
+ for (const keyword of simpleKeywords) {
318
+ if (requirement.includes(keyword)) score -= 0.5;
319
+ }
320
+ const connectors = ["\u548C", "\u4EE5\u53CA", "\u540C\u65F6", "\u53E6\u5916", "\u6B64\u5916", "\u3001"];
321
+ for (const conn of connectors) {
322
+ const count = (requirement.match(new RegExp(conn, "g")) || []).length;
323
+ score += count * 0.3;
324
+ }
325
+ if (requirement.match(/https?:\/\//)) score += 0.5;
326
+ if (!context.framework) score += 0.5;
327
+ return Math.max(1, Math.min(10, Math.round(score)));
328
+ }
329
+ function generateBDDScenarios(requirement, context) {
330
+ const scenarios = [];
331
+ const features = extractFeatures(requirement);
332
+ for (const feature of features) {
333
+ const scenario = {
334
+ feature: feature.title,
335
+ description: feature.description,
336
+ scenarios: []
337
+ };
338
+ scenario.scenarios.push({
339
+ name: `\u6B63\u5E38\u6D41\u7A0B: ${feature.title}`,
340
+ given: [`\u7528\u6237\u8FDB\u5165\u76F8\u5173\u9875\u9762`],
341
+ when: [`\u7528\u6237\u6267\u884C "${feature.title}" \u64CD\u4F5C`],
342
+ then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5E76\u8FD4\u56DE\u9884\u671F\u7ED3\u679C`]
343
+ });
344
+ if (feature.hasInput) {
345
+ scenario.scenarios.push({
346
+ name: `\u8FB9\u754C\u60C5\u51B5: \u8F93\u5165\u9A8C\u8BC1`,
347
+ given: [`\u7528\u6237\u8FDB\u5165\u8F93\u5165\u754C\u9762`],
348
+ when: [`\u7528\u6237\u8F93\u5165\u8FB9\u754C\u503C\u6216\u7A7A\u503C`],
349
+ then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u8FB9\u754C\u60C5\u51B5`]
350
+ });
351
+ }
352
+ scenarios.push(scenario);
353
+ }
354
+ return scenarios;
355
+ }
356
+ function extractFeatures(requirement) {
357
+ const features = [];
358
+ const urlMatch = requirement.match(/https?:\/\/[^\s]+/);
359
+ if (urlMatch) {
360
+ features.push({
361
+ title: "\u53C2\u8003\u754C\u9762\u5206\u6790",
362
+ description: `\u5206\u6790\u53C2\u8003\u754C\u9762 ${urlMatch[0]}`,
363
+ hasInput: false
364
+ });
365
+ }
366
+ const featurePatterns = [
367
+ { pattern: /排盘|计算|算法/, title: "\u6838\u5FC3\u7B97\u6CD5\u5B9E\u73B0", hasInput: true },
368
+ { pattern: /界面|UI|页面|显示|展示/, title: "\u754C\u9762\u5F00\u53D1", hasInput: true },
369
+ { pattern: /表格|列表/, title: "\u6570\u636E\u5C55\u793A", hasInput: false },
370
+ { pattern: /图表|图形|可视化/, title: "\u56FE\u8868\u53EF\u89C6\u5316", hasInput: false },
371
+ { pattern: /表单|输入/, title: "\u8868\u5355\u5904\u7406", hasInput: true },
372
+ { pattern: /登录|注册|认证/, title: "\u7528\u6237\u8BA4\u8BC1", hasInput: true },
373
+ { pattern: /接口|API/, title: "API \u63A5\u53E3", hasInput: false },
374
+ { pattern: /存储|缓存/, title: "\u6570\u636E\u5B58\u50A8", hasInput: false },
375
+ { pattern: /导出|下载/, title: "\u5BFC\u51FA\u529F\u80FD", hasInput: false },
376
+ { pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", hasInput: true }
377
+ ];
378
+ for (const { pattern, title, hasInput } of featurePatterns) {
379
+ if (pattern.test(requirement)) {
380
+ features.push({
381
+ title,
382
+ description: `${title}\u76F8\u5173\u529F\u80FD`,
383
+ hasInput
384
+ });
385
+ }
386
+ }
387
+ if (features.length === 0) {
388
+ features.push({
389
+ title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
390
+ description: requirement,
391
+ hasInput: true
392
+ });
393
+ }
394
+ return features;
395
+ }
396
+ function generateSpecItems(requirement, context, bddScenarios) {
397
+ const items = [];
398
+ let id = 1;
399
+ for (const scenario of bddScenarios) {
400
+ items.push({
401
+ id: `T${id.toString().padStart(3, "0")}`,
402
+ title: scenario.feature,
403
+ description: scenario.description,
404
+ priority: id <= 2 ? "high" : "medium",
405
+ files: [],
406
+ tests: []
407
+ });
408
+ id++;
409
+ }
410
+ items.push({
411
+ id: `T${id.toString().padStart(3, "0")}`,
412
+ title: "\u5355\u5143\u6D4B\u8BD5",
413
+ description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\u786E\u4FDD\u529F\u80FD\u6B63\u786E\u6027",
414
+ priority: "medium",
415
+ files: [],
416
+ tests: []
417
+ });
418
+ return items;
419
+ }
420
+ async function saveSpecFile(workingDir, session) {
421
+ const specDir = path5.join(workingDir, "openspec", "changes");
422
+ await fs4.mkdir(specDir, { recursive: true });
423
+ const specPath = path5.join(specDir, `${session.id}-spec.md`);
424
+ const content = formatSpecFile(session);
425
+ await fs4.writeFile(specPath, content, "utf-8");
426
+ return specPath;
427
+ }
428
+ function formatSpecFile(session) {
429
+ const lines = [];
430
+ lines.push(`# \u9700\u6C42\u89C4\u683C: ${session.requirement.slice(0, 50)}`);
431
+ lines.push("");
432
+ lines.push(`> \u53D8\u66F4ID: ${session.id}`);
433
+ lines.push(`> \u590D\u6742\u5EA6: ${session.complexity}/10`);
434
+ lines.push(`> \u751F\u6210\u65F6\u95F4: ${session.createdAt.toISOString()}`);
435
+ lines.push("");
436
+ lines.push("---");
437
+ lines.push("");
438
+ lines.push("## BDD \u573A\u666F");
439
+ lines.push("");
440
+ for (const scenario of session.bddScenarios) {
441
+ lines.push(`### Feature: ${scenario.feature}`);
442
+ lines.push("");
443
+ for (const s of scenario.scenarios) {
444
+ lines.push(`**Scenario: ${s.name}**`);
445
+ for (const g of s.given) lines.push(` Given ${g}`);
446
+ for (const w of s.when) lines.push(` When ${w}`);
447
+ for (const t of s.then) lines.push(` Then ${t}`);
448
+ lines.push("");
449
+ }
450
+ }
451
+ lines.push("## \u4EFB\u52A1\u5217\u8868");
452
+ lines.push("");
453
+ for (const item of session.specItems) {
454
+ const priority = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
455
+ lines.push(`- [ ] ${priority} [${item.id}] ${item.title}`);
456
+ }
457
+ lines.push("");
458
+ lines.push("---");
459
+ lines.push("");
460
+ lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
461
+ return lines.join("\n");
462
+ }
463
+ async function generateTests(workingDir, session) {
464
+ const testDir = path5.join(workingDir, "tests");
465
+ await fs4.mkdir(testDir, { recursive: true });
466
+ const testFiles = [];
467
+ for (const scenario of session.bddScenarios) {
468
+ const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
469
+ const testPath = path5.join(testDir, `${testName}.test.ts`);
470
+ const content = generateTestFile(scenario);
471
+ await fs4.writeFile(testPath, content, "utf-8");
472
+ testFiles.push(`tests/${testName}.test.ts`);
473
+ }
474
+ return testFiles;
475
+ }
476
+ function generateTestFile(scenario) {
477
+ const lines = [];
478
+ lines.push(`import { describe, it, expect } from 'vitest';`);
479
+ lines.push("");
480
+ lines.push(`describe('${scenario.feature}', () => {`);
481
+ for (const s of scenario.scenarios) {
482
+ lines.push(` it('${s.name}', () => {`);
483
+ lines.push(` // Given: ${s.given.join(", ")}`);
484
+ lines.push(` // When: ${s.when.join(", ")}`);
485
+ lines.push(` // Then: ${s.then.join(", ")}`);
486
+ lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
487
+ lines.push(` });`);
488
+ lines.push("");
489
+ }
490
+ lines.push(`});`);
491
+ return lines.join("\n");
492
+ }
493
+ async function archiveWorkflow(workingDir) {
494
+ if (!activeSession) return;
495
+ const archiveDir = path5.join(workingDir, "openspec", "spec");
496
+ await fs4.mkdir(archiveDir, { recursive: true });
497
+ const archivePath = path5.join(archiveDir, `${activeSession.id}.md`);
498
+ const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
499
+
500
+ > \u5F52\u6863\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}
501
+ > \u590D\u6742\u5EA6: ${activeSession.complexity}/10
502
+
503
+ ## \u5B8C\u6210\u60C5\u51B5
504
+
505
+ - [x] \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6
506
+ - [x] \u590D\u6742\u5EA6\u8BC4\u4F30
507
+ - [x] BDD \u573A\u666F\u62C6\u89E3
508
+ - [x] OpenSpec \u89C4\u683C
509
+ - [x] TDD \u6D4B\u8BD5\u751F\u6210
510
+ - [x] \u5F00\u53D1\u5B9E\u73B0
511
+ - [x] \u4EE3\u7801\u5BA1\u6838
512
+
513
+ ## \u6D4B\u8BD5\u6587\u4EF6
514
+
515
+ ${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
516
+ `;
517
+ await fs4.writeFile(archivePath, content, "utf-8");
518
+ }
519
+ function generateSessionId() {
520
+ const timestamp = Date.now().toString(36);
521
+ const random = Math.random().toString(36).slice(2, 6);
522
+ return `WF-${timestamp}-${random}`.toUpperCase();
523
+ }
524
+ function generateComplexityBar(score) {
525
+ const filled = Math.round(score / 2);
526
+ const empty = 5 - filled;
527
+ return "\u2588".repeat(filled) + "\u2591".repeat(empty);
528
+ }
529
+ function getPhaseLabel(phase) {
530
+ const labels = {
531
+ context: "\u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6",
532
+ analysis: "\u590D\u6742\u5EA6\u8BC4\u4F30",
533
+ bdd: "BDD \u573A\u666F\u62C6\u89E3",
534
+ spec: "OpenSpec \u89C4\u683C",
535
+ tdd: "TDD \u6D4B\u8BD5\u751F\u6210",
536
+ develop: "\u5F00\u53D1\u5B9E\u73B0",
537
+ review: "\u4EE3\u7801\u5BA1\u6838"
538
+ };
539
+ return labels[phase];
540
+ }
541
+ function getActiveSession() {
542
+ return activeSession;
543
+ }
544
+ function clearActiveSession() {
545
+ activeSession = null;
546
+ }
547
+ var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, activeSession, new_default;
548
+ var init_new = __esm({
549
+ "src/commands/new.ts"() {
550
+ init_esm_shims();
551
+ MAX_FILE_SIZE2 = 1024 * 1024;
552
+ COMPLEXITY_THRESHOLD = 6;
553
+ activeSession = null;
554
+ new_default = handleNew;
555
+ }
556
+ });
557
+
558
+ // src/index.ts
559
+ init_esm_shims();
560
+
561
+ // src/types/index.ts
562
+ init_esm_shims();
563
+
564
+ // src/types/agent.ts
565
+ init_esm_shims();
566
+
567
+ // src/types/mcp.ts
568
+ init_esm_shims();
569
+
570
+ // src/services/config.ts
571
+ init_esm_shims();
20
572
  var DEFAULT_CONFIG = {
21
573
  model: "GLM-5",
22
574
  apiKey: "",
@@ -144,7 +696,11 @@ var ConfigManager = class {
144
696
  }
145
697
  };
146
698
 
699
+ // src/services/model.ts
700
+ init_esm_shims();
701
+
147
702
  // src/types/model.ts
703
+ init_esm_shims();
148
704
  var ModelError = class extends Error {
149
705
  code;
150
706
  provider;
@@ -234,6 +790,12 @@ var AVAILABLE_MODELS = [
234
790
  function getModelInfo(modelId) {
235
791
  return AVAILABLE_MODELS.find((m) => m.id === modelId);
236
792
  }
793
+
794
+ // src/services/adapters/index.ts
795
+ init_esm_shims();
796
+
797
+ // src/services/adapters/base.ts
798
+ init_esm_shims();
237
799
  var BaseAdapter = class {
238
800
  config = null;
239
801
  initialized = false;
@@ -355,6 +917,7 @@ var BaseAdapter = class {
355
917
  };
356
918
 
357
919
  // src/services/adapters/glm.ts
920
+ init_esm_shims();
358
921
  var GLMAdapter = class extends BaseAdapter {
359
922
  name = "GLM-5";
360
923
  provider = "glm";
@@ -516,6 +1079,7 @@ var GLMAdapter = class extends BaseAdapter {
516
1079
  };
517
1080
 
518
1081
  // src/services/adapters/openai.ts
1082
+ init_esm_shims();
519
1083
  var OpenAIAdapter = class extends BaseAdapter {
520
1084
  name = "OpenAI";
521
1085
  provider = "openai";
@@ -692,6 +1256,7 @@ var OpenAIAdapter = class extends BaseAdapter {
692
1256
  };
693
1257
 
694
1258
  // src/services/adapters/claude.ts
1259
+ init_esm_shims();
695
1260
  var ClaudeAdapter = class extends BaseAdapter {
696
1261
  name = "Claude";
697
1262
  provider = "anthropic";
@@ -1188,6 +1753,12 @@ var ModelService = class {
1188
1753
  return new ModelError("UNKNOWN_ERROR", err.message, { retryable: false });
1189
1754
  }
1190
1755
  };
1756
+
1757
+ // src/services/mcp/index.ts
1758
+ init_esm_shims();
1759
+
1760
+ // src/services/mcp/base.ts
1761
+ init_esm_shims();
1191
1762
  var MCPAdapterBase = class {
1192
1763
  client = null;
1193
1764
  transport = null;
@@ -1369,6 +1940,7 @@ var MCPAdapterBase = class {
1369
1940
  };
1370
1941
 
1371
1942
  // src/services/mcp/lanhu.ts
1943
+ init_esm_shims();
1372
1944
  var LanhuMCPAdapter = class extends MCPAdapterBase {
1373
1945
  name = "lanhu";
1374
1946
  platform = "lanhu";
@@ -1747,6 +2319,7 @@ ${children}
1747
2319
  };
1748
2320
 
1749
2321
  // src/services/mcp/figma.ts
2322
+ init_esm_shims();
1750
2323
  var FigmaMCPAdapter = class extends MCPAdapterBase {
1751
2324
  name = "figma";
1752
2325
  platform = "figma";
@@ -2169,6 +2742,7 @@ const Container = styled.div\`
2169
2742
  };
2170
2743
 
2171
2744
  // src/services/mcp/manager.ts
2745
+ init_esm_shims();
2172
2746
  var MCPManager = class {
2173
2747
  adapters = /* @__PURE__ */ new Map();
2174
2748
  connectionStates = /* @__PURE__ */ new Map();
@@ -2413,7 +2987,11 @@ function createMCPManager() {
2413
2987
  return new MCPManager();
2414
2988
  }
2415
2989
 
2990
+ // src/agents/runner.ts
2991
+ init_esm_shims();
2992
+
2416
2993
  // src/agents/definitions.ts
2994
+ init_esm_shims();
2417
2995
  var FRONTEND_DEV_AGENT = {
2418
2996
  id: "frontend-dev",
2419
2997
  name: "\u524D\u7AEF\u5F00\u53D1",
@@ -3038,6 +3616,9 @@ ${divider}
3038
3616
 
3039
3617
  ${content}`;
3040
3618
  }
3619
+
3620
+ // src/agents/executor.ts
3621
+ init_esm_shims();
3041
3622
  var AgentExecutor = class {
3042
3623
  modelService;
3043
3624
  normsManager;
@@ -3423,7 +4004,11 @@ function createAgentExecutor(modelService, normsManager, contextManager) {
3423
4004
  return new AgentExecutor(modelService, normsManager, contextManager);
3424
4005
  }
3425
4006
 
4007
+ // src/agents/index.ts
4008
+ init_esm_shims();
4009
+
3426
4010
  // src/agents/scheduler.ts
4011
+ init_esm_shims();
3427
4012
  var SCHEDULE_RULES = {
3428
4013
  "explore": {
3429
4014
  agent: "architect",
@@ -3601,7 +4186,11 @@ function getScheduleRuleDescription(step) {
3601
4186
  return `${strategyText[rule.strategy]} $${rule.agent} (${agent?.name})`;
3602
4187
  }
3603
4188
 
4189
+ // src/workflow/index.ts
4190
+ init_esm_shims();
4191
+
3604
4192
  // src/workflow/checkpoint.ts
4193
+ init_esm_shims();
3605
4194
  var DEFAULT_CONFIRMATION_POINTS = [
3606
4195
  {
3607
4196
  type: "spec-review",
@@ -3643,8 +4232,8 @@ var ROLLBACK_RULES = {
3643
4232
  // 可回滚到 explore
3644
4233
  "continue": ["new", "explore"],
3645
4234
  // 可回滚到 new 或 explore
3646
- "propose": [],
3647
- // 简单流程初始阶段不可回滚
4235
+ "propose": ["propose"],
4236
+ // 可重新生成规格(回滚到自身)
3648
4237
  "apply": ["new", "explore", "propose"],
3649
4238
  // 代码审查未通过,可回滚到 new/explore (复杂) 或 propose (简单)
3650
4239
  "archive": []
@@ -4438,6 +5027,9 @@ var ConfirmationRequiredError = class extends Error {
4438
5027
  this.name = "ConfirmationRequiredError";
4439
5028
  }
4440
5029
  };
5030
+
5031
+ // src/norms/index.ts
5032
+ init_esm_shims();
4441
5033
  var MAX_FILE_SIZE = 1024 * 1024;
4442
5034
  var MAX_FILES_TO_SCAN = 500;
4443
5035
  var IGNORED_DIRS = ["node_modules", "dist", "build", ".git", "coverage", ".next", ".nuxt"];
@@ -5366,6 +5958,9 @@ ${standard.examples[0]}
5366
5958
  return /:\s*(string|number|boolean|void|any|unknown|never|object|React\.\w+|[A-Z]\w+)(\s*[\)=\{\[;<>]|>)/.test(content);
5367
5959
  }
5368
5960
  };
5961
+
5962
+ // src/context/index.ts
5963
+ init_esm_shims();
5369
5964
  var DEFAULT_LIMIT = 512 * 1024;
5370
5965
  var COMPRESSION_THRESHOLD = 0.9;
5371
5966
  var MIN_MESSAGES_TO_KEEP = 10;
@@ -5672,6 +6267,7 @@ ${summary}`,
5672
6267
  };
5673
6268
 
5674
6269
  // src/cli/parser.ts
6270
+ init_esm_shims();
5675
6271
  var CommandType = /* @__PURE__ */ ((CommandType3) => {
5676
6272
  CommandType3["SLASH"] = "slash";
5677
6273
  CommandType3["AT"] = "at";
@@ -5837,6 +6433,18 @@ var CommandParser = class {
5837
6433
  };
5838
6434
  }
5839
6435
  };
6436
+
6437
+ // src/cli/executor.ts
6438
+ init_esm_shims();
6439
+
6440
+ // src/commands/index.ts
6441
+ init_esm_shims();
6442
+
6443
+ // src/commands/runner.ts
6444
+ init_esm_shims();
6445
+
6446
+ // src/commands/init.ts
6447
+ init_esm_shims();
5840
6448
  async function handleInit(args, ctx) {
5841
6449
  const options = {
5842
6450
  force: args.includes("-f") || args.includes("--force")
@@ -6275,6 +6883,9 @@ async function fileExists(filePath) {
6275
6883
  return false;
6276
6884
  }
6277
6885
  }
6886
+
6887
+ // src/commands/help.ts
6888
+ init_esm_shims();
6278
6889
  var COMMAND_DETAILS = {
6279
6890
  help: {
6280
6891
  usage: "/help [command]",
@@ -6428,6 +7039,14 @@ ${chalk9.yellow("\u793A\u4F8B:")}
6428
7039
  `;
6429
7040
  return { output: help };
6430
7041
  }
7042
+
7043
+ // src/commands/model.ts
7044
+ init_esm_shims();
7045
+
7046
+ // src/services/index.ts
7047
+ init_esm_shims();
7048
+
7049
+ // src/commands/model.ts
6431
7050
  async function handleModel(args, ctx) {
6432
7051
  const subCommand = args[0];
6433
7052
  const configManager = ctx.configManager;
@@ -6645,6 +7264,9 @@ function createSpinner(message) {
6645
7264
  stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
6646
7265
  };
6647
7266
  }
7267
+
7268
+ // src/commands/update.ts
7269
+ init_esm_shims();
6648
7270
  function getPackageInfo() {
6649
7271
  const possiblePaths = [
6650
7272
  path5.resolve(__dirname$1, "..", "..", "package.json"),
@@ -6810,6 +7432,9 @@ function extractTargetVersion(args) {
6810
7432
  }
6811
7433
  return void 0;
6812
7434
  }
7435
+
7436
+ // src/commands/clear.ts
7437
+ init_esm_shims();
6813
7438
  async function handleClear(args, ctx) {
6814
7439
  const options = {
6815
7440
  force: args.includes("-f") || args.includes("--force"),
@@ -6841,6 +7466,9 @@ async function clearHistory(ctx, options = {}) {
6841
7466
  }
6842
7467
  return { output };
6843
7468
  }
7469
+
7470
+ // src/commands/exit.ts
7471
+ init_esm_shims();
6844
7472
  async function handleExit(args, ctx) {
6845
7473
  const options = {
6846
7474
  force: args.includes("-f") || args.includes("--force")
@@ -6873,588 +7501,12 @@ async function exitCLI(ctx, options = {}) {
6873
7501
  exit: true
6874
7502
  };
6875
7503
  }
6876
- var MAX_FILE_SIZE2 = 1024 * 1024;
6877
- var COMPLEXITY_THRESHOLD = 6;
6878
- async function handleNew(args, ctx) {
6879
- const workingDir = ctx.options.workingDirectory;
6880
- const workflowEngine = ctx.workflowEngine;
6881
- if (workflowEngine) {
6882
- const existingState = workflowEngine.getState();
6883
- if (existingState && existingState.status === "running") {
6884
- if (existingState.currentStep === "explore" || existingState.currentStep === "propose") {
6885
- const specPath = path5.join(workingDir, "openspec", "changes", `${existingState.id}-spec.md`);
6886
- if (fs10.existsSync(specPath)) {
6887
- return {
6888
- output: chalk9.yellow("\u5F53\u524D\u5DE5\u4F5C\u6D41\u6B63\u5728\u7B49\u5F85\u89C4\u683C\u786E\u8BA4") + chalk9.gray(`
6889
7504
 
6890
- \u5DE5\u4F5C\u6D41: ${existingState.title}`) + chalk9.gray(`
6891
- \u53D8\u66F4ID: ${existingState.id}`) + chalk9.cyan("\n\n\u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210:") + chalk9.white(`
6892
- ${specPath}`) + chalk9.yellow("\n\n\u8BF7\u786E\u8BA4\u89C4\u683C\u540E\u7EE7\u7EED:") + chalk9.gray("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C") + chalk9.gray("\n /opsx:status - \u67E5\u770B\u8BE6\u60C5")
6893
- };
6894
- }
6895
- }
6896
- return {
6897
- output: chalk9.yellow("\u5F53\u524D\u5DF2\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.white(`
6898
-
6899
- \u{1F4CB} ${existingState.title || existingState.id}`) + chalk9.gray(`
6900
- \u7C7B\u578B: ${existingState.type} | \u590D\u6742\u5EA6: ${existingState.complexity}/10`) + chalk9.cyan(`
7505
+ // src/commands/runner.ts
7506
+ init_new();
6901
7507
 
6902
- \u8FDB\u5EA6: ${existingState.steps.map((s) => {
6903
- const icon = s.status === "completed" ? "\u2713" : s.status === "running" ? "\u25CF" : "\u25CB";
6904
- return `${icon} ${s.step}`;
6905
- }).join(" \u2192 ")}`) + chalk9.yellow("\n\n\u53EF\u7528\u547D\u4EE4:") + chalk9.white("\n /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u8BE6\u60C5") + chalk9.white("\n /opsx:cancel - \u53D6\u6D88\u5F53\u524D\u5DE5\u4F5C\u6D41")
6906
- };
6907
- }
6908
- }
6909
- const { requirement, forceComplexity } = parseArgs(args);
6910
- if (!requirement) {
6911
- return {
6912
- output: chalk9.red("\u8BF7\u8F93\u5165\u9700\u6C42\u63CF\u8FF0") + chalk9.gray("\n\u7528\u6CD5: /new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray("\n\u9009\u9879:") + chalk9.gray("\n --simple \u5F3A\u5236\u4F7F\u7528\u7B80\u5355\u6D41\u7A0B") + chalk9.gray("\n --complex \u5F3A\u5236\u4F7F\u7528\u590D\u6742\u6D41\u7A0B")
6913
- };
6914
- }
6915
- return newFeature({ requirement, forceComplexity }, workingDir, workflowEngine);
6916
- }
6917
- async function newFeature(options, workingDir, workflowEngine) {
6918
- const cwd = workingDir || process.cwd();
6919
- const { requirement, forceComplexity } = options;
6920
- const lines = [];
6921
- try {
6922
- const stats = await fs4.stat(cwd);
6923
- if (!stats.isDirectory()) {
6924
- return {
6925
- output: chalk9.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
6926
- };
6927
- }
6928
- } catch {
6929
- return {
6930
- output: chalk9.red(`\u9519\u8BEF: \u76EE\u5F55\u4E0D\u5B58\u5728\u6216\u65E0\u6743\u9650\u8BBF\u95EE ${cwd}`)
6931
- };
6932
- }
6933
- lines.push(chalk9.cyan("\u{1F50D} \u5206\u6790\u9879\u76EE..."));
6934
- const context = await readProjectContext(cwd);
6935
- lines.push(chalk9.gray(` \u9879\u76EE: ${context.name}`));
6936
- lines.push(chalk9.gray(` \u7C7B\u578B: ${context.type}`));
6937
- lines.push(chalk9.gray(` \u6846\u67B6: ${context.framework || "\u672A\u8BC6\u522B"}`));
6938
- lines.push("");
6939
- lines.push(chalk9.cyan("\u{1F4CA} \u5206\u6790\u9700\u6C42\u590D\u6742\u5EA6..."));
6940
- const analysis = forceComplexity ? createForcedAnalysis(forceComplexity) : analyzeComplexity(requirement, context);
6941
- lines.push(chalk9.gray(` \u590D\u6742\u5EA6: ${analysis.score}/10`));
6942
- lines.push(chalk9.gray(` \u6D41\u7A0B\u7C7B\u578B: ${analysis.recommendation === "complex" ? "\u590D\u6742\u6D41\u7A0B" : "\u7B80\u5355\u6D41\u7A0B"}`));
6943
- for (const factor of analysis.factors) {
6944
- lines.push(chalk9.gray(` - ${factor}`));
6945
- }
6946
- lines.push("");
6947
- lines.push(chalk9.cyan("\u{1F4CB} \u521D\u59CB\u5316\u5DE5\u4F5C\u6D41..."));
6948
- const workflow = workflowEngine || new WorkflowEngine();
6949
- if (!workflowEngine) {
6950
- await workflow.initialize(cwd);
6951
- }
6952
- const state = await workflow.start(requirement, analysis.score, {
6953
- title: extractTitle(requirement)
6954
- });
6955
- lines.push(chalk9.gray(` \u53D8\u66F4ID: ${state.id}`));
6956
- lines.push(chalk9.gray(` \u5DE5\u4F5C\u6D41: ${state.type}`));
6957
- lines.push("");
6958
- lines.push(chalk9.cyan("\u{1F4DD} \u751F\u6210\u89C4\u683C\u62C6\u5206..."));
6959
- const spec = await generateSpec(requirement, context, analysis, state.id);
6960
- const specPath = await saveSpecFile(cwd, spec);
6961
- lines.push(chalk9.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
6962
- lines.push(chalk9.gray(` \u8DEF\u5F84: ${specPath}`));
6963
- lines.push("");
6964
- lines.push(chalk9.cyan.bold("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
6965
- lines.push(chalk9.white(`
6966
- ${spec.summary}`));
6967
- if (spec.items.length > 0) {
6968
- lines.push("");
6969
- lines.push(chalk9.cyan(" \u4EFB\u52A1\u62C6\u5206:"));
6970
- for (const item of spec.items) {
6971
- const priorityIcon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
6972
- lines.push(chalk9.gray(` ${priorityIcon} [${item.id}] ${item.title}`));
6973
- }
6974
- }
6975
- if (spec.risks.length > 0) {
6976
- lines.push("");
6977
- lines.push(chalk9.yellow(" \u26A0\uFE0F \u98CE\u9669\u63D0\u793A:"));
6978
- for (const risk of spec.risks) {
6979
- lines.push(chalk9.gray(` - ${risk}`));
6980
- }
6981
- }
6982
- lines.push("");
6983
- lines.push(chalk9.yellow.bold("\u23F3 \u7B49\u5F85\u89C4\u683C\u786E\u8BA4"));
6984
- lines.push(chalk9.gray("\n\u8BF7\u68C0\u67E5\u751F\u6210\u7684\u89C4\u683C\u6587\u4EF6\uFF0C\u786E\u8BA4\u540E\u7EE7\u7EED:"));
6985
- lines.push(chalk9.white("\n /opsx:confirm spec-review - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
6986
- lines.push(chalk9.white(" /opsx:rollback explore - \u89C4\u683C\u4E0D\u7B26\uFF0C\u91CD\u65B0\u62C6\u5206"));
6987
- lines.push(chalk9.white(" /opsx:status - \u67E5\u770B\u5DE5\u4F5C\u6D41\u72B6\u6001"));
6988
- return { output: lines.join("\n") };
6989
- }
6990
- async function generateSpec(requirement, context, analysis, changeId) {
6991
- const spec = {
6992
- changeId,
6993
- requirement,
6994
- summary: "",
6995
- items: [],
6996
- architectureNotes: [],
6997
- risks: [],
6998
- suggestions: []
6999
- };
7000
- spec.summary = generateSummary(requirement);
7001
- if (analysis.recommendation === "complex") {
7002
- spec.items = generateComplexTasks(requirement, context, analysis);
7003
- spec.architectureNotes = generateArchitectureNotes(requirement, context);
7004
- } else {
7005
- spec.items = generateSimpleTasks(requirement);
7006
- }
7007
- spec.risks = generateRisks(requirement, context, analysis);
7008
- spec.suggestions = generateSuggestions(requirement, context, analysis);
7009
- return spec;
7010
- }
7011
- function generateSummary(requirement) {
7012
- const firstSentence = requirement.split(/[。!?\n]/)[0];
7013
- return firstSentence.length > 100 ? firstSentence.slice(0, 97) + "..." : firstSentence;
7014
- }
7015
- function generateComplexTasks(requirement, context, analysis) {
7016
- const items = [];
7017
- let itemId = 1;
7018
- const featurePatterns = [
7019
- { pattern: /用户|登录|注册|认证|权限/, title: "\u7528\u6237\u8BA4\u8BC1\u6A21\u5757", priority: "high" },
7020
- { pattern: /数据|存储|缓存|数据库/, title: "\u6570\u636E\u5C42\u5B9E\u73B0", priority: "high" },
7021
- { pattern: /接口|API|请求|响应/, title: "API \u63A5\u53E3\u5F00\u53D1", priority: "high" },
7022
- { pattern: /界面|页面|组件|UI/, title: "\u754C\u9762\u5F00\u53D1", priority: "medium" },
7023
- { pattern: /测试|单测|覆盖/, title: "\u6D4B\u8BD5\u7528\u4F8B\u7F16\u5199", priority: "medium" },
7024
- { pattern: /文档|说明/, title: "\u6587\u6863\u7F16\u5199", priority: "low" },
7025
- { pattern: /配置|设置/, title: "\u914D\u7F6E\u7BA1\u7406", priority: "low" },
7026
- { pattern: /优化|性能/, title: "\u6027\u80FD\u4F18\u5316", priority: "medium" },
7027
- { pattern: /安全|加密/, title: "\u5B89\u5168\u5B9E\u73B0", priority: "high" },
7028
- { pattern: /日志|监控/, title: "\u65E5\u5FD7\u76D1\u63A7", priority: "low" }
7029
- ];
7030
- for (const { pattern, title, priority } of featurePatterns) {
7031
- if (pattern.test(requirement)) {
7032
- items.push({
7033
- id: `T${itemId.toString().padStart(3, "0")}`,
7034
- title,
7035
- description: `${title}\u76F8\u5173\u7684\u529F\u80FD\u5B9E\u73B0`,
7036
- priority,
7037
- dependencies: itemId > 1 ? [`T${(itemId - 1).toString().padStart(3, "0")}`] : [],
7038
- estimatedComplexity: priority === "high" ? 3 : priority === "medium" ? 2 : 1
7039
- });
7040
- itemId++;
7041
- }
7042
- }
7043
- if (items.length === 0) {
7044
- items.push({
7045
- id: "T001",
7046
- title: "\u9700\u6C42\u5206\u6790\u4E0E\u8BBE\u8BA1",
7047
- description: "\u5206\u6790\u9700\u6C42\u7EC6\u8282\uFF0C\u8BBE\u8BA1\u5B9E\u73B0\u65B9\u6848",
7048
- priority: "high",
7049
- dependencies: [],
7050
- estimatedComplexity: 2
7051
- });
7052
- items.push({
7053
- id: "T002",
7054
- title: "\u6838\u5FC3\u529F\u80FD\u5B9E\u73B0",
7055
- description: requirement,
7056
- priority: "high",
7057
- dependencies: ["T001"],
7058
- estimatedComplexity: analysis.score
7059
- });
7060
- items.push({
7061
- id: "T003",
7062
- title: "\u6D4B\u8BD5\u4E0E\u9A8C\u8BC1",
7063
- description: "\u7F16\u5199\u6D4B\u8BD5\u7528\u4F8B\uFF0C\u9A8C\u8BC1\u529F\u80FD\u6B63\u786E\u6027",
7064
- priority: "medium",
7065
- dependencies: ["T002"],
7066
- estimatedComplexity: 2
7067
- });
7068
- }
7069
- return items;
7070
- }
7071
- function generateSimpleTasks(requirement, context) {
7072
- return [
7073
- {
7074
- id: "T001",
7075
- title: "\u5B9E\u73B0\u53D8\u66F4",
7076
- description: requirement,
7077
- priority: "high",
7078
- dependencies: [],
7079
- estimatedComplexity: 3
7080
- },
7081
- {
7082
- id: "T002",
7083
- title: "\u6D4B\u8BD5\u9A8C\u8BC1",
7084
- description: "\u9A8C\u8BC1\u53D8\u66F4\u6B63\u786E\u6027",
7085
- priority: "medium",
7086
- dependencies: ["T001"],
7087
- estimatedComplexity: 1
7088
- }
7089
- ];
7090
- }
7091
- function generateArchitectureNotes(requirement, context) {
7092
- const notes = [];
7093
- if (context.framework) {
7094
- notes.push(`\u9879\u76EE\u4F7F\u7528 ${context.framework} \u6846\u67B6\uFF0C\u9700\u9075\u5FAA\u5176\u6700\u4F73\u5B9E\u8DF5`);
7095
- }
7096
- if (requirement.includes("\u6A21\u5757") || requirement.includes("\u7EC4\u4EF6")) {
7097
- notes.push("\u5EFA\u8BAE\u91C7\u7528\u6A21\u5757\u5316\u8BBE\u8BA1\uFF0C\u4FDD\u6301\u7EC4\u4EF6\u804C\u8D23\u5355\u4E00");
7098
- }
7099
- if (requirement.includes("API") || requirement.includes("\u63A5\u53E3")) {
7100
- notes.push("API \u8BBE\u8BA1\u9700\u8003\u8651\u7248\u672C\u63A7\u5236\u548C\u5411\u540E\u517C\u5BB9");
7101
- }
7102
- if (context.structure.srcStructure) {
7103
- notes.push(`\u73B0\u6709\u6E90\u7801\u7ED3\u6784: ${context.structure.srcStructure}`);
7104
- }
7105
- return notes;
7106
- }
7107
- function generateRisks(requirement, context, analysis) {
7108
- const risks = [];
7109
- if (!context.framework) {
7110
- risks.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B\uFF0C\u53EF\u80FD\u5F71\u54CD\u4EE3\u7801\u98CE\u683C\u4E00\u81F4\u6027");
7111
- }
7112
- if (analysis.score >= 7) {
7113
- risks.push("\u9700\u6C42\u590D\u6742\u5EA6\u8F83\u9AD8\uFF0C\u5EFA\u8BAE\u5206\u9636\u6BB5\u5B9E\u73B0");
7114
- }
7115
- if (requirement.includes("\u8FC1\u79FB") || requirement.includes("\u91CD\u6784")) {
7116
- risks.push("\u6D89\u53CA\u73B0\u6709\u4EE3\u7801\u4FEE\u6539\uFF0C\u9700\u6CE8\u610F\u56DE\u5F52\u6D4B\u8BD5");
7117
- }
7118
- if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168")) {
7119
- risks.push("\u6D89\u53CA\u5B89\u5168\u654F\u611F\u529F\u80FD\uFF0C\u9700\u8981\u989D\u5916\u5BA1\u67E5");
7120
- }
7121
- return risks;
7122
- }
7123
- function generateSuggestions(requirement, context, analysis) {
7124
- const suggestions = [];
7125
- if (context.norms.devStandards) {
7126
- suggestions.push("\u9879\u76EE\u5DF2\u6709\u5F00\u53D1\u89C4\u8303\uFF0C\u8BF7\u9075\u5FAA\u73B0\u6709\u89C4\u8303");
7127
- }
7128
- if (analysis.recommendation === "complex") {
7129
- suggestions.push("\u590D\u6742\u9700\u6C42\u5EFA\u8BAE\u5148\u8FDB\u884C\u6280\u672F\u8BC4\u5BA1");
7130
- suggestions.push("\u5EFA\u8BAE\u8C03\u7528 $architect \u83B7\u53D6\u67B6\u6784\u5EFA\u8BAE");
7131
- }
7132
- if (context.techStack.length > 0) {
7133
- suggestions.push(`\u6280\u672F\u6808: ${context.techStack.join(", ")}`);
7134
- }
7135
- return suggestions;
7136
- }
7137
- async function saveSpecFile(cwd, spec) {
7138
- const changesDir = path5.join(cwd, "openspec", "changes");
7139
- await fs4.mkdir(changesDir, { recursive: true });
7140
- const specPath = path5.join(changesDir, `${spec.changeId}-spec.md`);
7141
- const content = formatSpecFile(spec);
7142
- await fs4.writeFile(specPath, content, "utf-8");
7143
- return specPath;
7144
- }
7145
- function formatSpecFile(spec) {
7146
- const lines = [];
7147
- lines.push(`# Spec: ${spec.summary}`);
7148
- lines.push("");
7149
- lines.push(`> \u53D8\u66F4ID: ${spec.changeId}`);
7150
- lines.push(`> \u751F\u6210\u65F6\u95F4: ${(/* @__PURE__ */ new Date()).toISOString()}`);
7151
- lines.push("");
7152
- lines.push("---");
7153
- lines.push("");
7154
- lines.push("## \u9700\u6C42\u6982\u8FF0");
7155
- lines.push("");
7156
- lines.push(spec.requirement);
7157
- lines.push("");
7158
- lines.push("## \u4EFB\u52A1\u62C6\u5206");
7159
- lines.push("");
7160
- for (const item of spec.items) {
7161
- const priorityLabel = item.priority === "high" ? "\u{1F534} \u9AD8" : item.priority === "medium" ? "\u{1F7E1} \u4E2D" : "\u{1F7E2} \u4F4E";
7162
- lines.push(`### ${item.id}: ${item.title}`);
7163
- lines.push("");
7164
- lines.push(`- **\u4F18\u5148\u7EA7**: ${priorityLabel}`);
7165
- lines.push(`- **\u63CF\u8FF0**: ${item.description}`);
7166
- lines.push(`- **\u9884\u4F30\u590D\u6742\u5EA6**: ${item.estimatedComplexity}/5`);
7167
- if (item.dependencies.length > 0) {
7168
- lines.push(`- **\u4F9D\u8D56**: ${item.dependencies.join(", ")}`);
7169
- }
7170
- lines.push("");
7171
- }
7172
- if (spec.architectureNotes.length > 0) {
7173
- lines.push("## \u67B6\u6784\u8BF4\u660E");
7174
- lines.push("");
7175
- for (const note of spec.architectureNotes) {
7176
- lines.push(`- ${note}`);
7177
- }
7178
- lines.push("");
7179
- }
7180
- if (spec.risks.length > 0) {
7181
- lines.push("## \u26A0\uFE0F \u98CE\u9669\u8BC4\u4F30");
7182
- lines.push("");
7183
- for (const risk of spec.risks) {
7184
- lines.push(`- ${risk}`);
7185
- }
7186
- lines.push("");
7187
- }
7188
- if (spec.suggestions.length > 0) {
7189
- lines.push("## \u{1F4A1} \u5EFA\u8BAE");
7190
- lines.push("");
7191
- for (const suggestion of spec.suggestions) {
7192
- lines.push(`- ${suggestion}`);
7193
- }
7194
- lines.push("");
7195
- }
7196
- lines.push("---");
7197
- lines.push("");
7198
- lines.push("## \u786E\u8BA4\u72B6\u6001");
7199
- lines.push("");
7200
- lines.push("- [ ] \u89C4\u683C\u5DF2\u5BA1\u9605");
7201
- lines.push("- [ ] \u4EFB\u52A1\u62C6\u5206\u5DF2\u786E\u8BA4");
7202
- lines.push("");
7203
- lines.push("**\u786E\u8BA4\u540E\u6267\u884C**: `/opsx:confirm spec-review`");
7204
- return lines.join("\n");
7205
- }
7206
- function parseArgs(args) {
7207
- let forceComplexity;
7208
- const filteredArgs = [];
7209
- for (const arg of args) {
7210
- if (arg === "--simple") {
7211
- forceComplexity = "simple";
7212
- } else if (arg === "--complex") {
7213
- forceComplexity = "complex";
7214
- } else {
7215
- filteredArgs.push(arg);
7216
- }
7217
- }
7218
- return {
7219
- requirement: filteredArgs.join(" ").trim(),
7220
- forceComplexity
7221
- };
7222
- }
7223
- async function readProjectContext(cwd) {
7224
- const defaultContext = {
7225
- name: path5.basename(cwd),
7226
- type: "unknown",
7227
- framework: null,
7228
- techStack: [],
7229
- description: "",
7230
- structure: {
7231
- directories: [],
7232
- keyFiles: [],
7233
- srcStructure: ""
7234
- },
7235
- norms: {
7236
- devStandards: "",
7237
- patterns: "",
7238
- weights: ""
7239
- }
7240
- };
7241
- const [agentsContext, configContext, normsContext, structureContext] = await Promise.all([
7242
- readAgentsMd(cwd),
7243
- readConfigYaml(cwd),
7244
- readNorms(cwd),
7245
- analyzeStructure(cwd)
7246
- ]);
7247
- return {
7248
- ...defaultContext,
7249
- ...agentsContext,
7250
- ...configContext,
7251
- norms: normsContext,
7252
- structure: structureContext
7253
- };
7254
- }
7255
- async function readAgentsMd(cwd) {
7256
- const agentsPath = path5.join(cwd, "AGENTS.md");
7257
- try {
7258
- const stats = await fs4.stat(agentsPath);
7259
- if (stats.size > MAX_FILE_SIZE2) {
7260
- console.warn(`\u8B66\u544A: AGENTS.md \u6587\u4EF6\u8FC7\u5927 (${stats.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6`);
7261
- return {};
7262
- }
7263
- const content = await fs4.readFile(agentsPath, "utf-8");
7264
- return parseAgentsMd(content);
7265
- } catch (e) {
7266
- const err = e;
7267
- if (err.code !== "ENOENT") {
7268
- console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 AGENTS.md - ${err.message}`);
7269
- }
7270
- return {};
7271
- }
7272
- }
7273
- function parseAgentsMd(content) {
7274
- const context = {};
7275
- const nameMatch = content.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
7276
- if (nameMatch) {
7277
- context.name = nameMatch[1];
7278
- }
7279
- const typeMatch = content.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
7280
- if (typeMatch) {
7281
- context.type = typeMatch[1];
7282
- }
7283
- const frameworkMatch = content.match(/\|\s*技术框架\s*\|\s*([^\s|]+)/);
7284
- if (frameworkMatch && frameworkMatch[1] !== "\u5F85\u8BC6\u522B") {
7285
- context.framework = frameworkMatch[1];
7286
- }
7287
- const descMatch = content.match(/###\s*1\.2\s*项目描述\s*\n+([^\n#]+)/);
7288
- if (descMatch) {
7289
- context.description = descMatch[1].trim();
7290
- }
7291
- const techStackMatch = content.match(/技术栈[::]\s*([^\n]+)/);
7292
- if (techStackMatch) {
7293
- context.techStack = techStackMatch[1].split(/[,,、]/).map((s) => s.trim()).filter(Boolean);
7294
- }
7295
- return context;
7296
- }
7297
- async function readConfigYaml(cwd) {
7298
- const configPath = path5.join(cwd, "openspec", "config.yaml");
7299
- try {
7300
- const stats = await fs4.stat(configPath);
7301
- if (stats.size > MAX_FILE_SIZE2) {
7302
- console.warn("\u8B66\u544A: config.yaml \u6587\u4EF6\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u8BFB\u53D6");
7303
- return {};
7304
- }
7305
- const content = await fs4.readFile(configPath, "utf-8");
7306
- return parseConfigYaml(content);
7307
- } catch (e) {
7308
- const err = e;
7309
- if (err.code !== "ENOENT") {
7310
- console.warn(`\u8B66\u544A: \u65E0\u6CD5\u8BFB\u53D6 config.yaml - ${err.message}`);
7311
- }
7312
- return {};
7313
- }
7314
- }
7315
- function parseConfigYaml(content) {
7316
- const context = {};
7317
- const nameMatch = content.match(/name:\s*(.+)/);
7318
- if (nameMatch) {
7319
- context.name = nameMatch[1].trim();
7320
- }
7321
- const typeMatch = content.match(/type:\s*(.+)/);
7322
- if (typeMatch) {
7323
- context.type = typeMatch[1].trim();
7324
- }
7325
- const frameworkMatch = content.match(/framework:\s*(.+)/);
7326
- if (frameworkMatch && frameworkMatch[1].trim() !== "null") {
7327
- context.framework = frameworkMatch[1].trim();
7328
- }
7329
- return context;
7330
- }
7331
- async function readNorms(cwd) {
7332
- const normsDir = path5.join(cwd, ".sf-cli", "norms");
7333
- const norms = {
7334
- devStandards: "",
7335
- patterns: "",
7336
- weights: ""
7337
- };
7338
- try {
7339
- const devStandardsPath = path5.join(normsDir, "devstanded.md");
7340
- norms.devStandards = await fs4.readFile(devStandardsPath, "utf-8").catch(() => "");
7341
- } catch {
7342
- }
7343
- try {
7344
- const patternsPath = path5.join(normsDir, "patterns.json");
7345
- norms.patterns = await fs4.readFile(patternsPath, "utf-8").catch(() => "");
7346
- } catch {
7347
- }
7348
- try {
7349
- const weightsPath = path5.join(normsDir, "weights.json");
7350
- norms.weights = await fs4.readFile(weightsPath, "utf-8").catch(() => "");
7351
- } catch {
7352
- }
7353
- return norms;
7354
- }
7355
- async function analyzeStructure(cwd) {
7356
- const structure = {
7357
- directories: [],
7358
- keyFiles: [],
7359
- srcStructure: ""
7360
- };
7361
- try {
7362
- const entries = await fs4.readdir(cwd, { withFileTypes: true });
7363
- for (const entry of entries) {
7364
- if (entry.isDirectory() && !["node_modules", "dist", ".git", "build"].includes(entry.name)) {
7365
- structure.directories.push(entry.name);
7366
- }
7367
- }
7368
- const keyFiles = [
7369
- "package.json",
7370
- "tsconfig.json",
7371
- "AGENTS.md",
7372
- "README.md"
7373
- ];
7374
- for (const file of keyFiles) {
7375
- const filePath = path5.join(cwd, file);
7376
- try {
7377
- await fs4.access(filePath);
7378
- structure.keyFiles.push(file);
7379
- } catch {
7380
- }
7381
- }
7382
- const srcDir = path5.join(cwd, "src");
7383
- try {
7384
- const srcEntries = await fs4.readdir(srcDir, { withFileTypes: true });
7385
- structure.srcStructure = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name).join("/");
7386
- } catch {
7387
- }
7388
- } catch (e) {
7389
- }
7390
- return { structure };
7391
- }
7392
- function analyzeComplexity(requirement, context) {
7393
- let score = 3;
7394
- const factors = [];
7395
- if (requirement.length > 200) {
7396
- score += 1;
7397
- factors.push("\u9700\u6C42\u63CF\u8FF0\u8F83\u957F");
7398
- }
7399
- const complexKeywords = ["\u67B6\u6784", "\u91CD\u6784", "\u8FC1\u79FB", "\u96C6\u6210", "\u7CFB\u7EDF", "\u6A21\u5757", "\u5DE5\u4F5C\u6D41", "\u6D41\u7A0B"];
7400
- const negationWords = ["\u4E0D\u9700\u8981", "\u4E0D\u7528", "\u65E0\u9700", "\u907F\u514D"];
7401
- for (const keyword of complexKeywords) {
7402
- if (requirement.includes(keyword)) {
7403
- const keywordIndex = requirement.indexOf(keyword);
7404
- const contextStart = Math.max(0, keywordIndex - 10);
7405
- const contextText = requirement.slice(contextStart, keywordIndex);
7406
- const hasNegation = negationWords.some((neg) => contextText.includes(neg));
7407
- if (!hasNegation) {
7408
- score += 1;
7409
- factors.push(`\u6D89\u53CA"${keyword}"`);
7410
- }
7411
- }
7412
- }
7413
- const simpleKeywords = ["\u4FEE\u590D", "\u8C03\u6574", "\u4F18\u5316", "\u66F4\u65B0", "\u4FEE\u6539", "\u6DFB\u52A0", "\u6837\u5F0F"];
7414
- for (const keyword of simpleKeywords) {
7415
- if (requirement.includes(keyword)) {
7416
- score -= 0.5;
7417
- factors.push(`\u7B80\u5355\u4FEE\u6539"${keyword}"`);
7418
- }
7419
- }
7420
- const featureCount = (requirement.match(/和|以及|同时|另外|此外/g) || []).length;
7421
- if (featureCount > 0) {
7422
- score += featureCount * 0.5;
7423
- factors.push(`\u6D89\u53CA\u591A\u4E2A\u529F\u80FD\u70B9 (${featureCount + 1}\u4E2A)`);
7424
- }
7425
- if (!context.framework) {
7426
- score += 0.5;
7427
- factors.push("\u9879\u76EE\u6846\u67B6\u672A\u8BC6\u522B");
7428
- }
7429
- if (requirement.includes("\u6570\u636E") || requirement.includes("\u63A5\u53E3") || requirement.includes("API")) {
7430
- score += 1;
7431
- factors.push("\u6D89\u53CA\u6570\u636E\u4EA4\u4E92");
7432
- }
7433
- if (requirement.includes("\u6743\u9650") || requirement.includes("\u5B89\u5168") || requirement.includes("\u8BA4\u8BC1")) {
7434
- score += 1.5;
7435
- factors.push("\u6D89\u53CA\u6743\u9650\u6216\u5B89\u5168");
7436
- }
7437
- score = Math.max(1, Math.min(10, Math.round(score)));
7438
- return {
7439
- score,
7440
- factors: factors.length > 0 ? factors : ["\u9700\u6C42\u76F8\u5BF9\u7B80\u5355"],
7441
- recommendation: score >= COMPLEXITY_THRESHOLD ? "complex" : "simple"
7442
- };
7443
- }
7444
- function createForcedAnalysis(type) {
7445
- return {
7446
- score: type === "complex" ? 8 : 3,
7447
- factors: [`\u7528\u6237\u6307\u5B9A${type === "complex" ? "\u590D\u6742" : "\u7B80\u5355"}\u6D41\u7A0B`],
7448
- recommendation: type
7449
- };
7450
- }
7451
- function extractTitle(requirement) {
7452
- const firstSentence = requirement.split(/[。!?\n]/)[0];
7453
- if (firstSentence.length <= 50) {
7454
- return firstSentence;
7455
- }
7456
- return requirement.slice(0, 47) + "...";
7457
- }
7508
+ // src/commands/opsx.ts
7509
+ init_esm_shims();
7458
7510
  var autoScheduleEnabled = true;
7459
7511
  var DEFAULT_REGRESSION_CONFIG = {
7460
7512
  enabled: true,
@@ -7550,7 +7602,7 @@ async function handleOpsx(command, args, ctx) {
7550
7602
  case "cancel":
7551
7603
  return handleCancel(workflow, args);
7552
7604
  case "rollback":
7553
- return handleRollback(workflow, args);
7605
+ return handleRollback(workflow, args, ctx);
7554
7606
  case "confirm":
7555
7607
  return handleConfirm(workflow, args, ctx);
7556
7608
  case "next":
@@ -7884,16 +7936,20 @@ async function handleRollback(workflow, args, ctx) {
7884
7936
  output: chalk9.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41")
7885
7937
  };
7886
7938
  }
7939
+ const currentStep = state.currentStep;
7887
7940
  const rollbackTargets = workflow.getRollbackTargets();
7888
7941
  if (rollbackTargets.length === 0) {
7889
7942
  return {
7890
- output: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 ${state.currentStep} \u4E0D\u652F\u6301\u56DE\u6EDA`)
7943
+ output: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 ${currentStep} \u4E0D\u652F\u6301\u56DE\u6EDA`)
7891
7944
  };
7892
7945
  }
7946
+ if (currentStep === "propose" && rollbackTargets.includes("propose")) {
7947
+ return handleRegenerateSpec(workflow, ctx);
7948
+ }
7893
7949
  const targetStep = args[0];
7894
7950
  if (!targetStep || !rollbackTargets.includes(targetStep)) {
7895
7951
  return {
7896
- output: generateRollbackPrompt(state.currentStep) + chalk9.cyan("\n\n\u7528\u6CD5: /opsx:rollback <\u76EE\u6807\u9636\u6BB5> <\u539F\u56E0>") + chalk9.gray("\n\u539F\u56E0: code-review-failed | requirement-changed | design-issue | user-request")
7952
+ output: generateRollbackPrompt(currentStep) + chalk9.cyan("\n\n\u7528\u6CD5: /opsx:rollback <\u76EE\u6807\u9636\u6BB5> <\u539F\u56E0>") + chalk9.gray("\n\u539F\u56E0: code-review-failed | requirement-changed | design-issue | user-request")
7897
7953
  };
7898
7954
  }
7899
7955
  const reason = args[1] || "user-request";
@@ -7914,6 +7970,33 @@ ${result.message}`) + chalk9.cyan(`
7914
7970
  };
7915
7971
  }
7916
7972
  }
7973
+ async function handleRegenerateSpec(workflow, ctx) {
7974
+ const state = workflow.getState();
7975
+ if (!state) {
7976
+ return { output: chalk9.red("\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") };
7977
+ }
7978
+ const workingDir = ctx.options.workingDirectory;
7979
+ const specPath = path5.join(workingDir, "openspec", "changes", `${state.id}-spec.md`);
7980
+ workflow.confirm("spec-review", "regenerate");
7981
+ const lines = [];
7982
+ lines.push(chalk9.cyan("\u{1F504} \u91CD\u65B0\u751F\u6210\u89C4\u683C..."));
7983
+ lines.push("");
7984
+ const { readProjectContext: readProjectContext2, analyzeComplexity: analyzeComplexity2, generateSpecContent } = await Promise.resolve().then(() => (init_new(), new_exports));
7985
+ const context = await readProjectContext2(workingDir);
7986
+ const analysis = analyzeComplexity2(state.requirement, context);
7987
+ const specContent = await generateSpecContent(state.id, state.requirement, analysis, context);
7988
+ await fs10.promises.writeFile(specPath, specContent, "utf-8");
7989
+ lines.push(chalk9.green("\u2713 \u89C4\u683C\u5DF2\u91CD\u65B0\u751F\u6210"));
7990
+ lines.push(chalk9.gray(`\u8DEF\u5F84: ${specPath}`));
7991
+ lines.push("");
7992
+ lines.push(chalk9.cyan("\u{1F4CB} \u89C4\u683C\u6982\u89C8:"));
7993
+ lines.push(chalk9.white(` ${state.requirement}`));
7994
+ lines.push("");
7995
+ lines.push(chalk9.yellow("\u8BF7\u68C0\u67E5\u65B0\u7684\u89C4\u683C\u6587\u4EF6\uFF0C\u786E\u8BA4\u540E\u8F93\u5165:"));
7996
+ lines.push(chalk9.green(" y - \u786E\u8BA4\u89C4\u683C\uFF0C\u8FDB\u5165\u4E0B\u4E00\u9636\u6BB5"));
7997
+ lines.push(chalk9.red(" n - \u4E0D\u6EE1\u610F\uFF0C\u518D\u6B21\u91CD\u65B0\u751F\u6210"));
7998
+ return { output: lines.join("\n") };
7999
+ }
7917
8000
  async function handleConfirm(workflow, args, ctx) {
7918
8001
  const state = workflow.getState();
7919
8002
  if (!state) {
@@ -8173,6 +8256,12 @@ async function runSlashCommand(command, args, ctx) {
8173
8256
  function normalizeCommand(command) {
8174
8257
  return command.toLowerCase().trim();
8175
8258
  }
8259
+
8260
+ // src/commands/index.ts
8261
+ init_new();
8262
+
8263
+ // src/commands/reference.ts
8264
+ init_esm_shims();
8176
8265
  async function handleFileReference(filePath, ctx) {
8177
8266
  const cwd = ctx.options.workingDirectory;
8178
8267
  const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.join(cwd, filePath);
@@ -8212,6 +8301,9 @@ ${preview}`) + (truncated ? chalk9.gray(`
8212
8301
  };
8213
8302
  }
8214
8303
  }
8304
+
8305
+ // src/commands/shell.ts
8306
+ init_esm_shims();
8215
8307
  async function executeShell(command, ctx) {
8216
8308
  const dangerousCommands = ["rm -rf", "del /", "format", "mkfs", "dd if="];
8217
8309
  const isDangerous = dangerousCommands.some(
@@ -8258,7 +8350,22 @@ async function executeShell(command, ctx) {
8258
8350
  }, 6e4);
8259
8351
  });
8260
8352
  }
8353
+
8354
+ // src/commands/natural.ts
8355
+ init_esm_shims();
8356
+ init_new();
8261
8357
  async function handleNaturalLanguage(input, ctx) {
8358
+ input.trim().toLowerCase();
8359
+ const session = getActiveSession();
8360
+ if (session) {
8361
+ const result = await handleWorkflowInput(input, ctx);
8362
+ if (result) {
8363
+ return {
8364
+ output: result.output,
8365
+ contextUsed: 0
8366
+ };
8367
+ }
8368
+ }
8262
8369
  ctx.contextManager.addMessage({
8263
8370
  role: "user",
8264
8371
  content: input
@@ -8271,7 +8378,8 @@ async function handleNaturalLanguage(input, ctx) {
8271
8378
  }
8272
8379
 
8273
8380
  // src/cli/executor.ts
8274
- var ALLOWED_COMMANDS_WITHOUT_WORKFLOW = [
8381
+ init_new();
8382
+ var BASIC_COMMANDS = [
8275
8383
  "help",
8276
8384
  "h",
8277
8385
  "?",
@@ -8290,59 +8398,32 @@ var ALLOWED_COMMANDS_WITHOUT_WORKFLOW = [
8290
8398
  "update",
8291
8399
  "u",
8292
8400
  "version",
8293
- "v",
8294
- // OpenSpec 工作流管理命令(始终允许)
8295
- "opsx:status",
8296
- "opsx:cancel",
8297
- "opsx:rollback"
8401
+ "v"
8298
8402
  ];
8299
- var STAGE_PERMISSIONS = {
8300
- "explore": {
8301
- canRead: true,
8302
- canWrite: false,
8303
- canRunShell: false,
8304
- allowedAgents: ["architect"]
8305
- },
8306
- "new": {
8307
- canRead: true,
8308
- canWrite: true,
8309
- canRunShell: false,
8310
- allowedAgents: ["frontend-dev", "architect"]
8311
- },
8312
- "continue": {
8313
- canRead: true,
8314
- canWrite: true,
8315
- canRunShell: true,
8316
- allowedAgents: ["frontend-dev", "tester"]
8317
- },
8318
- "propose": {
8319
- canRead: true,
8320
- canWrite: true,
8321
- canRunShell: true,
8322
- allowedAgents: ["frontend-dev"]
8323
- },
8324
- "apply": {
8325
- canRead: true,
8326
- canWrite: true,
8327
- canRunShell: true,
8328
- allowedAgents: ["code-reviewer"]
8329
- },
8330
- "archive": {
8331
- canRead: true,
8332
- canWrite: false,
8333
- canRunShell: false,
8334
- allowedAgents: []
8335
- }
8336
- };
8337
8403
  var CommandExecutor = class {
8338
8404
  async execute(parseResult, ctx) {
8339
8405
  if (!parseResult.success || !parseResult.command) {
8340
8406
  return { output: chalk9.red(`\u9519\u8BEF: ${parseResult.error}`) };
8341
8407
  }
8342
8408
  const { command } = parseResult;
8343
- const workflowCheck = this.checkWorkflowPermission(command, ctx);
8344
- if (!workflowCheck.allowed) {
8345
- return { output: workflowCheck.message };
8409
+ const hasActiveWorkflow = getActiveSession() !== null;
8410
+ if (!hasActiveWorkflow) {
8411
+ if (command.type === "slash" /* SLASH */) {
8412
+ const cmd = command.command?.toLowerCase() || "";
8413
+ if (!BASIC_COMMANDS.includes(cmd)) {
8414
+ return {
8415
+ output: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8416
+ };
8417
+ }
8418
+ } else if (command.type === "dollar" /* DOLLAR */) {
8419
+ return {
8420
+ output: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8421
+ };
8422
+ } else if (command.type === "shell" /* SHELL */) {
8423
+ return {
8424
+ output: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8425
+ };
8426
+ }
8346
8427
  }
8347
8428
  switch (command.type) {
8348
8429
  case "slash" /* SLASH */:
@@ -8361,98 +8442,30 @@ var CommandExecutor = class {
8361
8442
  return { output: chalk9.red("\u672A\u77E5\u7684\u547D\u4EE4\u7C7B\u578B") };
8362
8443
  }
8363
8444
  }
8364
- /**
8365
- * 检查工作流权限
8366
- */
8367
- checkWorkflowPermission(command, ctx) {
8368
- const workflowEngine = ctx.workflowEngine;
8369
- const workflowState = workflowEngine?.getState();
8370
- if (command.type === "slash" /* SLASH */) {
8371
- const cmd = command.command?.toLowerCase();
8372
- if (cmd?.startsWith("opsx:")) {
8373
- return { allowed: true };
8374
- }
8375
- }
8376
- if (!workflowState) {
8377
- if (command.type === "slash" /* SLASH */) {
8378
- const cmd = command.command?.toLowerCase();
8379
- if (!ALLOWED_COMMANDS_WITHOUT_WORKFLOW.includes(cmd)) {
8380
- return {
8381
- allowed: false,
8382
- message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8383
- };
8384
- }
8385
- }
8386
- if (command.type === "natural" /* NATURAL */) {
8387
- return {
8388
- allowed: false,
8389
- message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8390
- };
8391
- }
8392
- if (command.type === "dollar" /* DOLLAR */) {
8393
- return {
8394
- allowed: false,
8395
- message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8396
- };
8397
- }
8398
- if (command.type === "shell" /* SHELL */) {
8399
- return {
8400
- allowed: false,
8401
- message: chalk9.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
8402
- };
8403
- }
8404
- return { allowed: true };
8405
- }
8406
- const currentStep = workflowState.currentStep;
8407
- const permissions = STAGE_PERMISSIONS[currentStep];
8408
- if (command.type === "at" /* AT */ && !permissions.canRead) {
8409
- return {
8410
- allowed: false,
8411
- message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8BFB\u53D6\u6587\u4EF6`)
8412
- };
8413
- }
8414
- if (command.type === "dollar" /* DOLLAR */) {
8415
- const agentId = command.agent?.toLowerCase().replace("$", "");
8416
- if (!permissions.allowedAgents.includes(agentId)) {
8417
- return {
8418
- allowed: false,
8419
- message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u8C03\u7528 $${agentId} Agent`) + chalk9.gray(`
8420
- \u5F53\u524D\u9636\u6BB5\u5141\u8BB8\u7684 Agent: ${permissions.allowedAgents.map((a) => `$${a}`).join(", ") || "\u65E0"}`)
8421
- };
8422
- }
8423
- }
8424
- if (command.type === "shell" /* SHELL */ && !permissions.canRunShell) {
8425
- return {
8426
- allowed: false,
8427
- message: chalk9.yellow(`\u5F53\u524D\u9636\u6BB5 [${currentStep}] \u4E0D\u5141\u8BB8\u6267\u884C Shell \u547D\u4EE4`)
8428
- };
8429
- }
8430
- return { allowed: true };
8431
- }
8432
8445
  async executeSlashCommand(command, ctx) {
8433
8446
  const result = await runSlashCommand(
8434
- command.command,
8447
+ command.command || "",
8435
8448
  command.args || [],
8436
8449
  ctx
8437
8450
  );
8438
8451
  return { output: result.output, exit: result.exit };
8439
8452
  }
8440
8453
  async executeFileReference(command, ctx) {
8441
- const result = await handleFileReference(command.path, ctx);
8454
+ const result = await handleFileReference(command.path || "", ctx);
8442
8455
  return {
8443
8456
  output: result.output,
8444
8457
  contextUsed: result.contextUsed
8445
8458
  };
8446
8459
  }
8447
8460
  async executeAgent(command, ctx) {
8448
- const result = await runAgent(command.agent, command.args || [], ctx);
8461
+ const result = await runAgent(command.agent || "", command.args || [], ctx);
8449
8462
  return {
8450
8463
  output: result.output,
8451
8464
  contextUsed: result.contextUsed
8452
8465
  };
8453
8466
  }
8454
8467
  async executeShell(command, ctx) {
8455
- const result = await executeShell(command.shellCommand, ctx);
8468
+ const result = await executeShell(command.shellCommand || "", ctx);
8456
8469
  return { output: result.output };
8457
8470
  }
8458
8471
  async executeNaturalLanguage(command, ctx) {
@@ -8469,6 +8482,9 @@ var CommandExecutor = class {
8469
8482
  };
8470
8483
  }
8471
8484
  };
8485
+
8486
+ // src/cli/completer.ts
8487
+ init_esm_shims();
8472
8488
  var Completer = class {
8473
8489
  parser;
8474
8490
  workingDirectory;