@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/CHANGELOG.md +44 -0
- package/dist/cli/index.js +4777 -4618
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +374 -382
- package/dist/index.d.ts +374 -382
- package/dist/index.js +733 -716
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +725 -709
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
|
|
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
|
-
|
|
6891
|
-
|
|
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
|
-
|
|
6903
|
-
|
|
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 ${
|
|
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(
|
|
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
|
-
|
|
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
|
|
8344
|
-
if (!
|
|
8345
|
-
|
|
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;
|