@nick848/sf-cli 1.0.16 → 1.0.18
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 +24 -0
- package/dist/cli/index.js +510 -35
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +508 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +508 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -84,6 +84,7 @@ async function handleNew(args, ctx) {
|
|
|
84
84
|
context: null,
|
|
85
85
|
clarityScore: 0,
|
|
86
86
|
clarificationQuestions: [],
|
|
87
|
+
referenceResources: [],
|
|
87
88
|
complexity: 0,
|
|
88
89
|
bddScenarios: [],
|
|
89
90
|
specItems: [],
|
|
@@ -126,7 +127,7 @@ async function executeWorkflow(ctx) {
|
|
|
126
127
|
const lines = [];
|
|
127
128
|
try {
|
|
128
129
|
if (activeSession.phase === "context") {
|
|
129
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/
|
|
130
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
|
|
130
131
|
lines.push("");
|
|
131
132
|
activeSession.context = await readProjectContext(ctx.options.workingDirectory);
|
|
132
133
|
lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
|
|
@@ -140,7 +141,7 @@ async function executeWorkflow(ctx) {
|
|
|
140
141
|
}
|
|
141
142
|
if (activeSession.phase === "clarify") {
|
|
142
143
|
lines.push("");
|
|
143
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/
|
|
144
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/9: \u9700\u6C42\u6F84\u6E05 \u2501\u2501\u2501"));
|
|
144
145
|
lines.push("");
|
|
145
146
|
const clarityResult = analyzeRequirementClarity(
|
|
146
147
|
activeSession.requirement,
|
|
@@ -166,11 +167,40 @@ async function executeWorkflow(ctx) {
|
|
|
166
167
|
return { output: lines.join("\n") };
|
|
167
168
|
}
|
|
168
169
|
lines.push(chalk9__default.default.green(" \u2713 \u9700\u6C42\u6E05\u6670\uFF0C\u7EE7\u7EED\u4E0B\u4E00\u6B65"));
|
|
170
|
+
activeSession.phase = "reference";
|
|
171
|
+
}
|
|
172
|
+
if (activeSession.phase === "reference") {
|
|
173
|
+
lines.push("");
|
|
174
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/9: \u53C2\u8003\u8D44\u6E90\u5206\u6790 \u2501\u2501\u2501"));
|
|
175
|
+
lines.push("");
|
|
176
|
+
const urls = extractUrls(activeSession.refinedRequirement);
|
|
177
|
+
if (urls.length > 0) {
|
|
178
|
+
lines.push(chalk9__default.default.gray(` \u53D1\u73B0 ${urls.length} \u4E2A\u53C2\u8003\u94FE\u63A5`));
|
|
179
|
+
lines.push("");
|
|
180
|
+
for (const url of urls) {
|
|
181
|
+
lines.push(chalk9__default.default.gray(` \u{1F4CE} ${url}`));
|
|
182
|
+
try {
|
|
183
|
+
const resource = await fetchAndAnalyzeReference(url, ctx);
|
|
184
|
+
activeSession.referenceResources.push(resource);
|
|
185
|
+
lines.push(chalk9__default.default.green(` \u2713 \u5DF2\u5206\u6790`));
|
|
186
|
+
activeSession.refinedRequirement += `
|
|
187
|
+
|
|
188
|
+
\u3010\u53C2\u8003\u8D44\u6E90\u5206\u6790 - ${url}\u3011
|
|
189
|
+
${resource.analysis}`;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
lines.push(chalk9__default.default.yellow(` \u26A0 \u83B7\u53D6\u5931\u8D25: ${error.message}`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
lines.push("");
|
|
195
|
+
lines.push(chalk9__default.default.green(" \u2713 \u53C2\u8003\u8D44\u6E90\u5206\u6790\u5B8C\u6210"));
|
|
196
|
+
} else {
|
|
197
|
+
lines.push(chalk9__default.default.gray(" \u65E0\u5916\u90E8\u53C2\u8003\u94FE\u63A5"));
|
|
198
|
+
}
|
|
169
199
|
activeSession.phase = "analysis";
|
|
170
200
|
}
|
|
171
201
|
if (activeSession.phase === "analysis") {
|
|
172
202
|
lines.push("");
|
|
173
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
203
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/9: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
|
|
174
204
|
lines.push("");
|
|
175
205
|
activeSession.complexity = analyzeComplexity(
|
|
176
206
|
activeSession.refinedRequirement,
|
|
@@ -187,13 +217,28 @@ async function executeWorkflow(ctx) {
|
|
|
187
217
|
}
|
|
188
218
|
if (activeSession.phase === "bdd") {
|
|
189
219
|
lines.push("");
|
|
190
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
220
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
191
221
|
lines.push("");
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
activeSession.
|
|
196
|
-
|
|
222
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
223
|
+
loader.start();
|
|
224
|
+
try {
|
|
225
|
+
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
226
|
+
activeSession.refinedRequirement,
|
|
227
|
+
activeSession.context,
|
|
228
|
+
activeSession.clarificationQuestions,
|
|
229
|
+
activeSession.referenceResources,
|
|
230
|
+
ctx
|
|
231
|
+
);
|
|
232
|
+
loader.stop(chalk9__default.default.green(" \u2713 BDD \u573A\u666F\u5DF2\u751F\u6210"));
|
|
233
|
+
} catch {
|
|
234
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840 BDD \u751F\u6210"));
|
|
235
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
236
|
+
activeSession.refinedRequirement,
|
|
237
|
+
activeSession.context,
|
|
238
|
+
activeSession.clarificationQuestions,
|
|
239
|
+
activeSession.referenceResources
|
|
240
|
+
);
|
|
241
|
+
}
|
|
197
242
|
for (const scenario of activeSession.bddScenarios) {
|
|
198
243
|
lines.push(chalk9__default.default.white(` Feature: ${scenario.feature}`));
|
|
199
244
|
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
@@ -207,13 +252,14 @@ async function executeWorkflow(ctx) {
|
|
|
207
252
|
}
|
|
208
253
|
if (activeSession.phase === "spec") {
|
|
209
254
|
lines.push("");
|
|
210
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
255
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
211
256
|
lines.push("");
|
|
212
257
|
activeSession.specItems = generateSpecItems(
|
|
213
258
|
activeSession.refinedRequirement,
|
|
214
259
|
activeSession.context,
|
|
215
260
|
activeSession.bddScenarios,
|
|
216
|
-
activeSession.clarificationQuestions
|
|
261
|
+
activeSession.clarificationQuestions,
|
|
262
|
+
activeSession.referenceResources
|
|
217
263
|
);
|
|
218
264
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
219
265
|
lines.push(chalk9__default.default.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
@@ -237,9 +283,9 @@ async function executeWorkflow(ctx) {
|
|
|
237
283
|
}
|
|
238
284
|
if (activeSession.phase === "tdd") {
|
|
239
285
|
lines.push("");
|
|
240
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
286
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
241
287
|
lines.push("");
|
|
242
|
-
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
288
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession, ctx);
|
|
243
289
|
lines.push(chalk9__default.default.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
244
290
|
for (const file of activeSession.testFiles) {
|
|
245
291
|
lines.push(chalk9__default.default.gray(` - ${file}`));
|
|
@@ -248,7 +294,7 @@ async function executeWorkflow(ctx) {
|
|
|
248
294
|
}
|
|
249
295
|
if (activeSession.phase === "develop") {
|
|
250
296
|
lines.push("");
|
|
251
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
297
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/9: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
|
|
252
298
|
lines.push("");
|
|
253
299
|
lines.push(chalk9__default.default.yellow(" \u{1F680} \u6B63\u5728\u8C03\u7528 AI \u751F\u6210\u4EE3\u7801..."));
|
|
254
300
|
try {
|
|
@@ -273,7 +319,7 @@ async function executeWorkflow(ctx) {
|
|
|
273
319
|
}
|
|
274
320
|
if (activeSession.phase === "review") {
|
|
275
321
|
lines.push("");
|
|
276
|
-
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
322
|
+
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 9/9: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
|
|
277
323
|
lines.push("");
|
|
278
324
|
lines.push(chalk9__default.default.yellow(" \u{1F50D} \u6B63\u5728\u8FDB\u884C\u4EE3\u7801\u5BA1\u6838..."));
|
|
279
325
|
try {
|
|
@@ -536,6 +582,8 @@ function getCategoryLabel(category) {
|
|
|
536
582
|
async function executeDevelopment(ctx, session) {
|
|
537
583
|
const workingDir = ctx.options.workingDirectory;
|
|
538
584
|
const files = [];
|
|
585
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
|
|
586
|
+
loader.start();
|
|
539
587
|
try {
|
|
540
588
|
const systemPrompt = buildDevelopmentPrompt(session);
|
|
541
589
|
const messages = [
|
|
@@ -563,11 +611,15 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
563
611
|
content: systemPrompt
|
|
564
612
|
}
|
|
565
613
|
];
|
|
614
|
+
loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
|
|
566
615
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
567
616
|
temperature: 0.3,
|
|
568
617
|
maxTokens: 8e3,
|
|
569
|
-
agent: "frontend-dev"
|
|
618
|
+
agent: "frontend-dev",
|
|
619
|
+
timeout: 18e4
|
|
620
|
+
// 3 分钟超时
|
|
570
621
|
});
|
|
622
|
+
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
571
623
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
572
624
|
for (const block of codeBlocks) {
|
|
573
625
|
const filePath = path4__namespace.join(workingDir, block.filename);
|
|
@@ -596,8 +648,10 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
596
648
|
await fs4__namespace.writeFile(filePath, stubCode, "utf-8");
|
|
597
649
|
files.push(`src/features/${fileName}`);
|
|
598
650
|
}
|
|
651
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
|
|
599
652
|
return { success: true, files };
|
|
600
653
|
} catch (error) {
|
|
654
|
+
loader.stop();
|
|
601
655
|
return {
|
|
602
656
|
success: false,
|
|
603
657
|
files: [],
|
|
@@ -815,13 +869,41 @@ function analyzeComplexity(requirement, context) {
|
|
|
815
869
|
if (!context.framework) score += 0.5;
|
|
816
870
|
return Math.max(1, Math.min(10, Math.round(score)));
|
|
817
871
|
}
|
|
818
|
-
function generateBDDScenarios(requirement, context, questions) {
|
|
872
|
+
function generateBDDScenarios(requirement, context, questions, references = []) {
|
|
819
873
|
const scenarios = [];
|
|
820
874
|
questions.find((q) => q.category === "ui" && q.answered)?.answer;
|
|
821
875
|
const interactionAnswer = questions.find((q) => q.category === "interaction" && q.answered)?.answer;
|
|
822
876
|
const edgeAnswer = questions.find((q) => q.category === "edge" && q.answered)?.answer;
|
|
877
|
+
if (references.length > 0) {
|
|
878
|
+
for (const ref of references) {
|
|
879
|
+
const refFeatures = extractFeaturesFromReference(ref);
|
|
880
|
+
for (const feature of refFeatures) {
|
|
881
|
+
const scenario = {
|
|
882
|
+
feature: feature.title,
|
|
883
|
+
description: feature.description,
|
|
884
|
+
scenarios: []
|
|
885
|
+
};
|
|
886
|
+
scenario.scenarios.push({
|
|
887
|
+
name: `\u6B63\u5E38\u6D41\u7A0B: ${feature.title}`,
|
|
888
|
+
given: [`\u7528\u6237\u8FDB\u5165\u76F8\u5173\u9875\u9762`],
|
|
889
|
+
when: [`\u7528\u6237\u6267\u884C "${feature.title}" \u64CD\u4F5C`],
|
|
890
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5E76\u8FD4\u56DE\u9884\u671F\u7ED3\u679C`]
|
|
891
|
+
});
|
|
892
|
+
if (feature.hasInput) {
|
|
893
|
+
scenario.scenarios.push({
|
|
894
|
+
name: `\u8FB9\u754C\u60C5\u51B5: \u8F93\u5165\u9A8C\u8BC1`,
|
|
895
|
+
given: [`\u7528\u6237\u8FDB\u5165\u8F93\u5165\u754C\u9762`],
|
|
896
|
+
when: [`\u7528\u6237\u8F93\u5165\u8FB9\u754C\u503C\u6216\u7A7A\u503C`],
|
|
897
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u8FB9\u754C\u60C5\u51B5`]
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
scenarios.push(scenario);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
823
904
|
const features = extractFeatures(requirement);
|
|
824
905
|
for (const feature of features) {
|
|
906
|
+
if (scenarios.some((s) => s.feature === feature.title)) continue;
|
|
825
907
|
const scenario = {
|
|
826
908
|
feature: feature.title,
|
|
827
909
|
description: feature.description,
|
|
@@ -853,6 +935,135 @@ function generateBDDScenarios(requirement, context, questions) {
|
|
|
853
935
|
}
|
|
854
936
|
return scenarios;
|
|
855
937
|
}
|
|
938
|
+
async function generateBDDScenariosWithAI(requirement, context, questions, references, ctx) {
|
|
939
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\u548C\u4E1A\u52A1\u5206\u6790\u5E08\u3002\u8BF7\u6839\u636E\u4EE5\u4E0B\u9700\u6C42\u751F\u6210\u8BE6\u7EC6\u7684 BDD (Behavior Driven Development) \u573A\u666F\u3002
|
|
940
|
+
|
|
941
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
942
|
+
${requirement}
|
|
943
|
+
|
|
944
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
945
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "\u672A\u6307\u5B9A"}
|
|
946
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
947
|
+
|
|
948
|
+
## \u6F84\u6E05\u95EE\u7B54
|
|
949
|
+
${questions.filter((q) => q.answered).map((q) => `- Q: ${q.question}
|
|
950
|
+
A: ${q.answer}`).join("\n")}
|
|
951
|
+
|
|
952
|
+
## \u53C2\u8003\u8D44\u6E90\u5206\u6790
|
|
953
|
+
${references.map((r) => `### ${r.url}
|
|
954
|
+
${r.analysis}`).join("\n\n")}
|
|
955
|
+
|
|
956
|
+
## \u8981\u6C42
|
|
957
|
+
1. \u6BCF\u4E2A\u529F\u80FD\u6A21\u5757\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684 Feature
|
|
958
|
+
2. \u6BCF\u4E2A Feature \u5305\u542B\u591A\u4E2A\u5177\u4F53\u7684 Scenario
|
|
959
|
+
3. \u4F7F\u7528 Given-When-Then \u683C\u5F0F
|
|
960
|
+
4. \u573A\u666F\u8981\u8986\u76D6: \u6B63\u5E38\u6D41\u7A0B\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u5F02\u5E38\u5904\u7406
|
|
961
|
+
5. \u573A\u666F\u8981\u5177\u4F53\u53EF\u6D4B\u8BD5\uFF0C\u4E0D\u8981\u6CDB\u6CDB\u800C\u8C08
|
|
962
|
+
|
|
963
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
964
|
+
\`\`\`json
|
|
965
|
+
[
|
|
966
|
+
{
|
|
967
|
+
"feature": "\u529F\u80FD\u540D\u79F0",
|
|
968
|
+
"description": "\u529F\u80FD\u63CF\u8FF0",
|
|
969
|
+
"scenarios": [
|
|
970
|
+
{
|
|
971
|
+
"name": "\u573A\u666F\u540D\u79F0",
|
|
972
|
+
"given": ["\u524D\u7F6E\u6761\u4EF61", "\u524D\u7F6E\u6761\u4EF62"],
|
|
973
|
+
"when": ["\u64CD\u4F5C1", "\u64CD\u4F5C2"],
|
|
974
|
+
"then": ["\u9884\u671F\u7ED3\u679C1", "\u9884\u671F\u7ED3\u679C2"]
|
|
975
|
+
}
|
|
976
|
+
]
|
|
977
|
+
}
|
|
978
|
+
]
|
|
979
|
+
\`\`\`
|
|
980
|
+
|
|
981
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9\u3002`;
|
|
982
|
+
const response = await ctx.modelService.sendMessage([
|
|
983
|
+
{ role: "user", content: prompt2 }
|
|
984
|
+
], {
|
|
985
|
+
temperature: 0.3,
|
|
986
|
+
maxTokens: 4e3,
|
|
987
|
+
timeout: 12e4
|
|
988
|
+
});
|
|
989
|
+
try {
|
|
990
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
991
|
+
if (jsonMatch) {
|
|
992
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
993
|
+
}
|
|
994
|
+
return JSON.parse(response.content);
|
|
995
|
+
} catch {
|
|
996
|
+
return generateBDDScenarios(requirement, context, questions, references);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
function extractFeaturesFromReference(ref) {
|
|
1000
|
+
const features = [];
|
|
1001
|
+
const analysis = ref.analysis;
|
|
1002
|
+
const featureSection = analysis.match(/###?\s*4\.\s*功能拆分建议[\s\S]*?(?=###?\s*\d|$)/i);
|
|
1003
|
+
if (featureSection) {
|
|
1004
|
+
const taskMatches = featureSection[0].matchAll(/[-*]\s*\*\*([^*]+)\*\*[::]?\s*([^\n]+)/g);
|
|
1005
|
+
for (const match of taskMatches) {
|
|
1006
|
+
features.push({
|
|
1007
|
+
title: match[1].trim(),
|
|
1008
|
+
description: match[2].trim(),
|
|
1009
|
+
hasInput: match[2].includes("\u8F93\u5165") || match[2].includes("\u8868\u5355") || match[2].includes("\u7528\u6237")
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const bizSection = analysis.match(/###?\s*1\.\s*业务功能分析[\s\S]*?(?=###?\s*\d|$)/i);
|
|
1014
|
+
if (bizSection && features.length === 0) {
|
|
1015
|
+
const lines = bizSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
1016
|
+
for (const line of lines.slice(0, 5)) {
|
|
1017
|
+
const content = line.replace(/^[-*]\s*/, "").trim();
|
|
1018
|
+
if (content.length > 5) {
|
|
1019
|
+
features.push({
|
|
1020
|
+
title: content.slice(0, 20),
|
|
1021
|
+
description: content,
|
|
1022
|
+
hasInput: content.includes("\u8F93\u5165") || content.includes("\u586B\u5199")
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
if (features.length === 0) {
|
|
1028
|
+
const lowerAnalysis = analysis.toLowerCase();
|
|
1029
|
+
if (lowerAnalysis.includes("\u8F93\u5165") || lowerAnalysis.includes("\u8868\u5355")) {
|
|
1030
|
+
features.push({
|
|
1031
|
+
title: "\u8F93\u5165\u8868\u5355",
|
|
1032
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
|
|
1033
|
+
hasInput: true
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
if (lowerAnalysis.includes("\u6309\u94AE") || lowerAnalysis.includes("\u64CD\u4F5C")) {
|
|
1037
|
+
features.push({
|
|
1038
|
+
title: "\u4EA4\u4E92\u6309\u94AE",
|
|
1039
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
1040
|
+
hasInput: false
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
if (lowerAnalysis.includes("\u8868\u683C") || lowerAnalysis.includes("\u5217\u8868")) {
|
|
1044
|
+
features.push({
|
|
1045
|
+
title: "\u6570\u636E\u5217\u8868",
|
|
1046
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
|
|
1047
|
+
hasInput: false
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
if (lowerAnalysis.includes("\u56FE\u8868") || lowerAnalysis.includes("\u53EF\u89C6\u5316")) {
|
|
1051
|
+
features.push({
|
|
1052
|
+
title: "\u56FE\u8868\u5C55\u793A",
|
|
1053
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
|
|
1054
|
+
hasInput: false
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
if (features.length === 0) {
|
|
1059
|
+
features.push({
|
|
1060
|
+
title: "\u53C2\u8003\u529F\u80FD\u5B9E\u73B0",
|
|
1061
|
+
description: `\u57FA\u4E8E\u53C2\u8003\u8D44\u6E90 ${ref.url} \u5B9E\u73B0\u7684\u529F\u80FD`,
|
|
1062
|
+
hasInput: true
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return features;
|
|
1066
|
+
}
|
|
856
1067
|
function extractFeatures(requirement) {
|
|
857
1068
|
const features = [];
|
|
858
1069
|
const urlMatch = requirement.match(/https?:\/\/[^\s]+/);
|
|
@@ -893,15 +1104,26 @@ function extractFeatures(requirement) {
|
|
|
893
1104
|
}
|
|
894
1105
|
return features;
|
|
895
1106
|
}
|
|
896
|
-
function generateSpecItems(requirement, context, bddScenarios, questions) {
|
|
1107
|
+
function generateSpecItems(requirement, context, bddScenarios, questions, references = []) {
|
|
897
1108
|
const items = [];
|
|
898
1109
|
let id = 1;
|
|
1110
|
+
for (const ref of references) {
|
|
1111
|
+
items.push({
|
|
1112
|
+
id: `T${id.toString().padStart(3, "0")}`,
|
|
1113
|
+
title: `\u53C2\u8003\u5206\u6790: ${ref.type}`,
|
|
1114
|
+
description: `\u5206\u6790\u53C2\u8003\u8D44\u6E90 ${ref.url}`,
|
|
1115
|
+
priority: "high",
|
|
1116
|
+
files: [],
|
|
1117
|
+
tests: []
|
|
1118
|
+
});
|
|
1119
|
+
id++;
|
|
1120
|
+
}
|
|
899
1121
|
for (const scenario of bddScenarios) {
|
|
900
1122
|
items.push({
|
|
901
1123
|
id: `T${id.toString().padStart(3, "0")}`,
|
|
902
1124
|
title: scenario.feature,
|
|
903
1125
|
description: scenario.description,
|
|
904
|
-
priority: id <=
|
|
1126
|
+
priority: id <= 3 ? "high" : "medium",
|
|
905
1127
|
files: [],
|
|
906
1128
|
tests: []
|
|
907
1129
|
});
|
|
@@ -944,6 +1166,19 @@ function formatSpecFile(session) {
|
|
|
944
1166
|
lines.push("---");
|
|
945
1167
|
lines.push("");
|
|
946
1168
|
}
|
|
1169
|
+
if (session.referenceResources.length > 0) {
|
|
1170
|
+
lines.push("## \u53C2\u8003\u8D44\u6E90");
|
|
1171
|
+
lines.push("");
|
|
1172
|
+
for (const ref of session.referenceResources) {
|
|
1173
|
+
lines.push(`### ${ref.url}`);
|
|
1174
|
+
lines.push(`> \u7C7B\u578B: ${ref.type}`);
|
|
1175
|
+
lines.push("");
|
|
1176
|
+
lines.push(ref.analysis);
|
|
1177
|
+
lines.push("");
|
|
1178
|
+
}
|
|
1179
|
+
lines.push("---");
|
|
1180
|
+
lines.push("");
|
|
1181
|
+
}
|
|
947
1182
|
if (session.clarificationQuestions.some((q) => q.answered)) {
|
|
948
1183
|
lines.push("## \u9700\u6C42\u6F84\u6E05");
|
|
949
1184
|
lines.push("");
|
|
@@ -982,33 +1217,119 @@ function formatSpecFile(session) {
|
|
|
982
1217
|
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
983
1218
|
return lines.join("\n");
|
|
984
1219
|
}
|
|
985
|
-
async function generateTests(workingDir, session) {
|
|
1220
|
+
async function generateTests(workingDir, session, ctx) {
|
|
986
1221
|
const testDir = path4__namespace.join(workingDir, "tests");
|
|
987
1222
|
await fs4__namespace.mkdir(testDir, { recursive: true });
|
|
988
1223
|
const testFiles = [];
|
|
989
|
-
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1224
|
+
if (ctx?.modelService) {
|
|
1225
|
+
for (const scenario of session.bddScenarios) {
|
|
1226
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1227
|
+
const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
|
|
1228
|
+
const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
1229
|
+
loader.start();
|
|
1230
|
+
try {
|
|
1231
|
+
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
1232
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1233
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1234
|
+
loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
1235
|
+
} catch {
|
|
1236
|
+
const content = generateTestFile(scenario, session);
|
|
1237
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1238
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1239
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
} else {
|
|
1243
|
+
for (const scenario of session.bddScenarios) {
|
|
1244
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1245
|
+
const testPath = path4__namespace.join(testDir, `${testName}.test.ts`);
|
|
1246
|
+
const content = generateTestFile(scenario, session);
|
|
1247
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
1248
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1249
|
+
}
|
|
995
1250
|
}
|
|
996
1251
|
return testFiles;
|
|
997
1252
|
}
|
|
998
|
-
function
|
|
1253
|
+
async function generateTestFileWithAI(scenario, session, ctx) {
|
|
1254
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u6D4B\u8BD5\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EE5\u4E0B BDD \u573A\u666F\u751F\u6210\u5B8C\u6574\u7684 Vitest \u6D4B\u8BD5\u4EE3\u7801\u3002
|
|
1255
|
+
|
|
1256
|
+
## \u529F\u80FD\u540D\u79F0
|
|
1257
|
+
${scenario.feature}
|
|
1258
|
+
|
|
1259
|
+
## BDD \u573A\u666F
|
|
1260
|
+
${scenario.scenarios.map((s) => `
|
|
1261
|
+
### ${s.name}
|
|
1262
|
+
- Given: ${s.given.join(", ")}
|
|
1263
|
+
- When: ${s.when.join(", ")}
|
|
1264
|
+
- Then: ${s.then.join(", ")}
|
|
1265
|
+
`).join("\n")}
|
|
1266
|
+
|
|
1267
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1268
|
+
- \u6280\u672F\u6808: ${session.context?.techStack?.join(", ") || "TypeScript"}
|
|
1269
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1270
|
+
|
|
1271
|
+
## \u8981\u6C42
|
|
1272
|
+
1. \u4F7F\u7528 vitest \u6D4B\u8BD5\u6846\u67B6 (describe, it, expect, beforeEach \u7B49)
|
|
1273
|
+
2. \u6BCF\u4E2A\u573A\u666F\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
1274
|
+
3. \u6D4B\u8BD5\u4EE3\u7801\u8981\u5B8C\u6574\u53EF\u8FD0\u884C\uFF0C\u5305\u542B\u5FC5\u8981\u7684 mock \u548C setup
|
|
1275
|
+
4. \u4F7F\u7528\u4E2D\u6587\u6CE8\u91CA\u8BF4\u660E\u6D4B\u8BD5\u610F\u56FE
|
|
1276
|
+
5. \u6D4B\u8BD5\u8981\u8986\u76D6\u6B63\u5E38\u6D41\u7A0B\u548C\u8FB9\u754C\u60C5\u51B5
|
|
1277
|
+
|
|
1278
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u9700\u8981\u89E3\u91CA\u3002`;
|
|
1279
|
+
const response = await ctx.modelService.sendMessage([
|
|
1280
|
+
{ role: "user", content: prompt2 }
|
|
1281
|
+
], {
|
|
1282
|
+
temperature: 0.3,
|
|
1283
|
+
maxTokens: 4e3
|
|
1284
|
+
});
|
|
1285
|
+
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
1286
|
+
if (codeMatch) {
|
|
1287
|
+
return codeMatch[1].trim();
|
|
1288
|
+
}
|
|
1289
|
+
return response.content;
|
|
1290
|
+
}
|
|
1291
|
+
function generateTestFile(scenario, session) {
|
|
999
1292
|
const lines = [];
|
|
1000
|
-
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
1293
|
+
lines.push(`import { describe, it, expect, beforeEach } from 'vitest';`);
|
|
1001
1294
|
lines.push("");
|
|
1295
|
+
lines.push(`/**`);
|
|
1296
|
+
lines.push(` * ${scenario.feature} \u529F\u80FD\u6D4B\u8BD5`);
|
|
1297
|
+
lines.push(` * `);
|
|
1298
|
+
lines.push(` * BDD \u573A\u666F\u6570\u91CF: ${scenario.scenarios.length}`);
|
|
1299
|
+
if (session?.context?.techStack) {
|
|
1300
|
+
lines.push(` * \u6280\u672F\u6808: ${session.context.techStack.join(", ")}`);
|
|
1301
|
+
}
|
|
1302
|
+
lines.push(` */`);
|
|
1002
1303
|
lines.push(`describe('${scenario.feature}', () => {`);
|
|
1003
1304
|
for (const s of scenario.scenarios) {
|
|
1004
|
-
lines.push(` it('${s.name}', () => {`);
|
|
1005
|
-
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
1006
|
-
lines.push(` // When: ${s.when.join(", ")}`);
|
|
1007
|
-
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
1008
|
-
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
1009
|
-
lines.push(` });`);
|
|
1010
1305
|
lines.push("");
|
|
1306
|
+
lines.push(` /**`);
|
|
1307
|
+
lines.push(` * \u573A\u666F: ${s.name}`);
|
|
1308
|
+
lines.push(` * Given: ${s.given.join(", ")}`);
|
|
1309
|
+
lines.push(` * When: ${s.when.join(", ")}`);
|
|
1310
|
+
lines.push(` * Then: ${s.then.join(", ")}`);
|
|
1311
|
+
lines.push(` */`);
|
|
1312
|
+
lines.push(` it('${s.name}', async () => {`);
|
|
1313
|
+
lines.push(` // Arrange (Given)`);
|
|
1314
|
+
for (const g of s.given) {
|
|
1315
|
+
lines.push(` // ${g}`);
|
|
1316
|
+
}
|
|
1317
|
+
lines.push(` const input = {}; // TODO: \u8BBE\u7F6E\u521D\u59CB\u72B6\u6001`);
|
|
1318
|
+
lines.push("");
|
|
1319
|
+
lines.push(` // Act (When)`);
|
|
1320
|
+
for (const w of s.when) {
|
|
1321
|
+
lines.push(` // ${w}`);
|
|
1322
|
+
}
|
|
1323
|
+
lines.push(` const result = {}; // TODO: \u6267\u884C\u64CD\u4F5C`);
|
|
1324
|
+
lines.push("");
|
|
1325
|
+
lines.push(` // Assert (Then)`);
|
|
1326
|
+
for (const t of s.then) {
|
|
1327
|
+
lines.push(` // ${t}`);
|
|
1328
|
+
}
|
|
1329
|
+
lines.push(` expect(result).toBeDefined(); // TODO: \u5B8C\u5584\u65AD\u8A00`);
|
|
1330
|
+
lines.push(` });`);
|
|
1011
1331
|
}
|
|
1332
|
+
lines.push("");
|
|
1012
1333
|
lines.push(`});`);
|
|
1013
1334
|
return lines.join("\n");
|
|
1014
1335
|
}
|
|
@@ -1049,6 +1370,124 @@ function generateSessionId() {
|
|
|
1049
1370
|
const random = Math.random().toString(36).slice(2, 6);
|
|
1050
1371
|
return `WF-${timestamp}-${random}`.toUpperCase();
|
|
1051
1372
|
}
|
|
1373
|
+
function extractUrls(text) {
|
|
1374
|
+
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]]+/gi;
|
|
1375
|
+
const matches = text.match(urlRegex);
|
|
1376
|
+
return matches ? [...new Set(matches)] : [];
|
|
1377
|
+
}
|
|
1378
|
+
async function fetchAndAnalyzeReference(url, ctx) {
|
|
1379
|
+
const type = detectResourceType(url);
|
|
1380
|
+
let content = "";
|
|
1381
|
+
let analysis = "";
|
|
1382
|
+
try {
|
|
1383
|
+
const response = await fetch(url, {
|
|
1384
|
+
headers: {
|
|
1385
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
if (!response.ok) {
|
|
1389
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1390
|
+
}
|
|
1391
|
+
content = await response.text();
|
|
1392
|
+
if (ctx.modelService.getCurrentModel()) {
|
|
1393
|
+
analysis = await analyzeReferenceContent(url, content, type, ctx);
|
|
1394
|
+
} else {
|
|
1395
|
+
analysis = extractBasicInfo(content, type);
|
|
1396
|
+
}
|
|
1397
|
+
} catch (error) {
|
|
1398
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
|
|
1399
|
+
}
|
|
1400
|
+
return { url, type, content: content.slice(0, 1e4), analysis };
|
|
1401
|
+
}
|
|
1402
|
+
function detectResourceType(url) {
|
|
1403
|
+
if (url.includes("figma.com") || url.includes("lanhuapp.com")) {
|
|
1404
|
+
return "design";
|
|
1405
|
+
}
|
|
1406
|
+
if (/\.(png|jpg|jpeg|gif|webp|svg)$/i.test(url)) {
|
|
1407
|
+
return "image";
|
|
1408
|
+
}
|
|
1409
|
+
if (/api\//i.test(url)) {
|
|
1410
|
+
return "api";
|
|
1411
|
+
}
|
|
1412
|
+
return "webpage";
|
|
1413
|
+
}
|
|
1414
|
+
async function analyzeReferenceContent(url, content, type, ctx) {
|
|
1415
|
+
const prompt2 = `
|
|
1416
|
+
\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4EA7\u54C1\u7ECF\u7406\u548C\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6DF1\u5165\u5206\u6790\u4EE5\u4E0B\u53C2\u8003\u8D44\u6E90\uFF0C\u63D0\u53D6\u4E1A\u52A1\u529F\u80FD\u548C\u6280\u672F\u5B9E\u73B0\u7EC6\u8282\u3002
|
|
1417
|
+
|
|
1418
|
+
## \u53C2\u8003\u8D44\u6E90\u4FE1\u606F
|
|
1419
|
+
- URL: ${url}
|
|
1420
|
+
- \u7C7B\u578B: ${type}
|
|
1421
|
+
|
|
1422
|
+
## \u7F51\u9875\u5185\u5BB9
|
|
1423
|
+
\`\`\`html
|
|
1424
|
+
${content.slice(0, 8e3)}
|
|
1425
|
+
\`\`\`
|
|
1426
|
+
|
|
1427
|
+
## \u5206\u6790\u8981\u6C42
|
|
1428
|
+
|
|
1429
|
+
\u8BF7\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u8FDB\u884C\u8BE6\u7EC6\u5206\u6790\uFF1A
|
|
1430
|
+
|
|
1431
|
+
### 1. \u4E1A\u52A1\u529F\u80FD\u5206\u6790
|
|
1432
|
+
- \u6838\u5FC3\u4E1A\u52A1\u529F\u80FD\u662F\u4EC0\u4E48\uFF1F\uFF08\u8BE6\u7EC6\u63CF\u8FF0\uFF09
|
|
1433
|
+
- \u7528\u6237\u53EF\u4EE5\u505A\u4EC0\u4E48\u64CD\u4F5C\uFF1F
|
|
1434
|
+
- \u4E1A\u52A1\u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F
|
|
1435
|
+
- \u6570\u636E\u8F93\u5165\u8F93\u51FA\u662F\u4EC0\u4E48\uFF1F
|
|
1436
|
+
|
|
1437
|
+
### 2. UI/UX \u7ED3\u6784\u5206\u6790
|
|
1438
|
+
- \u9875\u9762\u5E03\u5C40\u7ED3\u6784
|
|
1439
|
+
- \u4E3B\u8981\u7EC4\u4EF6\u6709\u54EA\u4E9B\uFF1F
|
|
1440
|
+
- \u7EC4\u4EF6\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1441
|
+
- \u4EA4\u4E92\u65B9\u5F0F\uFF08\u70B9\u51FB\u3001\u8F93\u5165\u3001\u62D6\u62FD\u7B49\uFF09
|
|
1442
|
+
|
|
1443
|
+
### 3. \u6570\u636E\u6A21\u578B\u5206\u6790
|
|
1444
|
+
- \u9700\u8981\u54EA\u4E9B\u6570\u636E\uFF1F
|
|
1445
|
+
- \u6570\u636E\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1446
|
+
- \u6570\u636E\u6765\u6E90\uFF08\u7528\u6237\u8F93\u5165/\u8BA1\u7B97/API\uFF09
|
|
1447
|
+
|
|
1448
|
+
### 4. \u529F\u80FD\u62C6\u5206\u5EFA\u8BAE
|
|
1449
|
+
\u8BF7\u5C06\u529F\u80FD\u62C6\u5206\u4E3A\u53EF\u72EC\u7ACB\u5F00\u53D1\u7684\u4EFB\u52A1\uFF0C\u6BCF\u4E2A\u4EFB\u52A1\u5305\u542B\uFF1A
|
|
1450
|
+
- \u4EFB\u52A1\u540D\u79F0
|
|
1451
|
+
- \u4EFB\u52A1\u63CF\u8FF0
|
|
1452
|
+
- \u6280\u672F\u8981\u70B9
|
|
1453
|
+
- \u4F9D\u8D56\u5173\u7CFB
|
|
1454
|
+
|
|
1455
|
+
### 5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
|
|
1456
|
+
- \u63A8\u8350\u7684\u6280\u672F\u65B9\u6848
|
|
1457
|
+
- \u9700\u8981\u6CE8\u610F\u7684\u6280\u672F\u96BE\u70B9
|
|
1458
|
+
- \u6027\u80FD\u4F18\u5316\u5EFA\u8BAE
|
|
1459
|
+
|
|
1460
|
+
\u8BF7\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\uFF0C\u91CD\u70B9\u7A81\u51FA\u4E1A\u52A1\u903B\u8F91\u548C\u529F\u80FD\u5B9E\u73B0\u7EC6\u8282\u3002
|
|
1461
|
+
`;
|
|
1462
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
1463
|
+
loader.start();
|
|
1464
|
+
try {
|
|
1465
|
+
const response = await ctx.modelService.sendMessage([
|
|
1466
|
+
{ role: "user", content: prompt2 }
|
|
1467
|
+
], {
|
|
1468
|
+
temperature: 0.3,
|
|
1469
|
+
maxTokens: 4e3
|
|
1470
|
+
});
|
|
1471
|
+
loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
1472
|
+
return response.content;
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
loader.stop();
|
|
1475
|
+
throw error;
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
function extractBasicInfo(content, type) {
|
|
1479
|
+
const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
1480
|
+
const descMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i);
|
|
1481
|
+
const parts = [];
|
|
1482
|
+
if (titleMatch) {
|
|
1483
|
+
parts.push(`\u6807\u9898: ${titleMatch[1]}`);
|
|
1484
|
+
}
|
|
1485
|
+
if (descMatch) {
|
|
1486
|
+
parts.push(`\u63CF\u8FF0: ${descMatch[1]}`);
|
|
1487
|
+
}
|
|
1488
|
+
parts.push(`\u8D44\u6E90\u7C7B\u578B: ${type}`);
|
|
1489
|
+
return parts.join("\n");
|
|
1490
|
+
}
|
|
1052
1491
|
function generateComplexityBar(score) {
|
|
1053
1492
|
const filled = Math.round(score / 2);
|
|
1054
1493
|
const empty = 5 - filled;
|
|
@@ -1058,6 +1497,7 @@ function getPhaseLabel(phase) {
|
|
|
1058
1497
|
const labels = {
|
|
1059
1498
|
context: "\u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6",
|
|
1060
1499
|
clarify: "\u9700\u6C42\u6F84\u6E05",
|
|
1500
|
+
reference: "\u53C2\u8003\u8D44\u6E90\u5206\u6790",
|
|
1061
1501
|
analysis: "\u590D\u6742\u5EA6\u8BC4\u4F30",
|
|
1062
1502
|
bdd: "BDD \u573A\u666F\u62C6\u89E3",
|
|
1063
1503
|
spec: "OpenSpec \u89C4\u683C",
|
|
@@ -1073,10 +1513,43 @@ function getActiveSession() {
|
|
|
1073
1513
|
function clearActiveSession() {
|
|
1074
1514
|
activeSession = null;
|
|
1075
1515
|
}
|
|
1076
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1516
|
+
var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1077
1517
|
var init_new = __esm({
|
|
1078
1518
|
"src/commands/new.ts"() {
|
|
1079
1519
|
init_cjs_shims();
|
|
1520
|
+
LoadingIndicator = class {
|
|
1521
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1522
|
+
frameIndex = 0;
|
|
1523
|
+
interval = null;
|
|
1524
|
+
message;
|
|
1525
|
+
constructor(message) {
|
|
1526
|
+
this.message = message;
|
|
1527
|
+
}
|
|
1528
|
+
start() {
|
|
1529
|
+
process.stdout.write("\x1B[?25l");
|
|
1530
|
+
this.interval = setInterval(() => {
|
|
1531
|
+
const frame = this.frames[this.frameIndex];
|
|
1532
|
+
process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
|
|
1533
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1534
|
+
}, 80);
|
|
1535
|
+
}
|
|
1536
|
+
update(message) {
|
|
1537
|
+
this.message = message;
|
|
1538
|
+
}
|
|
1539
|
+
stop(finalMessage) {
|
|
1540
|
+
if (this.interval) {
|
|
1541
|
+
clearInterval(this.interval);
|
|
1542
|
+
this.interval = null;
|
|
1543
|
+
}
|
|
1544
|
+
process.stdout.write("\x1B[?25h");
|
|
1545
|
+
if (finalMessage) {
|
|
1546
|
+
process.stdout.write(`\r${finalMessage}
|
|
1547
|
+
`);
|
|
1548
|
+
} else {
|
|
1549
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1080
1553
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
1081
1554
|
COMPLEXITY_THRESHOLD = 6;
|
|
1082
1555
|
CLARITY_THRESHOLD = 0.6;
|