@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.mjs
CHANGED
|
@@ -59,6 +59,7 @@ async function handleNew(args, ctx) {
|
|
|
59
59
|
context: null,
|
|
60
60
|
clarityScore: 0,
|
|
61
61
|
clarificationQuestions: [],
|
|
62
|
+
referenceResources: [],
|
|
62
63
|
complexity: 0,
|
|
63
64
|
bddScenarios: [],
|
|
64
65
|
specItems: [],
|
|
@@ -101,7 +102,7 @@ async function executeWorkflow(ctx) {
|
|
|
101
102
|
const lines = [];
|
|
102
103
|
try {
|
|
103
104
|
if (activeSession.phase === "context") {
|
|
104
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/
|
|
105
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
|
|
105
106
|
lines.push("");
|
|
106
107
|
activeSession.context = await readProjectContext(ctx.options.workingDirectory);
|
|
107
108
|
lines.push(chalk9.gray(` \u9879\u76EE: ${activeSession.context.name}`));
|
|
@@ -115,7 +116,7 @@ async function executeWorkflow(ctx) {
|
|
|
115
116
|
}
|
|
116
117
|
if (activeSession.phase === "clarify") {
|
|
117
118
|
lines.push("");
|
|
118
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/
|
|
119
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 2/9: \u9700\u6C42\u6F84\u6E05 \u2501\u2501\u2501"));
|
|
119
120
|
lines.push("");
|
|
120
121
|
const clarityResult = analyzeRequirementClarity(
|
|
121
122
|
activeSession.requirement,
|
|
@@ -141,11 +142,40 @@ async function executeWorkflow(ctx) {
|
|
|
141
142
|
return { output: lines.join("\n") };
|
|
142
143
|
}
|
|
143
144
|
lines.push(chalk9.green(" \u2713 \u9700\u6C42\u6E05\u6670\uFF0C\u7EE7\u7EED\u4E0B\u4E00\u6B65"));
|
|
145
|
+
activeSession.phase = "reference";
|
|
146
|
+
}
|
|
147
|
+
if (activeSession.phase === "reference") {
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 3/9: \u53C2\u8003\u8D44\u6E90\u5206\u6790 \u2501\u2501\u2501"));
|
|
150
|
+
lines.push("");
|
|
151
|
+
const urls = extractUrls(activeSession.refinedRequirement);
|
|
152
|
+
if (urls.length > 0) {
|
|
153
|
+
lines.push(chalk9.gray(` \u53D1\u73B0 ${urls.length} \u4E2A\u53C2\u8003\u94FE\u63A5`));
|
|
154
|
+
lines.push("");
|
|
155
|
+
for (const url of urls) {
|
|
156
|
+
lines.push(chalk9.gray(` \u{1F4CE} ${url}`));
|
|
157
|
+
try {
|
|
158
|
+
const resource = await fetchAndAnalyzeReference(url, ctx);
|
|
159
|
+
activeSession.referenceResources.push(resource);
|
|
160
|
+
lines.push(chalk9.green(` \u2713 \u5DF2\u5206\u6790`));
|
|
161
|
+
activeSession.refinedRequirement += `
|
|
162
|
+
|
|
163
|
+
\u3010\u53C2\u8003\u8D44\u6E90\u5206\u6790 - ${url}\u3011
|
|
164
|
+
${resource.analysis}`;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
lines.push(chalk9.yellow(` \u26A0 \u83B7\u53D6\u5931\u8D25: ${error.message}`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
lines.push("");
|
|
170
|
+
lines.push(chalk9.green(" \u2713 \u53C2\u8003\u8D44\u6E90\u5206\u6790\u5B8C\u6210"));
|
|
171
|
+
} else {
|
|
172
|
+
lines.push(chalk9.gray(" \u65E0\u5916\u90E8\u53C2\u8003\u94FE\u63A5"));
|
|
173
|
+
}
|
|
144
174
|
activeSession.phase = "analysis";
|
|
145
175
|
}
|
|
146
176
|
if (activeSession.phase === "analysis") {
|
|
147
177
|
lines.push("");
|
|
148
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
178
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 4/9: \u590D\u6742\u5EA6\u8BC4\u4F30 \u2501\u2501\u2501"));
|
|
149
179
|
lines.push("");
|
|
150
180
|
activeSession.complexity = analyzeComplexity(
|
|
151
181
|
activeSession.refinedRequirement,
|
|
@@ -162,13 +192,28 @@ async function executeWorkflow(ctx) {
|
|
|
162
192
|
}
|
|
163
193
|
if (activeSession.phase === "bdd") {
|
|
164
194
|
lines.push("");
|
|
165
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
195
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
166
196
|
lines.push("");
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
activeSession.
|
|
171
|
-
|
|
197
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
198
|
+
loader.start();
|
|
199
|
+
try {
|
|
200
|
+
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
201
|
+
activeSession.refinedRequirement,
|
|
202
|
+
activeSession.context,
|
|
203
|
+
activeSession.clarificationQuestions,
|
|
204
|
+
activeSession.referenceResources,
|
|
205
|
+
ctx
|
|
206
|
+
);
|
|
207
|
+
loader.stop(chalk9.green(" \u2713 BDD \u573A\u666F\u5DF2\u751F\u6210"));
|
|
208
|
+
} catch {
|
|
209
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840 BDD \u751F\u6210"));
|
|
210
|
+
activeSession.bddScenarios = generateBDDScenarios(
|
|
211
|
+
activeSession.refinedRequirement,
|
|
212
|
+
activeSession.context,
|
|
213
|
+
activeSession.clarificationQuestions,
|
|
214
|
+
activeSession.referenceResources
|
|
215
|
+
);
|
|
216
|
+
}
|
|
172
217
|
for (const scenario of activeSession.bddScenarios) {
|
|
173
218
|
lines.push(chalk9.white(` Feature: ${scenario.feature}`));
|
|
174
219
|
for (const s of scenario.scenarios.slice(0, 3)) {
|
|
@@ -182,13 +227,14 @@ async function executeWorkflow(ctx) {
|
|
|
182
227
|
}
|
|
183
228
|
if (activeSession.phase === "spec") {
|
|
184
229
|
lines.push("");
|
|
185
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
230
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
186
231
|
lines.push("");
|
|
187
232
|
activeSession.specItems = generateSpecItems(
|
|
188
233
|
activeSession.refinedRequirement,
|
|
189
234
|
activeSession.context,
|
|
190
235
|
activeSession.bddScenarios,
|
|
191
|
-
activeSession.clarificationQuestions
|
|
236
|
+
activeSession.clarificationQuestions,
|
|
237
|
+
activeSession.referenceResources
|
|
192
238
|
);
|
|
193
239
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
194
240
|
lines.push(chalk9.green(" \u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
@@ -212,9 +258,9 @@ async function executeWorkflow(ctx) {
|
|
|
212
258
|
}
|
|
213
259
|
if (activeSession.phase === "tdd") {
|
|
214
260
|
lines.push("");
|
|
215
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
261
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/9: TDD \u6D4B\u8BD5\u751F\u6210 \u2501\u2501\u2501"));
|
|
216
262
|
lines.push("");
|
|
217
|
-
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession);
|
|
263
|
+
activeSession.testFiles = await generateTests(ctx.options.workingDirectory, activeSession, ctx);
|
|
218
264
|
lines.push(chalk9.green(" \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210"));
|
|
219
265
|
for (const file of activeSession.testFiles) {
|
|
220
266
|
lines.push(chalk9.gray(` - ${file}`));
|
|
@@ -223,7 +269,7 @@ async function executeWorkflow(ctx) {
|
|
|
223
269
|
}
|
|
224
270
|
if (activeSession.phase === "develop") {
|
|
225
271
|
lines.push("");
|
|
226
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
272
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/9: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
|
|
227
273
|
lines.push("");
|
|
228
274
|
lines.push(chalk9.yellow(" \u{1F680} \u6B63\u5728\u8C03\u7528 AI \u751F\u6210\u4EE3\u7801..."));
|
|
229
275
|
try {
|
|
@@ -248,7 +294,7 @@ async function executeWorkflow(ctx) {
|
|
|
248
294
|
}
|
|
249
295
|
if (activeSession.phase === "review") {
|
|
250
296
|
lines.push("");
|
|
251
|
-
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5
|
|
297
|
+
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 9/9: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
|
|
252
298
|
lines.push("");
|
|
253
299
|
lines.push(chalk9.yellow(" \u{1F50D} \u6B63\u5728\u8FDB\u884C\u4EE3\u7801\u5BA1\u6838..."));
|
|
254
300
|
try {
|
|
@@ -511,6 +557,8 @@ function getCategoryLabel(category) {
|
|
|
511
557
|
async function executeDevelopment(ctx, session) {
|
|
512
558
|
const workingDir = ctx.options.workingDirectory;
|
|
513
559
|
const files = [];
|
|
560
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u751F\u6210\u4EE3\u7801");
|
|
561
|
+
loader.start();
|
|
514
562
|
try {
|
|
515
563
|
const systemPrompt = buildDevelopmentPrompt(session);
|
|
516
564
|
const messages = [
|
|
@@ -538,11 +586,15 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
538
586
|
content: systemPrompt
|
|
539
587
|
}
|
|
540
588
|
];
|
|
589
|
+
loader.update("\u6B63\u5728\u8C03\u7528 AI \u6A21\u578B");
|
|
541
590
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
542
591
|
temperature: 0.3,
|
|
543
592
|
maxTokens: 8e3,
|
|
544
|
-
agent: "frontend-dev"
|
|
593
|
+
agent: "frontend-dev",
|
|
594
|
+
timeout: 18e4
|
|
595
|
+
// 3 分钟超时
|
|
545
596
|
});
|
|
597
|
+
loader.update("\u6B63\u5728\u89E3\u6790\u4EE3\u7801");
|
|
546
598
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
547
599
|
for (const block of codeBlocks) {
|
|
548
600
|
const filePath = path5.join(workingDir, block.filename);
|
|
@@ -571,8 +623,10 @@ export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
|
571
623
|
await fs4.writeFile(filePath, stubCode, "utf-8");
|
|
572
624
|
files.push(`src/features/${fileName}`);
|
|
573
625
|
}
|
|
626
|
+
loader.stop(chalk9.green(` \u2713 \u5DF2\u751F\u6210 ${files.length} \u4E2A\u6587\u4EF6`));
|
|
574
627
|
return { success: true, files };
|
|
575
628
|
} catch (error) {
|
|
629
|
+
loader.stop();
|
|
576
630
|
return {
|
|
577
631
|
success: false,
|
|
578
632
|
files: [],
|
|
@@ -790,13 +844,41 @@ function analyzeComplexity(requirement, context) {
|
|
|
790
844
|
if (!context.framework) score += 0.5;
|
|
791
845
|
return Math.max(1, Math.min(10, Math.round(score)));
|
|
792
846
|
}
|
|
793
|
-
function generateBDDScenarios(requirement, context, questions) {
|
|
847
|
+
function generateBDDScenarios(requirement, context, questions, references = []) {
|
|
794
848
|
const scenarios = [];
|
|
795
849
|
questions.find((q) => q.category === "ui" && q.answered)?.answer;
|
|
796
850
|
const interactionAnswer = questions.find((q) => q.category === "interaction" && q.answered)?.answer;
|
|
797
851
|
const edgeAnswer = questions.find((q) => q.category === "edge" && q.answered)?.answer;
|
|
852
|
+
if (references.length > 0) {
|
|
853
|
+
for (const ref of references) {
|
|
854
|
+
const refFeatures = extractFeaturesFromReference(ref);
|
|
855
|
+
for (const feature of refFeatures) {
|
|
856
|
+
const scenario = {
|
|
857
|
+
feature: feature.title,
|
|
858
|
+
description: feature.description,
|
|
859
|
+
scenarios: []
|
|
860
|
+
};
|
|
861
|
+
scenario.scenarios.push({
|
|
862
|
+
name: `\u6B63\u5E38\u6D41\u7A0B: ${feature.title}`,
|
|
863
|
+
given: [`\u7528\u6237\u8FDB\u5165\u76F8\u5173\u9875\u9762`],
|
|
864
|
+
when: [`\u7528\u6237\u6267\u884C "${feature.title}" \u64CD\u4F5C`],
|
|
865
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u5E76\u8FD4\u56DE\u9884\u671F\u7ED3\u679C`]
|
|
866
|
+
});
|
|
867
|
+
if (feature.hasInput) {
|
|
868
|
+
scenario.scenarios.push({
|
|
869
|
+
name: `\u8FB9\u754C\u60C5\u51B5: \u8F93\u5165\u9A8C\u8BC1`,
|
|
870
|
+
given: [`\u7528\u6237\u8FDB\u5165\u8F93\u5165\u754C\u9762`],
|
|
871
|
+
when: [`\u7528\u6237\u8F93\u5165\u8FB9\u754C\u503C\u6216\u7A7A\u503C`],
|
|
872
|
+
then: [`\u7CFB\u7EDF\u5E94\u6B63\u786E\u5904\u7406\u8FB9\u754C\u60C5\u51B5`]
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
scenarios.push(scenario);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
798
879
|
const features = extractFeatures(requirement);
|
|
799
880
|
for (const feature of features) {
|
|
881
|
+
if (scenarios.some((s) => s.feature === feature.title)) continue;
|
|
800
882
|
const scenario = {
|
|
801
883
|
feature: feature.title,
|
|
802
884
|
description: feature.description,
|
|
@@ -828,6 +910,135 @@ function generateBDDScenarios(requirement, context, questions) {
|
|
|
828
910
|
}
|
|
829
911
|
return scenarios;
|
|
830
912
|
}
|
|
913
|
+
async function generateBDDScenariosWithAI(requirement, context, questions, references, ctx) {
|
|
914
|
+
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
|
|
915
|
+
|
|
916
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
917
|
+
${requirement}
|
|
918
|
+
|
|
919
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
920
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "\u672A\u6307\u5B9A"}
|
|
921
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
922
|
+
|
|
923
|
+
## \u6F84\u6E05\u95EE\u7B54
|
|
924
|
+
${questions.filter((q) => q.answered).map((q) => `- Q: ${q.question}
|
|
925
|
+
A: ${q.answer}`).join("\n")}
|
|
926
|
+
|
|
927
|
+
## \u53C2\u8003\u8D44\u6E90\u5206\u6790
|
|
928
|
+
${references.map((r) => `### ${r.url}
|
|
929
|
+
${r.analysis}`).join("\n\n")}
|
|
930
|
+
|
|
931
|
+
## \u8981\u6C42
|
|
932
|
+
1. \u6BCF\u4E2A\u529F\u80FD\u6A21\u5757\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684 Feature
|
|
933
|
+
2. \u6BCF\u4E2A Feature \u5305\u542B\u591A\u4E2A\u5177\u4F53\u7684 Scenario
|
|
934
|
+
3. \u4F7F\u7528 Given-When-Then \u683C\u5F0F
|
|
935
|
+
4. \u573A\u666F\u8981\u8986\u76D6: \u6B63\u5E38\u6D41\u7A0B\u3001\u8FB9\u754C\u60C5\u51B5\u3001\u5F02\u5E38\u5904\u7406
|
|
936
|
+
5. \u573A\u666F\u8981\u5177\u4F53\u53EF\u6D4B\u8BD5\uFF0C\u4E0D\u8981\u6CDB\u6CDB\u800C\u8C08
|
|
937
|
+
|
|
938
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
939
|
+
\`\`\`json
|
|
940
|
+
[
|
|
941
|
+
{
|
|
942
|
+
"feature": "\u529F\u80FD\u540D\u79F0",
|
|
943
|
+
"description": "\u529F\u80FD\u63CF\u8FF0",
|
|
944
|
+
"scenarios": [
|
|
945
|
+
{
|
|
946
|
+
"name": "\u573A\u666F\u540D\u79F0",
|
|
947
|
+
"given": ["\u524D\u7F6E\u6761\u4EF61", "\u524D\u7F6E\u6761\u4EF62"],
|
|
948
|
+
"when": ["\u64CD\u4F5C1", "\u64CD\u4F5C2"],
|
|
949
|
+
"then": ["\u9884\u671F\u7ED3\u679C1", "\u9884\u671F\u7ED3\u679C2"]
|
|
950
|
+
}
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
]
|
|
954
|
+
\`\`\`
|
|
955
|
+
|
|
956
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u6709\u5176\u4ED6\u5185\u5BB9\u3002`;
|
|
957
|
+
const response = await ctx.modelService.sendMessage([
|
|
958
|
+
{ role: "user", content: prompt2 }
|
|
959
|
+
], {
|
|
960
|
+
temperature: 0.3,
|
|
961
|
+
maxTokens: 4e3,
|
|
962
|
+
timeout: 12e4
|
|
963
|
+
});
|
|
964
|
+
try {
|
|
965
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
966
|
+
if (jsonMatch) {
|
|
967
|
+
return JSON.parse(jsonMatch[1].trim());
|
|
968
|
+
}
|
|
969
|
+
return JSON.parse(response.content);
|
|
970
|
+
} catch {
|
|
971
|
+
return generateBDDScenarios(requirement, context, questions, references);
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
function extractFeaturesFromReference(ref) {
|
|
975
|
+
const features = [];
|
|
976
|
+
const analysis = ref.analysis;
|
|
977
|
+
const featureSection = analysis.match(/###?\s*4\.\s*功能拆分建议[\s\S]*?(?=###?\s*\d|$)/i);
|
|
978
|
+
if (featureSection) {
|
|
979
|
+
const taskMatches = featureSection[0].matchAll(/[-*]\s*\*\*([^*]+)\*\*[::]?\s*([^\n]+)/g);
|
|
980
|
+
for (const match of taskMatches) {
|
|
981
|
+
features.push({
|
|
982
|
+
title: match[1].trim(),
|
|
983
|
+
description: match[2].trim(),
|
|
984
|
+
hasInput: match[2].includes("\u8F93\u5165") || match[2].includes("\u8868\u5355") || match[2].includes("\u7528\u6237")
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
const bizSection = analysis.match(/###?\s*1\.\s*业务功能分析[\s\S]*?(?=###?\s*\d|$)/i);
|
|
989
|
+
if (bizSection && features.length === 0) {
|
|
990
|
+
const lines = bizSection[0].split("\n").filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*"));
|
|
991
|
+
for (const line of lines.slice(0, 5)) {
|
|
992
|
+
const content = line.replace(/^[-*]\s*/, "").trim();
|
|
993
|
+
if (content.length > 5) {
|
|
994
|
+
features.push({
|
|
995
|
+
title: content.slice(0, 20),
|
|
996
|
+
description: content,
|
|
997
|
+
hasInput: content.includes("\u8F93\u5165") || content.includes("\u586B\u5199")
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (features.length === 0) {
|
|
1003
|
+
const lowerAnalysis = analysis.toLowerCase();
|
|
1004
|
+
if (lowerAnalysis.includes("\u8F93\u5165") || lowerAnalysis.includes("\u8868\u5355")) {
|
|
1005
|
+
features.push({
|
|
1006
|
+
title: "\u8F93\u5165\u8868\u5355",
|
|
1007
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u8F93\u5165\u8868\u5355\u529F\u80FD",
|
|
1008
|
+
hasInput: true
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
if (lowerAnalysis.includes("\u6309\u94AE") || lowerAnalysis.includes("\u64CD\u4F5C")) {
|
|
1012
|
+
features.push({
|
|
1013
|
+
title: "\u4EA4\u4E92\u6309\u94AE",
|
|
1014
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6309\u94AE\u4EA4\u4E92",
|
|
1015
|
+
hasInput: false
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
if (lowerAnalysis.includes("\u8868\u683C") || lowerAnalysis.includes("\u5217\u8868")) {
|
|
1019
|
+
features.push({
|
|
1020
|
+
title: "\u6570\u636E\u5217\u8868",
|
|
1021
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u6570\u636E\u5C55\u793A",
|
|
1022
|
+
hasInput: false
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
if (lowerAnalysis.includes("\u56FE\u8868") || lowerAnalysis.includes("\u53EF\u89C6\u5316")) {
|
|
1026
|
+
features.push({
|
|
1027
|
+
title: "\u56FE\u8868\u5C55\u793A",
|
|
1028
|
+
description: "\u53C2\u8003\u754C\u9762\u4E2D\u7684\u56FE\u8868\u53EF\u89C6\u5316",
|
|
1029
|
+
hasInput: false
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
if (features.length === 0) {
|
|
1034
|
+
features.push({
|
|
1035
|
+
title: "\u53C2\u8003\u529F\u80FD\u5B9E\u73B0",
|
|
1036
|
+
description: `\u57FA\u4E8E\u53C2\u8003\u8D44\u6E90 ${ref.url} \u5B9E\u73B0\u7684\u529F\u80FD`,
|
|
1037
|
+
hasInput: true
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
return features;
|
|
1041
|
+
}
|
|
831
1042
|
function extractFeatures(requirement) {
|
|
832
1043
|
const features = [];
|
|
833
1044
|
const urlMatch = requirement.match(/https?:\/\/[^\s]+/);
|
|
@@ -868,15 +1079,26 @@ function extractFeatures(requirement) {
|
|
|
868
1079
|
}
|
|
869
1080
|
return features;
|
|
870
1081
|
}
|
|
871
|
-
function generateSpecItems(requirement, context, bddScenarios, questions) {
|
|
1082
|
+
function generateSpecItems(requirement, context, bddScenarios, questions, references = []) {
|
|
872
1083
|
const items = [];
|
|
873
1084
|
let id = 1;
|
|
1085
|
+
for (const ref of references) {
|
|
1086
|
+
items.push({
|
|
1087
|
+
id: `T${id.toString().padStart(3, "0")}`,
|
|
1088
|
+
title: `\u53C2\u8003\u5206\u6790: ${ref.type}`,
|
|
1089
|
+
description: `\u5206\u6790\u53C2\u8003\u8D44\u6E90 ${ref.url}`,
|
|
1090
|
+
priority: "high",
|
|
1091
|
+
files: [],
|
|
1092
|
+
tests: []
|
|
1093
|
+
});
|
|
1094
|
+
id++;
|
|
1095
|
+
}
|
|
874
1096
|
for (const scenario of bddScenarios) {
|
|
875
1097
|
items.push({
|
|
876
1098
|
id: `T${id.toString().padStart(3, "0")}`,
|
|
877
1099
|
title: scenario.feature,
|
|
878
1100
|
description: scenario.description,
|
|
879
|
-
priority: id <=
|
|
1101
|
+
priority: id <= 3 ? "high" : "medium",
|
|
880
1102
|
files: [],
|
|
881
1103
|
tests: []
|
|
882
1104
|
});
|
|
@@ -919,6 +1141,19 @@ function formatSpecFile(session) {
|
|
|
919
1141
|
lines.push("---");
|
|
920
1142
|
lines.push("");
|
|
921
1143
|
}
|
|
1144
|
+
if (session.referenceResources.length > 0) {
|
|
1145
|
+
lines.push("## \u53C2\u8003\u8D44\u6E90");
|
|
1146
|
+
lines.push("");
|
|
1147
|
+
for (const ref of session.referenceResources) {
|
|
1148
|
+
lines.push(`### ${ref.url}`);
|
|
1149
|
+
lines.push(`> \u7C7B\u578B: ${ref.type}`);
|
|
1150
|
+
lines.push("");
|
|
1151
|
+
lines.push(ref.analysis);
|
|
1152
|
+
lines.push("");
|
|
1153
|
+
}
|
|
1154
|
+
lines.push("---");
|
|
1155
|
+
lines.push("");
|
|
1156
|
+
}
|
|
922
1157
|
if (session.clarificationQuestions.some((q) => q.answered)) {
|
|
923
1158
|
lines.push("## \u9700\u6C42\u6F84\u6E05");
|
|
924
1159
|
lines.push("");
|
|
@@ -957,33 +1192,119 @@ function formatSpecFile(session) {
|
|
|
957
1192
|
lines.push("**\u786E\u8BA4\u72B6\u6001**: \u23F3 \u7B49\u5F85\u786E\u8BA4");
|
|
958
1193
|
return lines.join("\n");
|
|
959
1194
|
}
|
|
960
|
-
async function generateTests(workingDir, session) {
|
|
1195
|
+
async function generateTests(workingDir, session, ctx) {
|
|
961
1196
|
const testDir = path5.join(workingDir, "tests");
|
|
962
1197
|
await fs4.mkdir(testDir, { recursive: true });
|
|
963
1198
|
const testFiles = [];
|
|
964
|
-
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1199
|
+
if (ctx?.modelService) {
|
|
1200
|
+
for (const scenario of session.bddScenarios) {
|
|
1201
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1202
|
+
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1203
|
+
const loader = new LoadingIndicator(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
1204
|
+
loader.start();
|
|
1205
|
+
try {
|
|
1206
|
+
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
1207
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1208
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1209
|
+
loader.stop(chalk9.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
1210
|
+
} catch {
|
|
1211
|
+
const content = generateTestFile(scenario, session);
|
|
1212
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1213
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1214
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
} else {
|
|
1218
|
+
for (const scenario of session.bddScenarios) {
|
|
1219
|
+
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1220
|
+
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1221
|
+
const content = generateTestFile(scenario, session);
|
|
1222
|
+
await fs4.writeFile(testPath, content, "utf-8");
|
|
1223
|
+
testFiles.push(`tests/${testName}.test.ts`);
|
|
1224
|
+
}
|
|
970
1225
|
}
|
|
971
1226
|
return testFiles;
|
|
972
1227
|
}
|
|
973
|
-
function
|
|
1228
|
+
async function generateTestFileWithAI(scenario, session, ctx) {
|
|
1229
|
+
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
|
|
1230
|
+
|
|
1231
|
+
## \u529F\u80FD\u540D\u79F0
|
|
1232
|
+
${scenario.feature}
|
|
1233
|
+
|
|
1234
|
+
## BDD \u573A\u666F
|
|
1235
|
+
${scenario.scenarios.map((s) => `
|
|
1236
|
+
### ${s.name}
|
|
1237
|
+
- Given: ${s.given.join(", ")}
|
|
1238
|
+
- When: ${s.when.join(", ")}
|
|
1239
|
+
- Then: ${s.then.join(", ")}
|
|
1240
|
+
`).join("\n")}
|
|
1241
|
+
|
|
1242
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1243
|
+
- \u6280\u672F\u6808: ${session.context?.techStack?.join(", ") || "TypeScript"}
|
|
1244
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1245
|
+
|
|
1246
|
+
## \u8981\u6C42
|
|
1247
|
+
1. \u4F7F\u7528 vitest \u6D4B\u8BD5\u6846\u67B6 (describe, it, expect, beforeEach \u7B49)
|
|
1248
|
+
2. \u6BCF\u4E2A\u573A\u666F\u751F\u6210\u4E00\u4E2A\u72EC\u7ACB\u7684\u6D4B\u8BD5\u7528\u4F8B
|
|
1249
|
+
3. \u6D4B\u8BD5\u4EE3\u7801\u8981\u5B8C\u6574\u53EF\u8FD0\u884C\uFF0C\u5305\u542B\u5FC5\u8981\u7684 mock \u548C setup
|
|
1250
|
+
4. \u4F7F\u7528\u4E2D\u6587\u6CE8\u91CA\u8BF4\u660E\u6D4B\u8BD5\u610F\u56FE
|
|
1251
|
+
5. \u6D4B\u8BD5\u8981\u8986\u76D6\u6B63\u5E38\u6D41\u7A0B\u548C\u8FB9\u754C\u60C5\u51B5
|
|
1252
|
+
|
|
1253
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA\u6D4B\u8BD5\u4EE3\u7801\uFF0C\u4E0D\u9700\u8981\u89E3\u91CA\u3002`;
|
|
1254
|
+
const response = await ctx.modelService.sendMessage([
|
|
1255
|
+
{ role: "user", content: prompt2 }
|
|
1256
|
+
], {
|
|
1257
|
+
temperature: 0.3,
|
|
1258
|
+
maxTokens: 4e3
|
|
1259
|
+
});
|
|
1260
|
+
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
1261
|
+
if (codeMatch) {
|
|
1262
|
+
return codeMatch[1].trim();
|
|
1263
|
+
}
|
|
1264
|
+
return response.content;
|
|
1265
|
+
}
|
|
1266
|
+
function generateTestFile(scenario, session) {
|
|
974
1267
|
const lines = [];
|
|
975
|
-
lines.push(`import { describe, it, expect } from 'vitest';`);
|
|
1268
|
+
lines.push(`import { describe, it, expect, beforeEach } from 'vitest';`);
|
|
976
1269
|
lines.push("");
|
|
1270
|
+
lines.push(`/**`);
|
|
1271
|
+
lines.push(` * ${scenario.feature} \u529F\u80FD\u6D4B\u8BD5`);
|
|
1272
|
+
lines.push(` * `);
|
|
1273
|
+
lines.push(` * BDD \u573A\u666F\u6570\u91CF: ${scenario.scenarios.length}`);
|
|
1274
|
+
if (session?.context?.techStack) {
|
|
1275
|
+
lines.push(` * \u6280\u672F\u6808: ${session.context.techStack.join(", ")}`);
|
|
1276
|
+
}
|
|
1277
|
+
lines.push(` */`);
|
|
977
1278
|
lines.push(`describe('${scenario.feature}', () => {`);
|
|
978
1279
|
for (const s of scenario.scenarios) {
|
|
979
|
-
lines.push(` it('${s.name}', () => {`);
|
|
980
|
-
lines.push(` // Given: ${s.given.join(", ")}`);
|
|
981
|
-
lines.push(` // When: ${s.when.join(", ")}`);
|
|
982
|
-
lines.push(` // Then: ${s.then.join(", ")}`);
|
|
983
|
-
lines.push(` expect(true).toBe(true); // TODO: \u5B9E\u73B0\u6D4B\u8BD5`);
|
|
984
|
-
lines.push(` });`);
|
|
985
1280
|
lines.push("");
|
|
1281
|
+
lines.push(` /**`);
|
|
1282
|
+
lines.push(` * \u573A\u666F: ${s.name}`);
|
|
1283
|
+
lines.push(` * Given: ${s.given.join(", ")}`);
|
|
1284
|
+
lines.push(` * When: ${s.when.join(", ")}`);
|
|
1285
|
+
lines.push(` * Then: ${s.then.join(", ")}`);
|
|
1286
|
+
lines.push(` */`);
|
|
1287
|
+
lines.push(` it('${s.name}', async () => {`);
|
|
1288
|
+
lines.push(` // Arrange (Given)`);
|
|
1289
|
+
for (const g of s.given) {
|
|
1290
|
+
lines.push(` // ${g}`);
|
|
1291
|
+
}
|
|
1292
|
+
lines.push(` const input = {}; // TODO: \u8BBE\u7F6E\u521D\u59CB\u72B6\u6001`);
|
|
1293
|
+
lines.push("");
|
|
1294
|
+
lines.push(` // Act (When)`);
|
|
1295
|
+
for (const w of s.when) {
|
|
1296
|
+
lines.push(` // ${w}`);
|
|
1297
|
+
}
|
|
1298
|
+
lines.push(` const result = {}; // TODO: \u6267\u884C\u64CD\u4F5C`);
|
|
1299
|
+
lines.push("");
|
|
1300
|
+
lines.push(` // Assert (Then)`);
|
|
1301
|
+
for (const t of s.then) {
|
|
1302
|
+
lines.push(` // ${t}`);
|
|
1303
|
+
}
|
|
1304
|
+
lines.push(` expect(result).toBeDefined(); // TODO: \u5B8C\u5584\u65AD\u8A00`);
|
|
1305
|
+
lines.push(` });`);
|
|
986
1306
|
}
|
|
1307
|
+
lines.push("");
|
|
987
1308
|
lines.push(`});`);
|
|
988
1309
|
return lines.join("\n");
|
|
989
1310
|
}
|
|
@@ -1024,6 +1345,124 @@ function generateSessionId() {
|
|
|
1024
1345
|
const random = Math.random().toString(36).slice(2, 6);
|
|
1025
1346
|
return `WF-${timestamp}-${random}`.toUpperCase();
|
|
1026
1347
|
}
|
|
1348
|
+
function extractUrls(text) {
|
|
1349
|
+
const urlRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]]+/gi;
|
|
1350
|
+
const matches = text.match(urlRegex);
|
|
1351
|
+
return matches ? [...new Set(matches)] : [];
|
|
1352
|
+
}
|
|
1353
|
+
async function fetchAndAnalyzeReference(url, ctx) {
|
|
1354
|
+
const type = detectResourceType(url);
|
|
1355
|
+
let content = "";
|
|
1356
|
+
let analysis = "";
|
|
1357
|
+
try {
|
|
1358
|
+
const response = await fetch(url, {
|
|
1359
|
+
headers: {
|
|
1360
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
if (!response.ok) {
|
|
1364
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1365
|
+
}
|
|
1366
|
+
content = await response.text();
|
|
1367
|
+
if (ctx.modelService.getCurrentModel()) {
|
|
1368
|
+
analysis = await analyzeReferenceContent(url, content, type, ctx);
|
|
1369
|
+
} else {
|
|
1370
|
+
analysis = extractBasicInfo(content, type);
|
|
1371
|
+
}
|
|
1372
|
+
} catch (error) {
|
|
1373
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
|
|
1374
|
+
}
|
|
1375
|
+
return { url, type, content: content.slice(0, 1e4), analysis };
|
|
1376
|
+
}
|
|
1377
|
+
function detectResourceType(url) {
|
|
1378
|
+
if (url.includes("figma.com") || url.includes("lanhuapp.com")) {
|
|
1379
|
+
return "design";
|
|
1380
|
+
}
|
|
1381
|
+
if (/\.(png|jpg|jpeg|gif|webp|svg)$/i.test(url)) {
|
|
1382
|
+
return "image";
|
|
1383
|
+
}
|
|
1384
|
+
if (/api\//i.test(url)) {
|
|
1385
|
+
return "api";
|
|
1386
|
+
}
|
|
1387
|
+
return "webpage";
|
|
1388
|
+
}
|
|
1389
|
+
async function analyzeReferenceContent(url, content, type, ctx) {
|
|
1390
|
+
const prompt2 = `
|
|
1391
|
+
\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
|
|
1392
|
+
|
|
1393
|
+
## \u53C2\u8003\u8D44\u6E90\u4FE1\u606F
|
|
1394
|
+
- URL: ${url}
|
|
1395
|
+
- \u7C7B\u578B: ${type}
|
|
1396
|
+
|
|
1397
|
+
## \u7F51\u9875\u5185\u5BB9
|
|
1398
|
+
\`\`\`html
|
|
1399
|
+
${content.slice(0, 8e3)}
|
|
1400
|
+
\`\`\`
|
|
1401
|
+
|
|
1402
|
+
## \u5206\u6790\u8981\u6C42
|
|
1403
|
+
|
|
1404
|
+
\u8BF7\u6309\u7167\u4EE5\u4E0B\u7ED3\u6784\u8FDB\u884C\u8BE6\u7EC6\u5206\u6790\uFF1A
|
|
1405
|
+
|
|
1406
|
+
### 1. \u4E1A\u52A1\u529F\u80FD\u5206\u6790
|
|
1407
|
+
- \u6838\u5FC3\u4E1A\u52A1\u529F\u80FD\u662F\u4EC0\u4E48\uFF1F\uFF08\u8BE6\u7EC6\u63CF\u8FF0\uFF09
|
|
1408
|
+
- \u7528\u6237\u53EF\u4EE5\u505A\u4EC0\u4E48\u64CD\u4F5C\uFF1F
|
|
1409
|
+
- \u4E1A\u52A1\u6D41\u7A0B\u662F\u4EC0\u4E48\uFF1F
|
|
1410
|
+
- \u6570\u636E\u8F93\u5165\u8F93\u51FA\u662F\u4EC0\u4E48\uFF1F
|
|
1411
|
+
|
|
1412
|
+
### 2. UI/UX \u7ED3\u6784\u5206\u6790
|
|
1413
|
+
- \u9875\u9762\u5E03\u5C40\u7ED3\u6784
|
|
1414
|
+
- \u4E3B\u8981\u7EC4\u4EF6\u6709\u54EA\u4E9B\uFF1F
|
|
1415
|
+
- \u7EC4\u4EF6\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1416
|
+
- \u4EA4\u4E92\u65B9\u5F0F\uFF08\u70B9\u51FB\u3001\u8F93\u5165\u3001\u62D6\u62FD\u7B49\uFF09
|
|
1417
|
+
|
|
1418
|
+
### 3. \u6570\u636E\u6A21\u578B\u5206\u6790
|
|
1419
|
+
- \u9700\u8981\u54EA\u4E9B\u6570\u636E\uFF1F
|
|
1420
|
+
- \u6570\u636E\u4E4B\u95F4\u7684\u5173\u7CFB
|
|
1421
|
+
- \u6570\u636E\u6765\u6E90\uFF08\u7528\u6237\u8F93\u5165/\u8BA1\u7B97/API\uFF09
|
|
1422
|
+
|
|
1423
|
+
### 4. \u529F\u80FD\u62C6\u5206\u5EFA\u8BAE
|
|
1424
|
+
\u8BF7\u5C06\u529F\u80FD\u62C6\u5206\u4E3A\u53EF\u72EC\u7ACB\u5F00\u53D1\u7684\u4EFB\u52A1\uFF0C\u6BCF\u4E2A\u4EFB\u52A1\u5305\u542B\uFF1A
|
|
1425
|
+
- \u4EFB\u52A1\u540D\u79F0
|
|
1426
|
+
- \u4EFB\u52A1\u63CF\u8FF0
|
|
1427
|
+
- \u6280\u672F\u8981\u70B9
|
|
1428
|
+
- \u4F9D\u8D56\u5173\u7CFB
|
|
1429
|
+
|
|
1430
|
+
### 5. \u6280\u672F\u5B9E\u73B0\u5EFA\u8BAE
|
|
1431
|
+
- \u63A8\u8350\u7684\u6280\u672F\u65B9\u6848
|
|
1432
|
+
- \u9700\u8981\u6CE8\u610F\u7684\u6280\u672F\u96BE\u70B9
|
|
1433
|
+
- \u6027\u80FD\u4F18\u5316\u5EFA\u8BAE
|
|
1434
|
+
|
|
1435
|
+
\u8BF7\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\uFF0C\u91CD\u70B9\u7A81\u51FA\u4E1A\u52A1\u903B\u8F91\u548C\u529F\u80FD\u5B9E\u73B0\u7EC6\u8282\u3002
|
|
1436
|
+
`;
|
|
1437
|
+
const loader = new LoadingIndicator("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
1438
|
+
loader.start();
|
|
1439
|
+
try {
|
|
1440
|
+
const response = await ctx.modelService.sendMessage([
|
|
1441
|
+
{ role: "user", content: prompt2 }
|
|
1442
|
+
], {
|
|
1443
|
+
temperature: 0.3,
|
|
1444
|
+
maxTokens: 4e3
|
|
1445
|
+
});
|
|
1446
|
+
loader.stop(chalk9.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
1447
|
+
return response.content;
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
loader.stop();
|
|
1450
|
+
throw error;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
function extractBasicInfo(content, type) {
|
|
1454
|
+
const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i);
|
|
1455
|
+
const descMatch = content.match(/<meta[^>]*name=["']description["'][^>]*content=["']([^"']+)["']/i);
|
|
1456
|
+
const parts = [];
|
|
1457
|
+
if (titleMatch) {
|
|
1458
|
+
parts.push(`\u6807\u9898: ${titleMatch[1]}`);
|
|
1459
|
+
}
|
|
1460
|
+
if (descMatch) {
|
|
1461
|
+
parts.push(`\u63CF\u8FF0: ${descMatch[1]}`);
|
|
1462
|
+
}
|
|
1463
|
+
parts.push(`\u8D44\u6E90\u7C7B\u578B: ${type}`);
|
|
1464
|
+
return parts.join("\n");
|
|
1465
|
+
}
|
|
1027
1466
|
function generateComplexityBar(score) {
|
|
1028
1467
|
const filled = Math.round(score / 2);
|
|
1029
1468
|
const empty = 5 - filled;
|
|
@@ -1033,6 +1472,7 @@ function getPhaseLabel(phase) {
|
|
|
1033
1472
|
const labels = {
|
|
1034
1473
|
context: "\u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6",
|
|
1035
1474
|
clarify: "\u9700\u6C42\u6F84\u6E05",
|
|
1475
|
+
reference: "\u53C2\u8003\u8D44\u6E90\u5206\u6790",
|
|
1036
1476
|
analysis: "\u590D\u6742\u5EA6\u8BC4\u4F30",
|
|
1037
1477
|
bdd: "BDD \u573A\u666F\u62C6\u89E3",
|
|
1038
1478
|
spec: "OpenSpec \u89C4\u683C",
|
|
@@ -1048,10 +1488,43 @@ function getActiveSession() {
|
|
|
1048
1488
|
function clearActiveSession() {
|
|
1049
1489
|
activeSession = null;
|
|
1050
1490
|
}
|
|
1051
|
-
var MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1491
|
+
var LoadingIndicator, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1052
1492
|
var init_new = __esm({
|
|
1053
1493
|
"src/commands/new.ts"() {
|
|
1054
1494
|
init_esm_shims();
|
|
1495
|
+
LoadingIndicator = class {
|
|
1496
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1497
|
+
frameIndex = 0;
|
|
1498
|
+
interval = null;
|
|
1499
|
+
message;
|
|
1500
|
+
constructor(message) {
|
|
1501
|
+
this.message = message;
|
|
1502
|
+
}
|
|
1503
|
+
start() {
|
|
1504
|
+
process.stdout.write("\x1B[?25l");
|
|
1505
|
+
this.interval = setInterval(() => {
|
|
1506
|
+
const frame = this.frames[this.frameIndex];
|
|
1507
|
+
process.stdout.write(`\r${chalk9.cyan(frame)} ${this.message}...`);
|
|
1508
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1509
|
+
}, 80);
|
|
1510
|
+
}
|
|
1511
|
+
update(message) {
|
|
1512
|
+
this.message = message;
|
|
1513
|
+
}
|
|
1514
|
+
stop(finalMessage) {
|
|
1515
|
+
if (this.interval) {
|
|
1516
|
+
clearInterval(this.interval);
|
|
1517
|
+
this.interval = null;
|
|
1518
|
+
}
|
|
1519
|
+
process.stdout.write("\x1B[?25h");
|
|
1520
|
+
if (finalMessage) {
|
|
1521
|
+
process.stdout.write(`\r${finalMessage}
|
|
1522
|
+
`);
|
|
1523
|
+
} else {
|
|
1524
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1055
1528
|
MAX_FILE_SIZE2 = 1024 * 1024;
|
|
1056
1529
|
COMPLEXITY_THRESHOLD = 6;
|
|
1057
1530
|
CLARITY_THRESHOLD = 0.6;
|