@nick848/sf-cli 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +613 -143
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +609 -139
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +608 -138
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import * as path5 from 'path';
|
|
|
2
2
|
import path5__default from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import chalk9 from 'chalk';
|
|
5
|
-
import * as
|
|
5
|
+
import * as fs9 from 'fs/promises';
|
|
6
6
|
import * as fs10 from 'fs';
|
|
7
7
|
import * as crypto from 'crypto';
|
|
8
8
|
import * as os from 'os';
|
|
@@ -104,7 +104,10 @@ async function executeWorkflow(ctx) {
|
|
|
104
104
|
if (activeSession.phase === "context") {
|
|
105
105
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
|
|
106
106
|
lines.push("");
|
|
107
|
+
const loader = new LoadingIndicator2("\u8BFB\u53D6\u9879\u76EE\u914D\u7F6E");
|
|
108
|
+
loader.start();
|
|
107
109
|
activeSession.context = await readProjectContext(ctx.options.workingDirectory);
|
|
110
|
+
loader.stop();
|
|
108
111
|
lines.push(chalk9.gray(` \u9879\u76EE: ${activeSession.context.name}`));
|
|
109
112
|
lines.push(chalk9.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
|
|
110
113
|
lines.push(chalk9.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
|
|
@@ -194,7 +197,7 @@ ${resource.analysis}`;
|
|
|
194
197
|
lines.push("");
|
|
195
198
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
196
199
|
lines.push("");
|
|
197
|
-
const loader = new
|
|
200
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
198
201
|
loader.start();
|
|
199
202
|
try {
|
|
200
203
|
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
@@ -229,7 +232,7 @@ ${resource.analysis}`;
|
|
|
229
232
|
lines.push("");
|
|
230
233
|
lines.push(chalk9.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
231
234
|
lines.push("");
|
|
232
|
-
const loader = new
|
|
235
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
233
236
|
loader.start();
|
|
234
237
|
try {
|
|
235
238
|
activeSession.specItems = await generateSpecItemsWithAI(
|
|
@@ -398,31 +401,85 @@ async function handleWorkflowInput(input, ctx) {
|
|
|
398
401
|
activeSession.phase = "analysis";
|
|
399
402
|
return executeWorkflow(ctx);
|
|
400
403
|
}
|
|
401
|
-
if (activeSession.phase === "spec") {
|
|
404
|
+
if (activeSession.phase === "spec" || activeSession.specFeedbackState === "waiting_confirm") {
|
|
402
405
|
if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
|
|
406
|
+
activeSession.specFeedbackState = void 0;
|
|
403
407
|
activeSession.phase = "tdd";
|
|
404
408
|
return executeWorkflow(ctx);
|
|
405
409
|
}
|
|
406
|
-
if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
|
|
407
|
-
activeSession.
|
|
408
|
-
|
|
409
|
-
activeSession.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
410
|
+
if (trimmed === "n" || trimmed === "no" || trimmed === "\u4E0D\u6EE1\u610F" || trimmed === "\u91CD\u65B0") {
|
|
411
|
+
activeSession.specFeedbackState = "collecting_feedback";
|
|
412
|
+
if (!activeSession.specFeedbacks) {
|
|
413
|
+
activeSession.specFeedbacks = [];
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
output: chalk9.yellow("\n\u{1F4DD} \u8BF7\u8F93\u5165\u60A8\u5BF9\u89C4\u683C\u4E0D\u6EE1\u610F\u7684\u5730\u65B9:") + chalk9.gray("\n - \u9057\u6F0F\u7684\u529F\u80FD\u70B9") + chalk9.gray("\n - \u4E0D\u5408\u7406\u7684\u62C6\u5206") + chalk9.gray("\n - \u9700\u8981\u8865\u5145\u7684\u7EC6\u8282") + chalk9.gray("\n - \u5176\u4ED6\u6539\u8FDB\u5EFA\u8BAE") + chalk9.cyan('\n\n\u8F93\u5165\u5B8C\u6210\u540E\uFF0C\u8F93\u5165\u7A7A\u884C\u6216 "done" \u7ED3\u675F\u8F93\u5165')
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (activeSession.specFeedbackState === "collecting_feedback") {
|
|
421
|
+
if (trimmed === "" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
|
|
422
|
+
if (!activeSession.specFeedbacks || activeSession.specFeedbacks.length === 0) {
|
|
423
|
+
return {
|
|
424
|
+
output: chalk9.yellow('\u26A0\uFE0F \u8BF7\u81F3\u5C11\u8F93\u5165\u4E00\u6761\u53CD\u9988\u610F\u89C1\uFF0C\u6216\u8F93\u5165 "cancel" \u53D6\u6D88')
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
activeSession.specFeedbackState = "feedback_received";
|
|
428
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210\u89C4\u683C");
|
|
429
|
+
loader.start();
|
|
430
|
+
try {
|
|
431
|
+
activeSession.specItems = await generateSpecItemsWithFeedback(
|
|
432
|
+
activeSession.refinedRequirement,
|
|
433
|
+
activeSession.context,
|
|
434
|
+
activeSession.bddScenarios,
|
|
435
|
+
activeSession.clarificationQuestions,
|
|
436
|
+
activeSession.referenceResources,
|
|
437
|
+
activeSession.specFeedbacks,
|
|
438
|
+
ctx
|
|
439
|
+
);
|
|
440
|
+
loader.stop(chalk9.green(" \u2713 \u89C4\u683C\u5DF2\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210"));
|
|
441
|
+
} catch (error) {
|
|
442
|
+
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u65B9\u6CD5\u91CD\u65B0\u751F\u6210"));
|
|
443
|
+
activeSession.specItems = generateSpecItems(
|
|
444
|
+
activeSession.refinedRequirement,
|
|
445
|
+
activeSession.context,
|
|
446
|
+
activeSession.bddScenarios,
|
|
447
|
+
activeSession.clarificationQuestions,
|
|
448
|
+
activeSession.referenceResources
|
|
449
|
+
);
|
|
450
|
+
}
|
|
420
451
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
452
|
+
activeSession.specFeedbackState = "waiting_confirm";
|
|
453
|
+
const lines = [];
|
|
454
|
+
lines.push(chalk9.cyan("\n\u2501\u2501\u2501 \u66F4\u65B0\u540E\u7684\u4EFB\u52A1\u6982\u89C8 \u2501\u2501\u2501"));
|
|
455
|
+
for (const item of activeSession.specItems.slice(0, 8)) {
|
|
456
|
+
const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
457
|
+
lines.push(chalk9.gray(` ${icon} [${item.id}] ${item.title}`));
|
|
458
|
+
}
|
|
459
|
+
if (activeSession.specItems.length > 8) {
|
|
460
|
+
lines.push(chalk9.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
|
|
461
|
+
}
|
|
421
462
|
return {
|
|
422
|
-
output:
|
|
423
|
-
|
|
463
|
+
output: lines.join("\n") + chalk9.green(`
|
|
464
|
+
|
|
465
|
+
\u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u66F4\u65B0`) + chalk9.gray(`
|
|
466
|
+
\u8DEF\u5F84: ${specPath}`) + chalk9.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9.red("\n n - \u4ECD\u6709\u95EE\u9898\uFF0C\u7EE7\u7EED\u53CD\u9988")
|
|
424
467
|
};
|
|
425
468
|
}
|
|
469
|
+
if (trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
|
|
470
|
+
activeSession.specFeedbackState = "waiting_confirm";
|
|
471
|
+
activeSession.specFeedbacks = [];
|
|
472
|
+
return {
|
|
473
|
+
output: chalk9.gray("\u5DF2\u53D6\u6D88\u53CD\u9988\uFF0C\u8FD4\u56DE\u89C4\u683C\u786E\u8BA4") + chalk9.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9.red("\n n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210")
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
if (!activeSession.specFeedbacks) {
|
|
477
|
+
activeSession.specFeedbacks = [];
|
|
478
|
+
}
|
|
479
|
+
activeSession.specFeedbacks.push(trimmed);
|
|
480
|
+
return {
|
|
481
|
+
output: chalk9.gray(`\u5DF2\u8BB0\u5F55\u53CD\u9988 #${activeSession.specFeedbacks.length}: `) + chalk9.white(trimmed.slice(0, 50)) + chalk9.gray('\n\u7EE7\u7EED\u8F93\u5165\uFF0C\u6216\u8F93\u5165 "done" \u5B8C\u6210\u53CD\u9988')
|
|
482
|
+
};
|
|
426
483
|
}
|
|
427
484
|
if (activeSession.phase === "develop") {
|
|
428
485
|
if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
|
|
@@ -581,28 +638,17 @@ async function executeDevelopment(ctx, session) {
|
|
|
581
638
|
if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
|
|
582
639
|
continue;
|
|
583
640
|
}
|
|
584
|
-
const loader = new
|
|
641
|
+
const loader = new LoadingIndicator2(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
|
|
585
642
|
loader.start();
|
|
586
643
|
try {
|
|
587
644
|
const taskPrompt = buildTaskPrompt(session, item, i);
|
|
645
|
+
const structureInfo = session.context?.projectStructure;
|
|
646
|
+
const structureGuide = buildStructureGuide(structureInfo);
|
|
647
|
+
const systemPrompt = buildCodeGenerationSystemPrompt(session, structureGuide);
|
|
588
648
|
const messages = [
|
|
589
649
|
{
|
|
590
650
|
role: "system",
|
|
591
|
-
content:
|
|
592
|
-
|
|
593
|
-
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
594
|
-
1. \u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
|
|
595
|
-
2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
596
|
-
3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
597
|
-
4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
598
|
-
|
|
599
|
-
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
600
|
-
- \u540D\u79F0: ${session.context?.name}
|
|
601
|
-
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
602
|
-
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
603
|
-
|
|
604
|
-
${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
|
|
605
|
-
${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
651
|
+
content: systemPrompt
|
|
606
652
|
},
|
|
607
653
|
{
|
|
608
654
|
role: "user",
|
|
@@ -614,25 +660,25 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
614
660
|
maxTokens: 4e3,
|
|
615
661
|
// 单个任务减少 token
|
|
616
662
|
agent: "frontend-dev",
|
|
617
|
-
timeout:
|
|
618
|
-
//
|
|
663
|
+
timeout: 3e5
|
|
664
|
+
// 5分钟超时,确保复杂代码有足够时间
|
|
619
665
|
});
|
|
620
666
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
621
667
|
if (codeBlocks.length > 0) {
|
|
622
668
|
for (const block of codeBlocks) {
|
|
623
669
|
const filePath = path5.join(workingDir, block.filename);
|
|
624
670
|
const dir = path5.dirname(filePath);
|
|
625
|
-
await
|
|
626
|
-
await
|
|
671
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
672
|
+
await fs9.writeFile(filePath, block.code, "utf-8");
|
|
627
673
|
files.push(block.filename);
|
|
628
674
|
}
|
|
629
675
|
loader.stop(chalk9.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
|
|
630
676
|
} else {
|
|
631
677
|
const implDir = path5.join(workingDir, "src");
|
|
632
|
-
await
|
|
678
|
+
await fs9.mkdir(implDir, { recursive: true });
|
|
633
679
|
const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
|
|
634
680
|
const filePath = path5.join(implDir, fileName);
|
|
635
|
-
await
|
|
681
|
+
await fs9.writeFile(filePath, `// TODO: ${item.title}
|
|
636
682
|
// ${item.description}
|
|
637
683
|
`, "utf-8");
|
|
638
684
|
files.push(`src/${fileName}`);
|
|
@@ -655,6 +701,91 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
655
701
|
};
|
|
656
702
|
}
|
|
657
703
|
}
|
|
704
|
+
function buildStructureGuide(structure) {
|
|
705
|
+
if (!structure) {
|
|
706
|
+
return `## \u9879\u76EE\u7ED3\u6784
|
|
707
|
+
- \u7EC4\u4EF6\u653E\u5728 src/components/
|
|
708
|
+
- \u9875\u9762\u653E\u5728 src/pages/
|
|
709
|
+
- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/`;
|
|
710
|
+
}
|
|
711
|
+
const lines = ["## \u9879\u76EE\u7ED3\u6784\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09"];
|
|
712
|
+
if (structure.hasSrc) {
|
|
713
|
+
lines.push(`- \u6E90\u7801\u76EE\u5F55: ${structure.srcDir}/`);
|
|
714
|
+
}
|
|
715
|
+
if (structure.hasPages) {
|
|
716
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: ${structure.pagesDir}/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
717
|
+
} else {
|
|
718
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: src/pages/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
719
|
+
}
|
|
720
|
+
if (structure.hasComponents) {
|
|
721
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: ${structure.componentsDir}/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
722
|
+
} else {
|
|
723
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: src/components/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
724
|
+
}
|
|
725
|
+
if (structure.hasApi) {
|
|
726
|
+
lines.push(`- API \u76EE\u5F55: src/api/ \u6216 src/services/`);
|
|
727
|
+
}
|
|
728
|
+
if (structure.hasHooks) {
|
|
729
|
+
lines.push(`- Hooks \u76EE\u5F55: src/hooks/`);
|
|
730
|
+
}
|
|
731
|
+
if (structure.hasStore) {
|
|
732
|
+
lines.push(`- \u72B6\u6001\u7BA1\u7406\u76EE\u5F55: src/store/`);
|
|
733
|
+
}
|
|
734
|
+
if (structure.hasUtils) {
|
|
735
|
+
lines.push(`- \u5DE5\u5177\u51FD\u6570\u76EE\u5F55: src/utils/`);
|
|
736
|
+
}
|
|
737
|
+
lines.push("");
|
|
738
|
+
lines.push("\u26A0\uFE0F \u6587\u4EF6\u5FC5\u987B\u653E\u5728\u4E0A\u8FF0\u5BF9\u5E94\u76EE\u5F55\u4E2D\uFF0C\u4E0D\u8981\u968F\u610F\u521B\u5EFA\u65B0\u76EE\u5F55");
|
|
739
|
+
return lines.join("\n");
|
|
740
|
+
}
|
|
741
|
+
function buildCodeGenerationSystemPrompt(session, structureGuide) {
|
|
742
|
+
const framework = session.context?.framework || "React";
|
|
743
|
+
const techStack = session.context?.techStack?.join(", ") || "TypeScript";
|
|
744
|
+
const devStandards = session.context?.devStandards || "";
|
|
745
|
+
const standardsText = devStandards.slice(0, 6e3);
|
|
746
|
+
return `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u4EFB\u52A1\u63CF\u8FF0\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
|
|
747
|
+
|
|
748
|
+
## \u26A0\uFE0F \u6838\u5FC3\u89C4\u5219\uFF08\u5FC5\u987B\u9075\u5B88\uFF09
|
|
749
|
+
|
|
750
|
+
1. **\u8BED\u8A00**: \u5FC5\u987B\u4F7F\u7528 TypeScript (.tsx/.ts \u6587\u4EF6)
|
|
751
|
+
2. **\u6846\u67B6**: ${framework}
|
|
752
|
+
3. **\u53EA\u751F\u6210\u5F53\u524D\u4EFB\u52A1\u76F8\u5173\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u751F\u6210\u5176\u4ED6\u4EFB\u52A1\u7684\u4EE3\u7801
|
|
753
|
+
4. **\u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u6B63\u786E**\uFF1A\u4E25\u683C\u6309\u7167\u9879\u76EE\u7ED3\u6784\u653E\u7F6E\u6587\u4EF6
|
|
754
|
+
5. **\u5FC5\u987B\u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u5199 TODO \u6216\u5360\u4F4D\u7B26
|
|
755
|
+
6. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA\u9875\u9762**\uFF0C\u5FC5\u987B\u751F\u6210\u9875\u9762\u7EC4\u4EF6
|
|
756
|
+
7. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA UI**\uFF0C\u5FC5\u987B\u751F\u6210\u5BF9\u5E94\u7684\u7EC4\u4EF6\u548C\u6837\u5F0F
|
|
757
|
+
|
|
758
|
+
${structureGuide}
|
|
759
|
+
|
|
760
|
+
## \u9879\u76EE\u4FE1\u606F
|
|
761
|
+
|
|
762
|
+
- \u540D\u79F0: ${session.context?.name || "\u672A\u547D\u540D\u9879\u76EE"}
|
|
763
|
+
- \u6846\u67B6: ${framework}
|
|
764
|
+
- \u6280\u672F\u6808: ${techStack}
|
|
765
|
+
|
|
766
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
767
|
+
|
|
768
|
+
${standardsText || `### \u9ED8\u8BA4\u89C4\u8303
|
|
769
|
+
- \u4F7F\u7528\u51FD\u6570\u5F0F\u7EC4\u4EF6
|
|
770
|
+
- \u4F7F\u7528 TypeScript \u7C7B\u578B\u5B9A\u4E49
|
|
771
|
+
- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65
|
|
772
|
+
- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase
|
|
773
|
+
- \u5BFC\u51FA\u4F7F\u7528 export default \u6216 export const`}
|
|
774
|
+
|
|
775
|
+
## \u8F93\u51FA\u683C\u5F0F
|
|
776
|
+
|
|
777
|
+
\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u5305\u88F9\uFF0C\u4F8B\u5982\uFF1A
|
|
778
|
+
|
|
779
|
+
\`\`\`src/pages/HomePage.tsx
|
|
780
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
781
|
+
\`\`\`
|
|
782
|
+
|
|
783
|
+
\`\`\`src/components/Button/Button.tsx
|
|
784
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
785
|
+
\`\`\`
|
|
786
|
+
|
|
787
|
+
\u73B0\u5728\u8BF7\u6839\u636E\u4EFB\u52A1\u8981\u6C42\u751F\u6210\u4EE3\u7801\u3002`;
|
|
788
|
+
}
|
|
658
789
|
function buildTaskPrompt(session, item, index) {
|
|
659
790
|
const lines = [];
|
|
660
791
|
lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
|
|
@@ -703,11 +834,13 @@ async function executeReview(ctx, session) {
|
|
|
703
834
|
const workingDir = ctx.options.workingDirectory;
|
|
704
835
|
const issues = [];
|
|
705
836
|
const suggestions = [];
|
|
837
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u5BA1\u6838\u4EE3\u7801");
|
|
838
|
+
loader.start();
|
|
706
839
|
try {
|
|
707
840
|
const codeContents = [];
|
|
708
841
|
for (const file of session.implFiles) {
|
|
709
842
|
try {
|
|
710
|
-
const content = await
|
|
843
|
+
const content = await fs9.readFile(path5.join(workingDir, file), "utf-8");
|
|
711
844
|
codeContents.push(`// ${file}
|
|
712
845
|
${content}`);
|
|
713
846
|
} catch {
|
|
@@ -716,7 +849,7 @@ ${content}`);
|
|
|
716
849
|
const testContents = [];
|
|
717
850
|
for (const file of session.testFiles) {
|
|
718
851
|
try {
|
|
719
|
-
const content = await
|
|
852
|
+
const content = await fs9.readFile(path5.join(workingDir, file), "utf-8");
|
|
720
853
|
testContents.push(`// ${file}
|
|
721
854
|
${content}`);
|
|
722
855
|
} catch {
|
|
@@ -765,7 +898,9 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
|
765
898
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
766
899
|
temperature: 0.2,
|
|
767
900
|
maxTokens: 2e3,
|
|
768
|
-
agent: "code-reviewer"
|
|
901
|
+
agent: "code-reviewer",
|
|
902
|
+
timeout: 18e4
|
|
903
|
+
// 3分钟超时
|
|
769
904
|
});
|
|
770
905
|
const result = response.content;
|
|
771
906
|
const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
|
|
@@ -787,8 +922,10 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
|
787
922
|
const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
|
|
788
923
|
suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
|
|
789
924
|
}
|
|
925
|
+
loader.stop(passed ? chalk9.green(" \u2713 \u5BA1\u6838\u901A\u8FC7") : chalk9.yellow(" \u26A0 \u53D1\u73B0\u95EE\u9898"));
|
|
790
926
|
return { passed, issues, suggestions };
|
|
791
927
|
} catch (error) {
|
|
928
|
+
loader.stop(chalk9.red(" \u2717 \u5BA1\u6838\u51FA\u9519"));
|
|
792
929
|
suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
|
|
793
930
|
return { passed: true, issues, suggestions };
|
|
794
931
|
}
|
|
@@ -806,9 +943,9 @@ async function readProjectContext(workingDir) {
|
|
|
806
943
|
};
|
|
807
944
|
const agentsPath = path5.join(workingDir, "AGENTS.md");
|
|
808
945
|
try {
|
|
809
|
-
const stats = await
|
|
946
|
+
const stats = await fs9.stat(agentsPath);
|
|
810
947
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
811
|
-
context.agentsMd = await
|
|
948
|
+
context.agentsMd = await fs9.readFile(agentsPath, "utf-8");
|
|
812
949
|
const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
813
950
|
if (nameMatch) context.name = nameMatch[1];
|
|
814
951
|
const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
|
|
@@ -822,9 +959,9 @@ async function readProjectContext(workingDir) {
|
|
|
822
959
|
}
|
|
823
960
|
const configPath = path5.join(workingDir, "openspec", "config.yaml");
|
|
824
961
|
try {
|
|
825
|
-
const stats = await
|
|
962
|
+
const stats = await fs9.stat(configPath);
|
|
826
963
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
827
|
-
context.configYaml = await
|
|
964
|
+
context.configYaml = await fs9.readFile(configPath, "utf-8");
|
|
828
965
|
const nameMatch = context.configYaml.match(/name:\s*(.+)/);
|
|
829
966
|
if (nameMatch) context.name = nameMatch[1].trim();
|
|
830
967
|
const typeMatch = context.configYaml.match(/type:\s*(.+)/);
|
|
@@ -845,14 +982,173 @@ async function readProjectContext(workingDir) {
|
|
|
845
982
|
}
|
|
846
983
|
const devStandardsPath = path5.join(workingDir, ".sf-cli", "norms", "devstanded.md");
|
|
847
984
|
try {
|
|
848
|
-
const stats = await
|
|
985
|
+
const stats = await fs9.stat(devStandardsPath);
|
|
849
986
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
850
|
-
context.devStandards = await
|
|
987
|
+
context.devStandards = await fs9.readFile(devStandardsPath, "utf-8");
|
|
851
988
|
}
|
|
852
989
|
} catch {
|
|
853
990
|
}
|
|
991
|
+
if (!context.framework) {
|
|
992
|
+
const detectedFramework = await detectFramework2(workingDir);
|
|
993
|
+
if (detectedFramework) {
|
|
994
|
+
context.framework = detectedFramework;
|
|
995
|
+
context.techStack = [detectedFramework, ...context.techStack.filter((t) => t !== detectedFramework)];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (!context.devStandards) {
|
|
999
|
+
context.devStandards = await analyzeProjectForStandards(workingDir, context);
|
|
1000
|
+
}
|
|
1001
|
+
context.projectStructure = await analyzeProjectStructure(workingDir);
|
|
854
1002
|
return context;
|
|
855
1003
|
}
|
|
1004
|
+
async function detectFramework2(workingDir) {
|
|
1005
|
+
try {
|
|
1006
|
+
const pkgPath = path5.join(workingDir, "package.json");
|
|
1007
|
+
const pkgContent = await fs9.readFile(pkgPath, "utf-8");
|
|
1008
|
+
const pkg = JSON.parse(pkgContent);
|
|
1009
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1010
|
+
if (deps["react"] || deps["next"]) return "React";
|
|
1011
|
+
if (deps["vue"] || deps["nuxt"]) return "Vue";
|
|
1012
|
+
if (deps["angular"] || deps["@angular/core"]) return "Angular";
|
|
1013
|
+
if (deps["svelte"]) return "Svelte";
|
|
1014
|
+
if (deps["solid-js"]) return "Solid";
|
|
1015
|
+
if (deps["vite"] || deps["webpack"]) return "JavaScript";
|
|
1016
|
+
if (deps["typescript"]) return "TypeScript";
|
|
1017
|
+
return null;
|
|
1018
|
+
} catch {
|
|
1019
|
+
return null;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
async function analyzeProjectForStandards(workingDir, context) {
|
|
1023
|
+
const standards = [];
|
|
1024
|
+
standards.push(`# \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u81EA\u52A8\u751F\u6210\uFF09
|
|
1025
|
+
> \u9879\u76EE\u540D\u79F0: ${context.name}
|
|
1026
|
+
> \u6846\u67B6: ${context.framework || "\u672A\u68C0\u6D4B\u5230"}
|
|
1027
|
+
> \u6280\u672F\u6808: ${context.techStack.join(", ") || "\u672A\u68C0\u6D4B\u5230"}
|
|
1028
|
+
`);
|
|
1029
|
+
try {
|
|
1030
|
+
const tsConfigPath = path5.join(workingDir, "tsconfig.json");
|
|
1031
|
+
await fs9.access(tsConfigPath);
|
|
1032
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 TypeScript");
|
|
1033
|
+
} catch {
|
|
1034
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 JavaScript");
|
|
1035
|
+
}
|
|
1036
|
+
try {
|
|
1037
|
+
const srcDir = path5.join(workingDir, "src");
|
|
1038
|
+
const files = await listFilesDeep(srcDir);
|
|
1039
|
+
const hasCss = files.some((f) => f.endsWith(".css"));
|
|
1040
|
+
const hasScss = files.some((f) => f.endsWith(".scss") || f.endsWith(".sass"));
|
|
1041
|
+
const hasLess = files.some((f) => f.endsWith(".less"));
|
|
1042
|
+
const hasStyled = files.some((f) => f.includes(".styled.") || f.includes("styled-components"));
|
|
1043
|
+
if (hasScss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 SCSS/Sass");
|
|
1044
|
+
else if (hasLess) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 Less");
|
|
1045
|
+
else if (hasStyled) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 styled-components");
|
|
1046
|
+
else if (hasCss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 CSS");
|
|
1047
|
+
} catch {
|
|
1048
|
+
}
|
|
1049
|
+
try {
|
|
1050
|
+
const srcDir = path5.join(workingDir, "src");
|
|
1051
|
+
const entries = await fs9.readdir(srcDir, { withFileTypes: true });
|
|
1052
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1053
|
+
if (dirs.includes("components")) standards.push("\n## \u76EE\u5F55\u7ED3\u6784\n- \u7EC4\u4EF6\u653E\u5728 src/components/");
|
|
1054
|
+
if (dirs.includes("pages") || dirs.includes("views")) standards.push("- \u9875\u9762\u653E\u5728 src/pages/ \u6216 src/views/");
|
|
1055
|
+
if (dirs.includes("hooks")) standards.push("- Hooks \u653E\u5728 src/hooks/");
|
|
1056
|
+
if (dirs.includes("utils")) standards.push("- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/");
|
|
1057
|
+
if (dirs.includes("api") || dirs.includes("services")) standards.push("- API \u8C03\u7528\u653E\u5728 src/api/ \u6216 src/services/");
|
|
1058
|
+
if (dirs.includes("store") || dirs.includes("stores")) standards.push("- \u72B6\u6001\u7BA1\u7406\u653E\u5728 src/store/");
|
|
1059
|
+
} catch {
|
|
1060
|
+
}
|
|
1061
|
+
standards.push("\n## \u4EE3\u7801\u89C4\u8303");
|
|
1062
|
+
standards.push("- \u4F7F\u7528 ES6+ \u8BED\u6CD5");
|
|
1063
|
+
standards.push("- \u7EC4\u4EF6\u4F7F\u7528\u51FD\u6570\u5F0F\u5199\u6CD5");
|
|
1064
|
+
standards.push("- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65");
|
|
1065
|
+
standards.push("- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase");
|
|
1066
|
+
standards.push("- \u5E38\u91CF\u4F7F\u7528 UPPER_SNAKE_CASE");
|
|
1067
|
+
return standards.join("\n");
|
|
1068
|
+
}
|
|
1069
|
+
async function listFilesDeep(dir, maxDepth = 3) {
|
|
1070
|
+
const files = [];
|
|
1071
|
+
async function scan(currentDir, depth) {
|
|
1072
|
+
if (depth > maxDepth) return;
|
|
1073
|
+
try {
|
|
1074
|
+
const entries = await fs9.readdir(currentDir, { withFileTypes: true });
|
|
1075
|
+
for (const entry of entries) {
|
|
1076
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1077
|
+
const fullPath = path5.join(currentDir, entry.name);
|
|
1078
|
+
if (entry.isDirectory()) {
|
|
1079
|
+
await scan(fullPath, depth + 1);
|
|
1080
|
+
} else {
|
|
1081
|
+
files.push(fullPath);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
} catch {
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
await scan(dir, 0);
|
|
1088
|
+
return files;
|
|
1089
|
+
}
|
|
1090
|
+
async function analyzeProjectStructure(workingDir) {
|
|
1091
|
+
const structure = {
|
|
1092
|
+
hasSrc: false,
|
|
1093
|
+
hasPages: false,
|
|
1094
|
+
hasComponents: false,
|
|
1095
|
+
hasApi: false,
|
|
1096
|
+
hasHooks: false,
|
|
1097
|
+
hasStore: false,
|
|
1098
|
+
hasUtils: false,
|
|
1099
|
+
srcDir: "src",
|
|
1100
|
+
pagesDir: "",
|
|
1101
|
+
componentsDir: ""
|
|
1102
|
+
};
|
|
1103
|
+
try {
|
|
1104
|
+
const srcDir = path5.join(workingDir, "src");
|
|
1105
|
+
try {
|
|
1106
|
+
const srcStat = await fs9.stat(srcDir);
|
|
1107
|
+
if (srcStat.isDirectory()) {
|
|
1108
|
+
structure.hasSrc = true;
|
|
1109
|
+
structure.srcDir = "src";
|
|
1110
|
+
const srcEntries = await fs9.readdir(srcDir, { withFileTypes: true });
|
|
1111
|
+
const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1112
|
+
if (srcDirs.includes("pages")) {
|
|
1113
|
+
structure.hasPages = true;
|
|
1114
|
+
structure.pagesDir = "src/pages";
|
|
1115
|
+
} else if (srcDirs.includes("views")) {
|
|
1116
|
+
structure.hasPages = true;
|
|
1117
|
+
structure.pagesDir = "src/views";
|
|
1118
|
+
}
|
|
1119
|
+
if (srcDirs.includes("components")) {
|
|
1120
|
+
structure.hasComponents = true;
|
|
1121
|
+
structure.componentsDir = "src/components";
|
|
1122
|
+
}
|
|
1123
|
+
if (srcDirs.includes("api") || srcDirs.includes("services")) {
|
|
1124
|
+
structure.hasApi = true;
|
|
1125
|
+
}
|
|
1126
|
+
if (srcDirs.includes("hooks")) {
|
|
1127
|
+
structure.hasHooks = true;
|
|
1128
|
+
}
|
|
1129
|
+
if (srcDirs.includes("store") || srcDirs.includes("stores")) {
|
|
1130
|
+
structure.hasStore = true;
|
|
1131
|
+
}
|
|
1132
|
+
if (srcDirs.includes("utils")) {
|
|
1133
|
+
structure.hasUtils = true;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
} catch {
|
|
1137
|
+
const rootEntries = await fs9.readdir(workingDir, { withFileTypes: true });
|
|
1138
|
+
const rootDirs = rootEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1139
|
+
if (rootDirs.includes("pages")) {
|
|
1140
|
+
structure.hasPages = true;
|
|
1141
|
+
structure.pagesDir = "pages";
|
|
1142
|
+
}
|
|
1143
|
+
if (rootDirs.includes("components")) {
|
|
1144
|
+
structure.hasComponents = true;
|
|
1145
|
+
structure.componentsDir = "components";
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
} catch {
|
|
1149
|
+
}
|
|
1150
|
+
return structure;
|
|
1151
|
+
}
|
|
856
1152
|
function analyzeComplexity(requirement, context) {
|
|
857
1153
|
let score = 3;
|
|
858
1154
|
if (requirement.length > 100) score += 1;
|
|
@@ -994,7 +1290,8 @@ ${r.analysis}`).join("\n\n") : "\u65E0"}
|
|
|
994
1290
|
], {
|
|
995
1291
|
temperature: 0.3,
|
|
996
1292
|
maxTokens: 4e3,
|
|
997
|
-
timeout:
|
|
1293
|
+
timeout: 3e5
|
|
1294
|
+
// 5分钟超时
|
|
998
1295
|
});
|
|
999
1296
|
try {
|
|
1000
1297
|
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
@@ -1202,7 +1499,7 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
1202
1499
|
- \u96C6\u6210\u6D4B\u8BD5
|
|
1203
1500
|
|
|
1204
1501
|
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
|
|
1205
|
-
const loader = new
|
|
1502
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
1206
1503
|
loader.start();
|
|
1207
1504
|
try {
|
|
1208
1505
|
const response = await ctx.modelService.sendMessage([
|
|
@@ -1210,8 +1507,8 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
1210
1507
|
], {
|
|
1211
1508
|
temperature: 0.3,
|
|
1212
1509
|
maxTokens: 4e3,
|
|
1213
|
-
timeout:
|
|
1214
|
-
//
|
|
1510
|
+
timeout: 3e5
|
|
1511
|
+
// 5分钟超时
|
|
1215
1512
|
});
|
|
1216
1513
|
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
1217
1514
|
if (jsonMatch) {
|
|
@@ -1254,12 +1551,96 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
1254
1551
|
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1255
1552
|
}
|
|
1256
1553
|
}
|
|
1554
|
+
async function generateSpecItemsWithFeedback(requirement, context, bddScenarios, questions, references, feedbacks, ctx) {
|
|
1555
|
+
const prompt2 = `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u9879\u76EE\u7ECF\u7406\u548C\u6280\u672F\u67B6\u6784\u5E08\u3002\u8BF7\u6839\u636E\u7528\u6237\u53CD\u9988\uFF0C\u91CD\u65B0\u62C6\u5206\u9700\u6C42\u4EFB\u52A1\u3002
|
|
1556
|
+
|
|
1557
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
1558
|
+
${requirement}
|
|
1559
|
+
|
|
1560
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
1561
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
|
|
1562
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
1563
|
+
${context.devStandards ? `
|
|
1564
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
1565
|
+
${context.devStandards.slice(0, 2e3)}
|
|
1566
|
+
` : ""}
|
|
1567
|
+
|
|
1568
|
+
## \u4E4B\u524D\u7684 BDD \u573A\u666F
|
|
1569
|
+
${bddScenarios.map((s) => `- Feature: ${s.feature}`).join("\n")}
|
|
1570
|
+
|
|
1571
|
+
## \u7528\u6237\u53CD\u9988\uFF08\u5FC5\u987B\u89E3\u51B3\u8FD9\u4E9B\u95EE\u9898\uFF09
|
|
1572
|
+
${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
|
|
1573
|
+
|
|
1574
|
+
## \u8981\u6C42
|
|
1575
|
+
1. **\u5FC5\u987B\u89E3\u51B3\u7528\u6237\u53CD\u9988\u4E2D\u7684\u6240\u6709\u95EE\u9898**
|
|
1576
|
+
2. \u5982\u679C\u7528\u6237\u6307\u51FA\u9057\u6F0F\u7684\u529F\u80FD\u70B9\uFF0C\u8BF7\u8865\u5145\u76F8\u5173\u4EFB\u52A1
|
|
1577
|
+
3. \u5982\u679C\u7528\u6237\u6307\u51FA\u62C6\u5206\u4E0D\u5408\u7406\uFF0C\u8BF7\u91CD\u65B0\u8C03\u6574\u7C92\u5EA6
|
|
1578
|
+
4. \u5982\u679C\u7528\u6237\u9700\u8981\u66F4\u591A\u7EC6\u8282\uFF0C\u8BF7\u62C6\u5206\u5F97\u66F4\u7EC6\u81F4
|
|
1579
|
+
5. \u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\u5355\u4E00\u804C\u8D23\u30012-4\u5C0F\u65F6\u53EF\u5B8C\u6210
|
|
1580
|
+
|
|
1581
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
1582
|
+
\`\`\`json
|
|
1583
|
+
[
|
|
1584
|
+
{
|
|
1585
|
+
"id": "T001",
|
|
1586
|
+
"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
|
|
1587
|
+
"description": "\u8BE6\u7EC6\u63CF\u8FF0",
|
|
1588
|
+
"priority": "high",
|
|
1589
|
+
"acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
|
|
1590
|
+
}
|
|
1591
|
+
]
|
|
1592
|
+
\`\`\`
|
|
1593
|
+
|
|
1594
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u786E\u4FDD\u89E3\u51B3\u4E86\u7528\u6237\u7684\u6240\u6709\u53CD\u9988\u3002`;
|
|
1595
|
+
try {
|
|
1596
|
+
const response = await ctx.modelService.sendMessage([
|
|
1597
|
+
{ role: "user", content: prompt2 }
|
|
1598
|
+
], {
|
|
1599
|
+
temperature: 0.3,
|
|
1600
|
+
maxTokens: 4e3,
|
|
1601
|
+
timeout: 3e5
|
|
1602
|
+
// 5分钟超时
|
|
1603
|
+
});
|
|
1604
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
1605
|
+
if (jsonMatch) {
|
|
1606
|
+
try {
|
|
1607
|
+
const parsed = JSON.parse(jsonMatch[1].trim());
|
|
1608
|
+
return parsed.map((item, index) => ({
|
|
1609
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
1610
|
+
title: item.title,
|
|
1611
|
+
description: item.description,
|
|
1612
|
+
priority: item.priority || "medium",
|
|
1613
|
+
files: [],
|
|
1614
|
+
tests: item.acceptanceCriteria || []
|
|
1615
|
+
}));
|
|
1616
|
+
} catch {
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
try {
|
|
1620
|
+
const parsed = JSON.parse(response.content);
|
|
1621
|
+
if (Array.isArray(parsed)) {
|
|
1622
|
+
return parsed.map((item, index) => ({
|
|
1623
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
1624
|
+
title: item.title,
|
|
1625
|
+
description: item.description,
|
|
1626
|
+
priority: item.priority || "medium",
|
|
1627
|
+
files: [],
|
|
1628
|
+
tests: item.acceptanceCriteria || []
|
|
1629
|
+
}));
|
|
1630
|
+
}
|
|
1631
|
+
} catch {
|
|
1632
|
+
}
|
|
1633
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1634
|
+
} catch {
|
|
1635
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1257
1638
|
async function saveSpecFile(workingDir, session) {
|
|
1258
1639
|
const specDir = path5.join(workingDir, "openspec", "changes");
|
|
1259
|
-
await
|
|
1640
|
+
await fs9.mkdir(specDir, { recursive: true });
|
|
1260
1641
|
const specPath = path5.join(specDir, `${session.id}-spec.md`);
|
|
1261
1642
|
const content = formatSpecFile(session);
|
|
1262
|
-
await
|
|
1643
|
+
await fs9.writeFile(specPath, content, "utf-8");
|
|
1263
1644
|
return specPath;
|
|
1264
1645
|
}
|
|
1265
1646
|
function formatSpecFile(session) {
|
|
@@ -1320,6 +1701,16 @@ function formatSpecFile(session) {
|
|
|
1320
1701
|
lines.push("");
|
|
1321
1702
|
}
|
|
1322
1703
|
}
|
|
1704
|
+
if (session.specFeedbacks && session.specFeedbacks.length > 0) {
|
|
1705
|
+
lines.push("## \u89C4\u683C\u8FED\u4EE3\u53CD\u9988");
|
|
1706
|
+
lines.push("");
|
|
1707
|
+
for (let i = 0; i < session.specFeedbacks.length; i++) {
|
|
1708
|
+
lines.push(`${i + 1}. ${session.specFeedbacks[i]}`);
|
|
1709
|
+
}
|
|
1710
|
+
lines.push("");
|
|
1711
|
+
lines.push("---");
|
|
1712
|
+
lines.push("");
|
|
1713
|
+
}
|
|
1323
1714
|
lines.push("## \u4EFB\u52A1\u5217\u8868");
|
|
1324
1715
|
lines.push("");
|
|
1325
1716
|
for (const item of session.specItems) {
|
|
@@ -1334,22 +1725,22 @@ function formatSpecFile(session) {
|
|
|
1334
1725
|
}
|
|
1335
1726
|
async function generateTests(workingDir, session, ctx) {
|
|
1336
1727
|
const testDir = path5.join(workingDir, "tests");
|
|
1337
|
-
await
|
|
1728
|
+
await fs9.mkdir(testDir, { recursive: true });
|
|
1338
1729
|
const testFiles = [];
|
|
1339
1730
|
if (ctx?.modelService) {
|
|
1340
1731
|
for (const scenario of session.bddScenarios) {
|
|
1341
1732
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1342
1733
|
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1343
|
-
const loader = new
|
|
1734
|
+
const loader = new LoadingIndicator2(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
1344
1735
|
loader.start();
|
|
1345
1736
|
try {
|
|
1346
1737
|
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
1347
|
-
await
|
|
1738
|
+
await fs9.writeFile(testPath, content, "utf-8");
|
|
1348
1739
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
1349
1740
|
loader.stop(chalk9.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
1350
1741
|
} catch {
|
|
1351
1742
|
const content = generateTestFile(scenario, session);
|
|
1352
|
-
await
|
|
1743
|
+
await fs9.writeFile(testPath, content, "utf-8");
|
|
1353
1744
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
1354
1745
|
loader.stop(chalk9.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
1355
1746
|
}
|
|
@@ -1359,7 +1750,7 @@ async function generateTests(workingDir, session, ctx) {
|
|
|
1359
1750
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
1360
1751
|
const testPath = path5.join(testDir, `${testName}.test.ts`);
|
|
1361
1752
|
const content = generateTestFile(scenario, session);
|
|
1362
|
-
await
|
|
1753
|
+
await fs9.writeFile(testPath, content, "utf-8");
|
|
1363
1754
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
1364
1755
|
}
|
|
1365
1756
|
}
|
|
@@ -1395,7 +1786,9 @@ ${scenario.scenarios.map((s) => `
|
|
|
1395
1786
|
{ role: "user", content: prompt2 }
|
|
1396
1787
|
], {
|
|
1397
1788
|
temperature: 0.3,
|
|
1398
|
-
maxTokens: 4e3
|
|
1789
|
+
maxTokens: 4e3,
|
|
1790
|
+
timeout: 18e4
|
|
1791
|
+
// 3分钟超时
|
|
1399
1792
|
});
|
|
1400
1793
|
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
1401
1794
|
if (codeMatch) {
|
|
@@ -1451,7 +1844,7 @@ function generateTestFile(scenario, session) {
|
|
|
1451
1844
|
async function archiveWorkflow(workingDir) {
|
|
1452
1845
|
if (!activeSession) return;
|
|
1453
1846
|
const archiveDir = path5.join(workingDir, "openspec", "spec");
|
|
1454
|
-
await
|
|
1847
|
+
await fs9.mkdir(archiveDir, { recursive: true });
|
|
1455
1848
|
const archivePath = path5.join(archiveDir, `${activeSession.id}.md`);
|
|
1456
1849
|
const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
|
|
1457
1850
|
|
|
@@ -1478,7 +1871,7 @@ ${activeSession.refinedRequirement}
|
|
|
1478
1871
|
|
|
1479
1872
|
${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
|
|
1480
1873
|
`;
|
|
1481
|
-
await
|
|
1874
|
+
await fs9.writeFile(archivePath, content, "utf-8");
|
|
1482
1875
|
}
|
|
1483
1876
|
function generateSessionId() {
|
|
1484
1877
|
const timestamp = Date.now().toString(36);
|
|
@@ -1494,7 +1887,10 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
|
1494
1887
|
const type = detectResourceType(url);
|
|
1495
1888
|
let content = "";
|
|
1496
1889
|
let analysis = "";
|
|
1890
|
+
const loader = new LoadingIndicator2(`\u83B7\u53D6 ${url.slice(0, 40)}...`);
|
|
1891
|
+
loader.start();
|
|
1497
1892
|
try {
|
|
1893
|
+
loader.update("\u6B63\u5728\u83B7\u53D6\u7F51\u9875\u5185\u5BB9");
|
|
1498
1894
|
const response = await fetch(url, {
|
|
1499
1895
|
headers: {
|
|
1500
1896
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
@@ -1505,11 +1901,14 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
|
1505
1901
|
}
|
|
1506
1902
|
content = await response.text();
|
|
1507
1903
|
if (ctx.modelService.getCurrentModel()) {
|
|
1904
|
+
loader.update("\u6B63\u5728\u5206\u6790\u5185\u5BB9");
|
|
1508
1905
|
analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
|
|
1509
1906
|
} else {
|
|
1510
1907
|
analysis = extractBasicInfo(content, type);
|
|
1511
1908
|
}
|
|
1909
|
+
loader.stop();
|
|
1512
1910
|
} catch (error) {
|
|
1911
|
+
loader.stop();
|
|
1513
1912
|
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
|
|
1514
1913
|
}
|
|
1515
1914
|
return { url, type, content: content.slice(0, 1e4), analysis };
|
|
@@ -1578,14 +1977,16 @@ ${content.slice(0, 8e3)}
|
|
|
1578
1977
|
\u6CE8\u610F\uFF1A\u8BF7\u4EE5 Markdown \u683C\u5F0F\u8F93\u51FA\uFF0C\u91CD\u70B9\u7A81\u51FA**\u4E1A\u52A1\u903B\u8F91**\u548C**\u529F\u80FD\u7279\u6027**\u3002
|
|
1579
1978
|
\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
|
|
1580
1979
|
`;
|
|
1581
|
-
const loader = new
|
|
1980
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
1582
1981
|
loader.start();
|
|
1583
1982
|
try {
|
|
1584
1983
|
const response = await ctx.modelService.sendMessage([
|
|
1585
1984
|
{ role: "user", content: prompt2 }
|
|
1586
1985
|
], {
|
|
1587
1986
|
temperature: 0.3,
|
|
1588
|
-
maxTokens: 4e3
|
|
1987
|
+
maxTokens: 4e3,
|
|
1988
|
+
timeout: 18e4
|
|
1989
|
+
// 3分钟超时
|
|
1589
1990
|
});
|
|
1590
1991
|
loader.stop(chalk9.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
1591
1992
|
return response.content;
|
|
@@ -1632,11 +2033,11 @@ function getActiveSession() {
|
|
|
1632
2033
|
function clearActiveSession() {
|
|
1633
2034
|
activeSession = null;
|
|
1634
2035
|
}
|
|
1635
|
-
var
|
|
2036
|
+
var LoadingIndicator2, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
1636
2037
|
var init_new = __esm({
|
|
1637
2038
|
"src/commands/new.ts"() {
|
|
1638
2039
|
init_esm_shims();
|
|
1639
|
-
|
|
2040
|
+
LoadingIndicator2 = class {
|
|
1640
2041
|
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1641
2042
|
frameIndex = 0;
|
|
1642
2043
|
interval = null;
|
|
@@ -1746,7 +2147,7 @@ var ConfigManager = class {
|
|
|
1746
2147
|
this.projectPath = projectPath;
|
|
1747
2148
|
this.configPath = path5.join(projectPath, ".sf-cli", "config.json");
|
|
1748
2149
|
try {
|
|
1749
|
-
const content = await
|
|
2150
|
+
const content = await fs9.readFile(this.configPath, "utf-8");
|
|
1750
2151
|
const loaded = JSON.parse(content);
|
|
1751
2152
|
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
1752
2153
|
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
@@ -1765,8 +2166,8 @@ var ConfigManager = class {
|
|
|
1765
2166
|
configToSave.apiKey = encrypted;
|
|
1766
2167
|
configToSave.apiKeyEncrypted = true;
|
|
1767
2168
|
}
|
|
1768
|
-
await
|
|
1769
|
-
await
|
|
2169
|
+
await fs9.mkdir(path5.dirname(this.configPath), { recursive: true });
|
|
2170
|
+
await fs9.writeFile(
|
|
1770
2171
|
this.configPath,
|
|
1771
2172
|
JSON.stringify(configToSave, null, 2),
|
|
1772
2173
|
"utf-8"
|
|
@@ -2001,9 +2402,10 @@ var BaseAdapter = class {
|
|
|
2001
2402
|
}
|
|
2002
2403
|
/**
|
|
2003
2404
|
* 获取超时时间
|
|
2405
|
+
* 默认 5 分钟,对于复杂的代码生成任务足够
|
|
2004
2406
|
*/
|
|
2005
2407
|
getTimeout() {
|
|
2006
|
-
return this.config?.timeout ||
|
|
2408
|
+
return this.config?.timeout || 3e5;
|
|
2007
2409
|
}
|
|
2008
2410
|
/**
|
|
2009
2411
|
* 获取重试次数
|
|
@@ -2863,8 +3265,8 @@ var ModelService = class {
|
|
|
2863
3265
|
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
2864
3266
|
const totalPath = path5.join(this.statsPath, "total.json");
|
|
2865
3267
|
const [daily, total] = await Promise.all([
|
|
2866
|
-
|
|
2867
|
-
|
|
3268
|
+
fs9.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
3269
|
+
fs9.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
2868
3270
|
]);
|
|
2869
3271
|
const dailyData = JSON.parse(daily);
|
|
2870
3272
|
const totalData = JSON.parse(total);
|
|
@@ -2877,12 +3279,12 @@ var ModelService = class {
|
|
|
2877
3279
|
async saveStats() {
|
|
2878
3280
|
if (!this.statsPath) return;
|
|
2879
3281
|
try {
|
|
2880
|
-
await
|
|
3282
|
+
await fs9.mkdir(this.statsPath, { recursive: true });
|
|
2881
3283
|
const dailyPath = path5.join(this.statsPath, "daily.json");
|
|
2882
3284
|
const totalPath = path5.join(this.statsPath, "total.json");
|
|
2883
3285
|
await Promise.all([
|
|
2884
|
-
|
|
2885
|
-
|
|
3286
|
+
fs9.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
3287
|
+
fs9.writeFile(totalPath, JSON.stringify({
|
|
2886
3288
|
totalInput: this.stats.totalInput,
|
|
2887
3289
|
totalOutput: this.stats.totalOutput
|
|
2888
3290
|
}))
|
|
@@ -4591,15 +4993,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
4591
4993
|
devStandards: ""
|
|
4592
4994
|
};
|
|
4593
4995
|
try {
|
|
4594
|
-
context.agentsMd = await
|
|
4996
|
+
context.agentsMd = await fs9.readFile(path5.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
4595
4997
|
} catch {
|
|
4596
4998
|
}
|
|
4597
4999
|
try {
|
|
4598
|
-
context.configYaml = await
|
|
5000
|
+
context.configYaml = await fs9.readFile(path5.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
4599
5001
|
} catch {
|
|
4600
5002
|
}
|
|
4601
5003
|
try {
|
|
4602
|
-
context.devStandards = await
|
|
5004
|
+
context.devStandards = await fs9.readFile(path5.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
4603
5005
|
} catch {
|
|
4604
5006
|
}
|
|
4605
5007
|
return context;
|
|
@@ -5562,19 +5964,19 @@ var WorkflowEngine = class {
|
|
|
5562
5964
|
async loadProjectContext() {
|
|
5563
5965
|
const agentsMdPath = path5.join(this.projectPath, "AGENTS.md");
|
|
5564
5966
|
try {
|
|
5565
|
-
this.projectContext = await
|
|
5967
|
+
this.projectContext = await fs9.readFile(agentsMdPath, "utf-8");
|
|
5566
5968
|
} catch {
|
|
5567
5969
|
this.projectContext = "";
|
|
5568
5970
|
}
|
|
5569
5971
|
const configPath = path5.join(this.openspecPath, "config.yaml");
|
|
5570
5972
|
try {
|
|
5571
|
-
this.projectConfig = await
|
|
5973
|
+
this.projectConfig = await fs9.readFile(configPath, "utf-8");
|
|
5572
5974
|
} catch {
|
|
5573
5975
|
this.projectConfig = "";
|
|
5574
5976
|
}
|
|
5575
5977
|
const devstandedPath = path5.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
5576
5978
|
try {
|
|
5577
|
-
this.devStandards = await
|
|
5979
|
+
this.devStandards = await fs9.readFile(devstandedPath, "utf-8");
|
|
5578
5980
|
} catch {
|
|
5579
5981
|
this.devStandards = "";
|
|
5580
5982
|
}
|
|
@@ -5603,7 +6005,7 @@ var WorkflowEngine = class {
|
|
|
5603
6005
|
const specPath = this.getSpecFilePath();
|
|
5604
6006
|
if (!specPath) return false;
|
|
5605
6007
|
try {
|
|
5606
|
-
await
|
|
6008
|
+
await fs9.access(specPath);
|
|
5607
6009
|
return true;
|
|
5608
6010
|
} catch {
|
|
5609
6011
|
return false;
|
|
@@ -5837,11 +6239,11 @@ var WorkflowEngine = class {
|
|
|
5837
6239
|
const workflows = [];
|
|
5838
6240
|
const changesDir = path5.join(this.openspecPath, "changes");
|
|
5839
6241
|
try {
|
|
5840
|
-
const files = await
|
|
6242
|
+
const files = await fs9.readdir(changesDir);
|
|
5841
6243
|
for (const file of files) {
|
|
5842
6244
|
if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
|
|
5843
6245
|
const filePath = path5.join(changesDir, file);
|
|
5844
|
-
const content = await
|
|
6246
|
+
const content = await fs9.readFile(filePath, "utf-8");
|
|
5845
6247
|
const state = this.parseChangeRecord(content);
|
|
5846
6248
|
if (state && state.status === "running") {
|
|
5847
6249
|
workflows.push(state);
|
|
@@ -5892,7 +6294,7 @@ var WorkflowEngine = class {
|
|
|
5892
6294
|
}
|
|
5893
6295
|
const statePath = path5.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
|
|
5894
6296
|
try {
|
|
5895
|
-
const content = await
|
|
6297
|
+
const content = await fs9.readFile(statePath, "utf-8");
|
|
5896
6298
|
this.state = JSON.parse(content, (key, value) => {
|
|
5897
6299
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5898
6300
|
return new Date(value);
|
|
@@ -5905,7 +6307,7 @@ var WorkflowEngine = class {
|
|
|
5905
6307
|
const changesDir = path5.join(this.openspecPath, "changes");
|
|
5906
6308
|
const changeFile = path5.join(changesDir, `${changeId}.md`);
|
|
5907
6309
|
try {
|
|
5908
|
-
const content = await
|
|
6310
|
+
const content = await fs9.readFile(changeFile, "utf-8");
|
|
5909
6311
|
const parsed = this.parseChangeRecord(content);
|
|
5910
6312
|
if (parsed && parsed.status === "running") {
|
|
5911
6313
|
this.state = parsed;
|
|
@@ -5970,8 +6372,8 @@ var WorkflowEngine = class {
|
|
|
5970
6372
|
const archiveDir = path5.join(changesDir, "archive");
|
|
5971
6373
|
const changeFile = path5.join(changesDir, `${changeId}.md`);
|
|
5972
6374
|
const archiveFile = path5.join(archiveDir, `${changeId}.md`);
|
|
5973
|
-
await
|
|
5974
|
-
await
|
|
6375
|
+
await fs9.mkdir(archiveDir, { recursive: true });
|
|
6376
|
+
await fs9.rename(changeFile, archiveFile).catch(() => {
|
|
5975
6377
|
});
|
|
5976
6378
|
this.state = null;
|
|
5977
6379
|
this.snapshots.clear();
|
|
@@ -5997,15 +6399,15 @@ var WorkflowEngine = class {
|
|
|
5997
6399
|
const archiveDir = path5.join(changesDir, "archive");
|
|
5998
6400
|
const specDir = path5.join(this.openspecPath, "spec");
|
|
5999
6401
|
const statesDir = path5.join(this.openspecPath, ".workflow-states");
|
|
6000
|
-
await
|
|
6001
|
-
await
|
|
6002
|
-
await
|
|
6402
|
+
await fs9.mkdir(archiveDir, { recursive: true });
|
|
6403
|
+
await fs9.mkdir(specDir, { recursive: true });
|
|
6404
|
+
await fs9.mkdir(statesDir, { recursive: true });
|
|
6003
6405
|
}
|
|
6004
6406
|
async restoreState() {
|
|
6005
6407
|
const activePath = path5.join(this.openspecPath, ".workflow-active.json");
|
|
6006
6408
|
let activeId = null;
|
|
6007
6409
|
try {
|
|
6008
|
-
const activeContent = await
|
|
6410
|
+
const activeContent = await fs9.readFile(activePath, "utf-8");
|
|
6009
6411
|
const activeData = JSON.parse(activeContent);
|
|
6010
6412
|
activeId = activeData.activeId;
|
|
6011
6413
|
} catch {
|
|
@@ -6013,7 +6415,7 @@ var WorkflowEngine = class {
|
|
|
6013
6415
|
if (activeId) {
|
|
6014
6416
|
const statePath = path5.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
6015
6417
|
try {
|
|
6016
|
-
const content = await
|
|
6418
|
+
const content = await fs9.readFile(statePath, "utf-8");
|
|
6017
6419
|
this.state = JSON.parse(content, (key, value) => {
|
|
6018
6420
|
if (key.endsWith("At") && typeof value === "string") {
|
|
6019
6421
|
return new Date(value);
|
|
@@ -6026,7 +6428,7 @@ var WorkflowEngine = class {
|
|
|
6026
6428
|
}
|
|
6027
6429
|
const oldStatePath = path5.join(this.openspecPath, ".workflow-state.json");
|
|
6028
6430
|
try {
|
|
6029
|
-
const content = await
|
|
6431
|
+
const content = await fs9.readFile(oldStatePath, "utf-8");
|
|
6030
6432
|
this.state = JSON.parse(content, (key, value) => {
|
|
6031
6433
|
if (key.endsWith("At") && typeof value === "string") {
|
|
6032
6434
|
return new Date(value);
|
|
@@ -6035,7 +6437,7 @@ var WorkflowEngine = class {
|
|
|
6035
6437
|
});
|
|
6036
6438
|
if (this.state) {
|
|
6037
6439
|
await this.saveState();
|
|
6038
|
-
await
|
|
6440
|
+
await fs9.unlink(oldStatePath).catch(() => {
|
|
6039
6441
|
});
|
|
6040
6442
|
}
|
|
6041
6443
|
} catch (e) {
|
|
@@ -6049,16 +6451,16 @@ var WorkflowEngine = class {
|
|
|
6049
6451
|
async saveState() {
|
|
6050
6452
|
if (!this.state) return;
|
|
6051
6453
|
const statesDir = path5.join(this.openspecPath, ".workflow-states");
|
|
6052
|
-
await
|
|
6454
|
+
await fs9.mkdir(statesDir, { recursive: true });
|
|
6053
6455
|
const statePath = path5.join(statesDir, `${this.state.id}.json`);
|
|
6054
|
-
await
|
|
6456
|
+
await fs9.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
6055
6457
|
const activePath = path5.join(this.openspecPath, ".workflow-active.json");
|
|
6056
|
-
await
|
|
6458
|
+
await fs9.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
6057
6459
|
}
|
|
6058
6460
|
async restoreSnapshots() {
|
|
6059
6461
|
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
6060
6462
|
try {
|
|
6061
|
-
const content = await
|
|
6463
|
+
const content = await fs9.readFile(snapshotsPath, "utf-8");
|
|
6062
6464
|
const data = JSON.parse(content, (key, value) => {
|
|
6063
6465
|
if (key === "timestamp" && typeof value === "string") {
|
|
6064
6466
|
return new Date(value);
|
|
@@ -6075,7 +6477,7 @@ var WorkflowEngine = class {
|
|
|
6075
6477
|
async saveSnapshots() {
|
|
6076
6478
|
const snapshotsPath = path5.join(this.openspecPath, ".workflow-snapshots.json");
|
|
6077
6479
|
const data = Array.from(this.snapshots.values());
|
|
6078
|
-
await
|
|
6480
|
+
await fs9.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
6079
6481
|
}
|
|
6080
6482
|
async createSnapshot() {
|
|
6081
6483
|
if (!this.state) return;
|
|
@@ -6096,7 +6498,7 @@ var WorkflowEngine = class {
|
|
|
6096
6498
|
async createChangeRecord() {
|
|
6097
6499
|
if (!this.state) return;
|
|
6098
6500
|
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
6099
|
-
await
|
|
6501
|
+
await fs9.writeFile(changePath, this.formatChangeRecord());
|
|
6100
6502
|
}
|
|
6101
6503
|
async updateChangeRecord(status) {
|
|
6102
6504
|
if (!this.state) return;
|
|
@@ -6104,7 +6506,7 @@ var WorkflowEngine = class {
|
|
|
6104
6506
|
this.state.status = status;
|
|
6105
6507
|
}
|
|
6106
6508
|
const changePath = path5.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
6107
|
-
await
|
|
6509
|
+
await fs9.writeFile(changePath, this.formatChangeRecord());
|
|
6108
6510
|
}
|
|
6109
6511
|
formatChangeRecord() {
|
|
6110
6512
|
if (!this.state) return "";
|
|
@@ -6168,7 +6570,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
|
|
|
6168
6570
|
|
|
6169
6571
|
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
6170
6572
|
`;
|
|
6171
|
-
await
|
|
6573
|
+
await fs9.writeFile(specPath, content);
|
|
6172
6574
|
}
|
|
6173
6575
|
};
|
|
6174
6576
|
var ConfirmationRequiredError = class extends Error {
|
|
@@ -6217,11 +6619,11 @@ var NormsManager = class {
|
|
|
6217
6619
|
const files = await this.collectProjectFiles(projectPath);
|
|
6218
6620
|
for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
|
|
6219
6621
|
try {
|
|
6220
|
-
const stats = await
|
|
6622
|
+
const stats = await fs9.stat(filePath);
|
|
6221
6623
|
if (stats.size > MAX_FILE_SIZE) {
|
|
6222
6624
|
continue;
|
|
6223
6625
|
}
|
|
6224
|
-
const content = await
|
|
6626
|
+
const content = await fs9.readFile(filePath, "utf-8");
|
|
6225
6627
|
const patterns = await this.learnFromFile(filePath, content);
|
|
6226
6628
|
result.patternsFound += patterns.length;
|
|
6227
6629
|
result.filesScanned++;
|
|
@@ -6847,7 +7249,7 @@ ${response}`;
|
|
|
6847
7249
|
const files = [];
|
|
6848
7250
|
async function scan(dir) {
|
|
6849
7251
|
try {
|
|
6850
|
-
const entries = await
|
|
7252
|
+
const entries = await fs9.readdir(dir, { withFileTypes: true });
|
|
6851
7253
|
for (const entry of entries) {
|
|
6852
7254
|
if (entry.isDirectory()) {
|
|
6853
7255
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
@@ -6890,8 +7292,8 @@ ${response}`;
|
|
|
6890
7292
|
* 确保规范目录存在
|
|
6891
7293
|
*/
|
|
6892
7294
|
async ensureNormsDir() {
|
|
6893
|
-
await
|
|
6894
|
-
await
|
|
7295
|
+
await fs9.mkdir(this.config.normsDir, { recursive: true });
|
|
7296
|
+
await fs9.mkdir(path5.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
6895
7297
|
}
|
|
6896
7298
|
/**
|
|
6897
7299
|
* 加载已有规范
|
|
@@ -6899,7 +7301,7 @@ ${response}`;
|
|
|
6899
7301
|
async loadStandards() {
|
|
6900
7302
|
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
6901
7303
|
try {
|
|
6902
|
-
const content = await
|
|
7304
|
+
const content = await fs9.readFile(standardsPath, "utf-8");
|
|
6903
7305
|
const data = JSON.parse(content);
|
|
6904
7306
|
for (const standard of data) {
|
|
6905
7307
|
this.standards.set(standard.id, {
|
|
@@ -6917,7 +7319,7 @@ ${response}`;
|
|
|
6917
7319
|
async loadWeights() {
|
|
6918
7320
|
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
6919
7321
|
try {
|
|
6920
|
-
const content = await
|
|
7322
|
+
const content = await fs9.readFile(weightsPath, "utf-8");
|
|
6921
7323
|
const data = JSON.parse(content);
|
|
6922
7324
|
for (const weight of data) {
|
|
6923
7325
|
this.weights.set(weight.standardId, {
|
|
@@ -6933,7 +7335,7 @@ ${response}`;
|
|
|
6933
7335
|
*/
|
|
6934
7336
|
async saveStandards() {
|
|
6935
7337
|
const standardsPath = path5.join(this.config.normsDir, "patterns.json");
|
|
6936
|
-
await
|
|
7338
|
+
await fs9.writeFile(
|
|
6937
7339
|
standardsPath,
|
|
6938
7340
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
6939
7341
|
"utf-8"
|
|
@@ -6944,7 +7346,7 @@ ${response}`;
|
|
|
6944
7346
|
*/
|
|
6945
7347
|
async saveWeights() {
|
|
6946
7348
|
const weightsPath = path5.join(this.config.normsDir, "weights.json");
|
|
6947
|
-
await
|
|
7349
|
+
await fs9.writeFile(
|
|
6948
7350
|
weightsPath,
|
|
6949
7351
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
6950
7352
|
"utf-8"
|
|
@@ -6972,7 +7374,7 @@ ${response}`;
|
|
|
6972
7374
|
*/
|
|
6973
7375
|
async saveWeeklyReport(weekId, report) {
|
|
6974
7376
|
const reportPath = path5.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
6975
|
-
await
|
|
7377
|
+
await fs9.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
6976
7378
|
}
|
|
6977
7379
|
/**
|
|
6978
7380
|
* 更新 devstanded.md
|
|
@@ -6981,7 +7383,7 @@ ${response}`;
|
|
|
6981
7383
|
const standards = this.getEffectiveStandards();
|
|
6982
7384
|
const content = this.generateDevStandedMd(standards);
|
|
6983
7385
|
const mdPath = path5.join(this.config.normsDir, "devstanded.md");
|
|
6984
|
-
await
|
|
7386
|
+
await fs9.writeFile(mdPath, content, "utf-8");
|
|
6985
7387
|
}
|
|
6986
7388
|
/**
|
|
6987
7389
|
* 生成 devstanded.md 内容
|
|
@@ -7373,14 +7775,14 @@ ${summary}`,
|
|
|
7373
7775
|
async persist() {
|
|
7374
7776
|
if (!this.persistPath) return;
|
|
7375
7777
|
const dir = path5.dirname(this.persistPath);
|
|
7376
|
-
await
|
|
7778
|
+
await fs9.mkdir(dir, { recursive: true });
|
|
7377
7779
|
const data = {
|
|
7378
7780
|
messages: this.state.messages,
|
|
7379
7781
|
totalTokens: this.state.totalTokens,
|
|
7380
7782
|
limit: this.state.limit,
|
|
7381
7783
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7382
7784
|
};
|
|
7383
|
-
await
|
|
7785
|
+
await fs9.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7384
7786
|
}
|
|
7385
7787
|
/**
|
|
7386
7788
|
* 加载持久化的上下文
|
|
@@ -7388,7 +7790,7 @@ ${summary}`,
|
|
|
7388
7790
|
async loadPersistedContext() {
|
|
7389
7791
|
if (!this.persistPath) return;
|
|
7390
7792
|
try {
|
|
7391
|
-
const content = await
|
|
7793
|
+
const content = await fs9.readFile(this.persistPath, "utf-8");
|
|
7392
7794
|
const data = JSON.parse(content);
|
|
7393
7795
|
this.state.messages = data.messages || [];
|
|
7394
7796
|
this.state.totalTokens = data.totalTokens || 0;
|
|
@@ -7605,7 +8007,7 @@ async function handleInit(args, ctx) {
|
|
|
7605
8007
|
async function initProject(options = {}, workingDir) {
|
|
7606
8008
|
const cwd = workingDir || process.cwd();
|
|
7607
8009
|
try {
|
|
7608
|
-
const stats = await
|
|
8010
|
+
const stats = await fs9.stat(cwd);
|
|
7609
8011
|
if (!stats.isDirectory()) {
|
|
7610
8012
|
return {
|
|
7611
8013
|
output: chalk9.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -7637,7 +8039,7 @@ async function initProject(options = {}, workingDir) {
|
|
|
7637
8039
|
await normsManager.initialize();
|
|
7638
8040
|
const scanResult = await normsManager.scanProject(cwd);
|
|
7639
8041
|
const agentsContent = generateAgentsMd(projectInfo, scanResult);
|
|
7640
|
-
await
|
|
8042
|
+
await fs9.writeFile(agentsMdPath, agentsContent, "utf-8");
|
|
7641
8043
|
await generateOpenSpecConfig(openspecDir, projectInfo);
|
|
7642
8044
|
return {
|
|
7643
8045
|
output: chalk9.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
|
|
@@ -7678,7 +8080,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
7678
8080
|
"logs"
|
|
7679
8081
|
];
|
|
7680
8082
|
for (const dir of dirs) {
|
|
7681
|
-
await
|
|
8083
|
+
await fs9.mkdir(path5.join(basePath, dir), { recursive: true });
|
|
7682
8084
|
}
|
|
7683
8085
|
const config = {
|
|
7684
8086
|
version: "1.0.0",
|
|
@@ -7686,22 +8088,22 @@ async function createSfCliDirectory(basePath) {
|
|
|
7686
8088
|
yolo: false,
|
|
7687
8089
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7688
8090
|
};
|
|
7689
|
-
await
|
|
8091
|
+
await fs9.writeFile(
|
|
7690
8092
|
path5.join(basePath, "config.json"),
|
|
7691
8093
|
JSON.stringify(config, null, 2),
|
|
7692
8094
|
"utf-8"
|
|
7693
8095
|
);
|
|
7694
|
-
await
|
|
8096
|
+
await fs9.writeFile(
|
|
7695
8097
|
path5.join(basePath, "agents", "registry.json"),
|
|
7696
8098
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
7697
8099
|
"utf-8"
|
|
7698
8100
|
);
|
|
7699
|
-
await
|
|
8101
|
+
await fs9.writeFile(
|
|
7700
8102
|
path5.join(basePath, "skills", "registry.json"),
|
|
7701
8103
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
7702
8104
|
"utf-8"
|
|
7703
8105
|
);
|
|
7704
|
-
await
|
|
8106
|
+
await fs9.writeFile(
|
|
7705
8107
|
path5.join(basePath, "health", "health.md"),
|
|
7706
8108
|
generateHealthTemplate(),
|
|
7707
8109
|
"utf-8"
|
|
@@ -7711,8 +8113,8 @@ async function createOpenSpecDirectory(basePath) {
|
|
|
7711
8113
|
const changesDir = path5.join(basePath, "changes");
|
|
7712
8114
|
const archiveDir = path5.join(changesDir, "archive");
|
|
7713
8115
|
const specDir = path5.join(basePath, "spec");
|
|
7714
|
-
await
|
|
7715
|
-
await
|
|
8116
|
+
await fs9.mkdir(archiveDir, { recursive: true });
|
|
8117
|
+
await fs9.mkdir(specDir, { recursive: true });
|
|
7716
8118
|
}
|
|
7717
8119
|
async function analyzeProject(cwd) {
|
|
7718
8120
|
const result = {
|
|
@@ -7737,7 +8139,7 @@ async function analyzeProject(cwd) {
|
|
|
7737
8139
|
};
|
|
7738
8140
|
const pkgPath = path5.join(cwd, "package.json");
|
|
7739
8141
|
try {
|
|
7740
|
-
const pkgContent = await
|
|
8142
|
+
const pkgContent = await fs9.readFile(pkgPath, "utf-8");
|
|
7741
8143
|
const pkg = JSON.parse(pkgContent);
|
|
7742
8144
|
result.name = pkg.name || result.name;
|
|
7743
8145
|
result.dependencies = pkg.dependencies || {};
|
|
@@ -7820,7 +8222,7 @@ async function analyzeDirectoryStructure(cwd) {
|
|
|
7820
8222
|
others: []
|
|
7821
8223
|
};
|
|
7822
8224
|
try {
|
|
7823
|
-
const entries = await
|
|
8225
|
+
const entries = await fs9.readdir(cwd, { withFileTypes: true });
|
|
7824
8226
|
for (const entry of entries) {
|
|
7825
8227
|
if (!entry.isDirectory()) continue;
|
|
7826
8228
|
const name = entry.name;
|
|
@@ -7865,7 +8267,7 @@ async function findEntryPoints(cwd) {
|
|
|
7865
8267
|
}
|
|
7866
8268
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
7867
8269
|
const analysisPath = path5.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
7868
|
-
await
|
|
8270
|
+
await fs9.writeFile(
|
|
7869
8271
|
analysisPath,
|
|
7870
8272
|
JSON.stringify(analysis, null, 2),
|
|
7871
8273
|
"utf-8"
|
|
@@ -7906,7 +8308,7 @@ architecture:
|
|
|
7906
8308
|
# \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
|
|
7907
8309
|
standards: []
|
|
7908
8310
|
`;
|
|
7909
|
-
await
|
|
8311
|
+
await fs9.writeFile(configPath, content, "utf-8");
|
|
7910
8312
|
}
|
|
7911
8313
|
function generateAgentsMd(info, scanResult) {
|
|
7912
8314
|
const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
|
|
@@ -8028,7 +8430,7 @@ function generateHealthTemplate() {
|
|
|
8028
8430
|
}
|
|
8029
8431
|
async function fileExists(filePath) {
|
|
8030
8432
|
try {
|
|
8031
|
-
await
|
|
8433
|
+
await fs9.access(filePath);
|
|
8032
8434
|
return true;
|
|
8033
8435
|
} catch {
|
|
8034
8436
|
return false;
|
|
@@ -8193,6 +8595,36 @@ ${chalk9.yellow("\u793A\u4F8B:")}
|
|
|
8193
8595
|
|
|
8194
8596
|
// src/commands/model.ts
|
|
8195
8597
|
init_esm_shims();
|
|
8598
|
+
var LoadingIndicator = class {
|
|
8599
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8600
|
+
frameIndex = 0;
|
|
8601
|
+
interval = null;
|
|
8602
|
+
message;
|
|
8603
|
+
constructor(message) {
|
|
8604
|
+
this.message = message;
|
|
8605
|
+
}
|
|
8606
|
+
start() {
|
|
8607
|
+
process.stdout.write("\x1B[?25l");
|
|
8608
|
+
this.interval = setInterval(() => {
|
|
8609
|
+
const frame = this.frames[this.frameIndex];
|
|
8610
|
+
process.stdout.write(`\r${chalk9.cyan(frame)} ${this.message}...`);
|
|
8611
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
8612
|
+
}, 80);
|
|
8613
|
+
}
|
|
8614
|
+
stop(finalMessage) {
|
|
8615
|
+
if (this.interval) {
|
|
8616
|
+
clearInterval(this.interval);
|
|
8617
|
+
this.interval = null;
|
|
8618
|
+
}
|
|
8619
|
+
process.stdout.write("\x1B[?25h");
|
|
8620
|
+
if (finalMessage) {
|
|
8621
|
+
process.stdout.write(`\r${finalMessage}
|
|
8622
|
+
`);
|
|
8623
|
+
} else {
|
|
8624
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
8625
|
+
}
|
|
8626
|
+
}
|
|
8627
|
+
};
|
|
8196
8628
|
async function handleModel(args, ctx) {
|
|
8197
8629
|
const subCommand = args[0];
|
|
8198
8630
|
const configManager = ctx.configManager;
|
|
@@ -8292,9 +8724,12 @@ async function verifyCurrentModel(configManager, modelService) {
|
|
|
8292
8724
|
output: chalk9.red(`\u2717 \u672A\u77E5\u6A21\u578B: ${currentModel}`)
|
|
8293
8725
|
};
|
|
8294
8726
|
}
|
|
8727
|
+
const loader = new LoadingIndicator(`\u9A8C\u8BC1 ${modelInfo.name} API Key`);
|
|
8728
|
+
loader.start();
|
|
8295
8729
|
try {
|
|
8296
8730
|
const adapter = createAdapter(modelInfo.provider);
|
|
8297
8731
|
const isValid = await adapter.validateApiKey(apiKey);
|
|
8732
|
+
loader.stop();
|
|
8298
8733
|
if (isValid) {
|
|
8299
8734
|
return {
|
|
8300
8735
|
output: chalk9.green(`\u2713 API Key \u9A8C\u8BC1\u6210\u529F
|
|
@@ -8306,6 +8741,7 @@ async function verifyCurrentModel(configManager, modelService) {
|
|
|
8306
8741
|
};
|
|
8307
8742
|
}
|
|
8308
8743
|
} catch (error) {
|
|
8744
|
+
loader.stop();
|
|
8309
8745
|
return {
|
|
8310
8746
|
output: chalk9.red(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${error.message}`)
|
|
8311
8747
|
};
|
|
@@ -9429,9 +9865,9 @@ async function handleFileReference(filePath, ctx) {
|
|
|
9429
9865
|
const cwd = ctx.options.workingDirectory;
|
|
9430
9866
|
const absolutePath = path5.isAbsolute(filePath) ? filePath : path5.join(cwd, filePath);
|
|
9431
9867
|
try {
|
|
9432
|
-
const stats = await
|
|
9868
|
+
const stats = await fs9.stat(absolutePath);
|
|
9433
9869
|
if (stats.isDirectory()) {
|
|
9434
|
-
const files = await
|
|
9870
|
+
const files = await fs9.readdir(absolutePath);
|
|
9435
9871
|
return {
|
|
9436
9872
|
output: chalk9.cyan(`\u{1F4C1} ${filePath}/`) + chalk9.gray(`
|
|
9437
9873
|
${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9.gray(`
|
|
@@ -9439,7 +9875,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9.gray(`
|
|
|
9439
9875
|
contextUsed: 0
|
|
9440
9876
|
};
|
|
9441
9877
|
}
|
|
9442
|
-
const content = await
|
|
9878
|
+
const content = await fs9.readFile(absolutePath, "utf-8");
|
|
9443
9879
|
const lines = content.split("\n");
|
|
9444
9880
|
ctx.contextManager.addMessage({
|
|
9445
9881
|
role: "user",
|
|
@@ -9517,6 +9953,36 @@ async function executeShell(command, ctx) {
|
|
|
9517
9953
|
// src/commands/natural.ts
|
|
9518
9954
|
init_esm_shims();
|
|
9519
9955
|
init_new();
|
|
9956
|
+
var LoadingIndicator3 = class {
|
|
9957
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
9958
|
+
frameIndex = 0;
|
|
9959
|
+
interval = null;
|
|
9960
|
+
message;
|
|
9961
|
+
constructor(message) {
|
|
9962
|
+
this.message = message;
|
|
9963
|
+
}
|
|
9964
|
+
start() {
|
|
9965
|
+
process.stdout.write("\x1B[?25l");
|
|
9966
|
+
this.interval = setInterval(() => {
|
|
9967
|
+
const frame = this.frames[this.frameIndex];
|
|
9968
|
+
process.stdout.write(`\r${chalk9.cyan(frame)} ${this.message}...`);
|
|
9969
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
9970
|
+
}, 80);
|
|
9971
|
+
}
|
|
9972
|
+
stop(finalMessage) {
|
|
9973
|
+
if (this.interval) {
|
|
9974
|
+
clearInterval(this.interval);
|
|
9975
|
+
this.interval = null;
|
|
9976
|
+
}
|
|
9977
|
+
process.stdout.write("\x1B[?25h");
|
|
9978
|
+
if (finalMessage) {
|
|
9979
|
+
process.stdout.write(`\r${finalMessage}
|
|
9980
|
+
`);
|
|
9981
|
+
} else {
|
|
9982
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
9983
|
+
}
|
|
9984
|
+
}
|
|
9985
|
+
};
|
|
9520
9986
|
async function handleNaturalLanguage(input, ctx) {
|
|
9521
9987
|
const trimmedInput = input.trim();
|
|
9522
9988
|
const session = getActiveSession();
|
|
@@ -9536,6 +10002,8 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
9536
10002
|
contextUsed: 0
|
|
9537
10003
|
};
|
|
9538
10004
|
}
|
|
10005
|
+
const loader = new LoadingIndicator3("AI \u601D\u8003\u4E2D");
|
|
10006
|
+
loader.start();
|
|
9539
10007
|
try {
|
|
9540
10008
|
const response = await ctx.modelService.sendMessage(
|
|
9541
10009
|
[
|
|
@@ -9553,6 +10021,7 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
9553
10021
|
maxTokens: 2e3
|
|
9554
10022
|
}
|
|
9555
10023
|
);
|
|
10024
|
+
loader.stop();
|
|
9556
10025
|
ctx.contextManager.addMessage({
|
|
9557
10026
|
role: "user",
|
|
9558
10027
|
content: trimmedInput
|
|
@@ -9566,6 +10035,7 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
9566
10035
|
contextUsed: response.usage?.totalTokens || 0
|
|
9567
10036
|
};
|
|
9568
10037
|
} catch (error) {
|
|
10038
|
+
loader.stop();
|
|
9569
10039
|
const errorMessage = error.message;
|
|
9570
10040
|
if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
|
|
9571
10041
|
return {
|
|
@@ -9834,7 +10304,7 @@ var Completer = class {
|
|
|
9834
10304
|
prefix = filePath.slice(lastSlash + 1);
|
|
9835
10305
|
}
|
|
9836
10306
|
try {
|
|
9837
|
-
const entries = await
|
|
10307
|
+
const entries = await fs9.readdir(dirPath, { withFileTypes: true });
|
|
9838
10308
|
const matches = [];
|
|
9839
10309
|
for (const entry of entries) {
|
|
9840
10310
|
if (entry.name.startsWith(prefix)) {
|