@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/cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var enquirer = require('enquirer');
|
|
|
7
7
|
var child_process = require('child_process');
|
|
8
8
|
var fs7 = require('fs');
|
|
9
9
|
var path5 = require('path');
|
|
10
|
-
var
|
|
10
|
+
var fs4 = require('fs/promises');
|
|
11
11
|
var commander = require('commander');
|
|
12
12
|
var readline = require('readline');
|
|
13
13
|
require('uuid');
|
|
@@ -39,7 +39,7 @@ function _interopNamespace(e) {
|
|
|
39
39
|
var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
|
|
40
40
|
var fs7__namespace = /*#__PURE__*/_interopNamespace(fs7);
|
|
41
41
|
var path5__namespace = /*#__PURE__*/_interopNamespace(path5);
|
|
42
|
-
var
|
|
42
|
+
var fs4__namespace = /*#__PURE__*/_interopNamespace(fs4);
|
|
43
43
|
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
44
44
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
45
45
|
var os__namespace = /*#__PURE__*/_interopNamespace(os);
|
|
@@ -244,9 +244,10 @@ var init_base = __esm({
|
|
|
244
244
|
}
|
|
245
245
|
/**
|
|
246
246
|
* 获取超时时间
|
|
247
|
+
* 默认 5 分钟,对于复杂的代码生成任务足够
|
|
247
248
|
*/
|
|
248
249
|
getTimeout() {
|
|
249
|
-
return this.config?.timeout ||
|
|
250
|
+
return this.config?.timeout || 3e5;
|
|
250
251
|
}
|
|
251
252
|
/**
|
|
252
253
|
* 获取重试次数
|
|
@@ -1057,9 +1058,12 @@ async function verifyCurrentModel(configManager, modelService) {
|
|
|
1057
1058
|
output: chalk9__default.default.red(`\u2717 \u672A\u77E5\u6A21\u578B: ${currentModel}`)
|
|
1058
1059
|
};
|
|
1059
1060
|
}
|
|
1061
|
+
const loader = new LoadingIndicator(`\u9A8C\u8BC1 ${modelInfo.name} API Key`);
|
|
1062
|
+
loader.start();
|
|
1060
1063
|
try {
|
|
1061
1064
|
const adapter = createAdapter(modelInfo.provider);
|
|
1062
1065
|
const isValid = await adapter.validateApiKey(apiKey);
|
|
1066
|
+
loader.stop();
|
|
1063
1067
|
if (isValid) {
|
|
1064
1068
|
return {
|
|
1065
1069
|
output: chalk9__default.default.green(`\u2713 API Key \u9A8C\u8BC1\u6210\u529F
|
|
@@ -1071,6 +1075,7 @@ async function verifyCurrentModel(configManager, modelService) {
|
|
|
1071
1075
|
};
|
|
1072
1076
|
}
|
|
1073
1077
|
} catch (error) {
|
|
1078
|
+
loader.stop();
|
|
1074
1079
|
return {
|
|
1075
1080
|
output: chalk9__default.default.red(`\u2717 \u9A8C\u8BC1\u5931\u8D25: ${error.message}`)
|
|
1076
1081
|
};
|
|
@@ -1192,12 +1197,42 @@ function createSpinner(message) {
|
|
|
1192
1197
|
stop: () => process.stdout.write(" ".repeat(message.length + 10) + "\r")
|
|
1193
1198
|
};
|
|
1194
1199
|
}
|
|
1195
|
-
var model_default;
|
|
1200
|
+
var LoadingIndicator, model_default;
|
|
1196
1201
|
var init_model2 = __esm({
|
|
1197
1202
|
"src/commands/model.ts"() {
|
|
1198
1203
|
init_cjs_shims();
|
|
1199
1204
|
init_adapters();
|
|
1200
1205
|
init_model();
|
|
1206
|
+
LoadingIndicator = class {
|
|
1207
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1208
|
+
frameIndex = 0;
|
|
1209
|
+
interval = null;
|
|
1210
|
+
message;
|
|
1211
|
+
constructor(message) {
|
|
1212
|
+
this.message = message;
|
|
1213
|
+
}
|
|
1214
|
+
start() {
|
|
1215
|
+
process.stdout.write("\x1B[?25l");
|
|
1216
|
+
this.interval = setInterval(() => {
|
|
1217
|
+
const frame = this.frames[this.frameIndex];
|
|
1218
|
+
process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
|
|
1219
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
1220
|
+
}, 80);
|
|
1221
|
+
}
|
|
1222
|
+
stop(finalMessage) {
|
|
1223
|
+
if (this.interval) {
|
|
1224
|
+
clearInterval(this.interval);
|
|
1225
|
+
this.interval = null;
|
|
1226
|
+
}
|
|
1227
|
+
process.stdout.write("\x1B[?25h");
|
|
1228
|
+
if (finalMessage) {
|
|
1229
|
+
process.stdout.write(`\r${finalMessage}
|
|
1230
|
+
`);
|
|
1231
|
+
} else {
|
|
1232
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1201
1236
|
model_default = selectModel;
|
|
1202
1237
|
}
|
|
1203
1238
|
});
|
|
@@ -1459,7 +1494,10 @@ async function executeWorkflow(ctx) {
|
|
|
1459
1494
|
if (activeSession.phase === "context") {
|
|
1460
1495
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 1/9: \u9879\u76EE\u4E0A\u4E0B\u6587\u83B7\u53D6 \u2501\u2501\u2501"));
|
|
1461
1496
|
lines.push("");
|
|
1497
|
+
const loader = new LoadingIndicator2("\u8BFB\u53D6\u9879\u76EE\u914D\u7F6E");
|
|
1498
|
+
loader.start();
|
|
1462
1499
|
activeSession.context = await readProjectContext(ctx.options.workingDirectory);
|
|
1500
|
+
loader.stop();
|
|
1463
1501
|
lines.push(chalk9__default.default.gray(` \u9879\u76EE: ${activeSession.context.name}`));
|
|
1464
1502
|
lines.push(chalk9__default.default.gray(` \u7C7B\u578B: ${activeSession.context.type}`));
|
|
1465
1503
|
lines.push(chalk9__default.default.gray(` \u6846\u67B6: ${activeSession.context.framework || "\u672A\u8BC6\u522B"}`));
|
|
@@ -1549,7 +1587,7 @@ ${resource.analysis}`;
|
|
|
1549
1587
|
lines.push("");
|
|
1550
1588
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 5/9: BDD \u573A\u666F\u62C6\u89E3 \u2501\u2501\u2501"));
|
|
1551
1589
|
lines.push("");
|
|
1552
|
-
const loader = new
|
|
1590
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u751F\u6210 BDD \u573A\u666F");
|
|
1553
1591
|
loader.start();
|
|
1554
1592
|
try {
|
|
1555
1593
|
activeSession.bddScenarios = await generateBDDScenariosWithAI(
|
|
@@ -1584,7 +1622,7 @@ ${resource.analysis}`;
|
|
|
1584
1622
|
lines.push("");
|
|
1585
1623
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 6/9: OpenSpec \u89C4\u683C \u2501\u2501\u2501"));
|
|
1586
1624
|
lines.push("");
|
|
1587
|
-
const loader = new
|
|
1625
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
1588
1626
|
loader.start();
|
|
1589
1627
|
try {
|
|
1590
1628
|
activeSession.specItems = await generateSpecItemsWithAI(
|
|
@@ -1753,31 +1791,85 @@ async function handleWorkflowInput(input, ctx) {
|
|
|
1753
1791
|
activeSession.phase = "analysis";
|
|
1754
1792
|
return executeWorkflow(ctx);
|
|
1755
1793
|
}
|
|
1756
|
-
if (activeSession.phase === "spec") {
|
|
1794
|
+
if (activeSession.phase === "spec" || activeSession.specFeedbackState === "waiting_confirm") {
|
|
1757
1795
|
if (trimmed === "y" || trimmed === "yes" || trimmed === "\u786E\u8BA4") {
|
|
1796
|
+
activeSession.specFeedbackState = void 0;
|
|
1758
1797
|
activeSession.phase = "tdd";
|
|
1759
1798
|
return executeWorkflow(ctx);
|
|
1760
1799
|
}
|
|
1761
|
-
if (trimmed === "n" || trimmed === "no" || trimmed === "\u91CD\u65B0") {
|
|
1762
|
-
activeSession.
|
|
1763
|
-
|
|
1764
|
-
activeSession.
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1800
|
+
if (trimmed === "n" || trimmed === "no" || trimmed === "\u4E0D\u6EE1\u610F" || trimmed === "\u91CD\u65B0") {
|
|
1801
|
+
activeSession.specFeedbackState = "collecting_feedback";
|
|
1802
|
+
if (!activeSession.specFeedbacks) {
|
|
1803
|
+
activeSession.specFeedbacks = [];
|
|
1804
|
+
}
|
|
1805
|
+
return {
|
|
1806
|
+
output: chalk9__default.default.yellow("\n\u{1F4DD} \u8BF7\u8F93\u5165\u60A8\u5BF9\u89C4\u683C\u4E0D\u6EE1\u610F\u7684\u5730\u65B9:") + chalk9__default.default.gray("\n - \u9057\u6F0F\u7684\u529F\u80FD\u70B9") + chalk9__default.default.gray("\n - \u4E0D\u5408\u7406\u7684\u62C6\u5206") + chalk9__default.default.gray("\n - \u9700\u8981\u8865\u5145\u7684\u7EC6\u8282") + chalk9__default.default.gray("\n - \u5176\u4ED6\u6539\u8FDB\u5EFA\u8BAE") + chalk9__default.default.cyan('\n\n\u8F93\u5165\u5B8C\u6210\u540E\uFF0C\u8F93\u5165\u7A7A\u884C\u6216 "done" \u7ED3\u675F\u8F93\u5165')
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
if (activeSession.specFeedbackState === "collecting_feedback") {
|
|
1811
|
+
if (trimmed === "" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
|
|
1812
|
+
if (!activeSession.specFeedbacks || activeSession.specFeedbacks.length === 0) {
|
|
1813
|
+
return {
|
|
1814
|
+
output: chalk9__default.default.yellow('\u26A0\uFE0F \u8BF7\u81F3\u5C11\u8F93\u5165\u4E00\u6761\u53CD\u9988\u610F\u89C1\uFF0C\u6216\u8F93\u5165 "cancel" \u53D6\u6D88')
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
activeSession.specFeedbackState = "feedback_received";
|
|
1818
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210\u89C4\u683C");
|
|
1819
|
+
loader.start();
|
|
1820
|
+
try {
|
|
1821
|
+
activeSession.specItems = await generateSpecItemsWithFeedback(
|
|
1822
|
+
activeSession.refinedRequirement,
|
|
1823
|
+
activeSession.context,
|
|
1824
|
+
activeSession.bddScenarios,
|
|
1825
|
+
activeSession.clarificationQuestions,
|
|
1826
|
+
activeSession.referenceResources,
|
|
1827
|
+
activeSession.specFeedbacks,
|
|
1828
|
+
ctx
|
|
1829
|
+
);
|
|
1830
|
+
loader.stop(chalk9__default.default.green(" \u2713 \u89C4\u683C\u5DF2\u6839\u636E\u53CD\u9988\u91CD\u65B0\u751F\u6210"));
|
|
1831
|
+
} catch (error) {
|
|
1832
|
+
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u65B9\u6CD5\u91CD\u65B0\u751F\u6210"));
|
|
1833
|
+
activeSession.specItems = generateSpecItems(
|
|
1834
|
+
activeSession.refinedRequirement,
|
|
1835
|
+
activeSession.context,
|
|
1836
|
+
activeSession.bddScenarios,
|
|
1837
|
+
activeSession.clarificationQuestions,
|
|
1838
|
+
activeSession.referenceResources
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1775
1841
|
const specPath = await saveSpecFile(ctx.options.workingDirectory, activeSession);
|
|
1842
|
+
activeSession.specFeedbackState = "waiting_confirm";
|
|
1843
|
+
const lines = [];
|
|
1844
|
+
lines.push(chalk9__default.default.cyan("\n\u2501\u2501\u2501 \u66F4\u65B0\u540E\u7684\u4EFB\u52A1\u6982\u89C8 \u2501\u2501\u2501"));
|
|
1845
|
+
for (const item of activeSession.specItems.slice(0, 8)) {
|
|
1846
|
+
const icon = item.priority === "high" ? "\u{1F534}" : item.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
1847
|
+
lines.push(chalk9__default.default.gray(` ${icon} [${item.id}] ${item.title}`));
|
|
1848
|
+
}
|
|
1849
|
+
if (activeSession.specItems.length > 8) {
|
|
1850
|
+
lines.push(chalk9__default.default.gray(` ... \u5171 ${activeSession.specItems.length} \u4E2A\u4EFB\u52A1`));
|
|
1851
|
+
}
|
|
1852
|
+
return {
|
|
1853
|
+
output: lines.join("\n") + chalk9__default.default.green(`
|
|
1854
|
+
|
|
1855
|
+
\u2713 \u89C4\u683C\u6587\u4EF6\u5DF2\u66F4\u65B0`) + chalk9__default.default.gray(`
|
|
1856
|
+
\u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u4ECD\u6709\u95EE\u9898\uFF0C\u7EE7\u7EED\u53CD\u9988")
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1859
|
+
if (trimmed === "cancel" || trimmed === "\u53D6\u6D88") {
|
|
1860
|
+
activeSession.specFeedbackState = "waiting_confirm";
|
|
1861
|
+
activeSession.specFeedbacks = [];
|
|
1776
1862
|
return {
|
|
1777
|
-
output: chalk9__default.default.
|
|
1778
|
-
\u8DEF\u5F84: ${specPath}`) + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u518D\u6B21\u91CD\u65B0\u751F\u6210")
|
|
1863
|
+
output: chalk9__default.default.gray("\u5DF2\u53D6\u6D88\u53CD\u9988\uFF0C\u8FD4\u56DE\u89C4\u683C\u786E\u8BA4") + chalk9__default.default.yellow("\n\n\u8BF7\u786E\u8BA4:") + chalk9__default.default.green("\n y - \u786E\u8BA4\u89C4\u683C") + chalk9__default.default.red("\n n - \u4E0D\u6EE1\u610F\uFF0C\u91CD\u65B0\u751F\u6210")
|
|
1779
1864
|
};
|
|
1780
1865
|
}
|
|
1866
|
+
if (!activeSession.specFeedbacks) {
|
|
1867
|
+
activeSession.specFeedbacks = [];
|
|
1868
|
+
}
|
|
1869
|
+
activeSession.specFeedbacks.push(trimmed);
|
|
1870
|
+
return {
|
|
1871
|
+
output: chalk9__default.default.gray(`\u5DF2\u8BB0\u5F55\u53CD\u9988 #${activeSession.specFeedbacks.length}: `) + chalk9__default.default.white(trimmed.slice(0, 50)) + chalk9__default.default.gray('\n\u7EE7\u7EED\u8F93\u5165\uFF0C\u6216\u8F93\u5165 "done" \u5B8C\u6210\u53CD\u9988')
|
|
1872
|
+
};
|
|
1781
1873
|
}
|
|
1782
1874
|
if (activeSession.phase === "develop") {
|
|
1783
1875
|
if (trimmed === "continue" || trimmed === "\u7EE7\u7EED" || trimmed === "done" || trimmed === "\u5B8C\u6210") {
|
|
@@ -1936,28 +2028,17 @@ async function executeDevelopment(ctx, session) {
|
|
|
1936
2028
|
if (item.title.includes("\u6D4B\u8BD5") || item.title.includes("test")) {
|
|
1937
2029
|
continue;
|
|
1938
2030
|
}
|
|
1939
|
-
const loader = new
|
|
2031
|
+
const loader = new LoadingIndicator2(`${prefix} \u751F\u6210: ${item.title.slice(0, 20)}...`);
|
|
1940
2032
|
loader.start();
|
|
1941
2033
|
try {
|
|
1942
2034
|
const taskPrompt = buildTaskPrompt(session, item, i);
|
|
2035
|
+
const structureInfo = session.context?.projectStructure;
|
|
2036
|
+
const structureGuide = buildStructureGuide(structureInfo);
|
|
2037
|
+
const systemPrompt = buildCodeGenerationSystemPrompt(session, structureGuide);
|
|
1943
2038
|
const messages = [
|
|
1944
2039
|
{
|
|
1945
2040
|
role: "system",
|
|
1946
|
-
content:
|
|
1947
|
-
|
|
1948
|
-
\u26A0\uFE0F \u91CD\u8981\u89C4\u5219\uFF1A
|
|
1949
|
-
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
|
|
1950
|
-
2. \u6280\u672F\u5B9E\u73B0\u5FC5\u987B\u4E25\u683C\u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u5F00\u53D1\u89C4\u8303
|
|
1951
|
-
3. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
1952
|
-
4. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
1953
|
-
|
|
1954
|
-
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
1955
|
-
- \u540D\u79F0: ${session.context?.name}
|
|
1956
|
-
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
1957
|
-
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
1958
|
-
|
|
1959
|
-
${session.context?.devStandards ? `\u3010\u5F00\u53D1\u89C4\u8303\u3011
|
|
1960
|
-
${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
2041
|
+
content: systemPrompt
|
|
1961
2042
|
},
|
|
1962
2043
|
{
|
|
1963
2044
|
role: "user",
|
|
@@ -1969,25 +2050,25 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
1969
2050
|
maxTokens: 4e3,
|
|
1970
2051
|
// 单个任务减少 token
|
|
1971
2052
|
agent: "frontend-dev",
|
|
1972
|
-
timeout:
|
|
1973
|
-
//
|
|
2053
|
+
timeout: 3e5
|
|
2054
|
+
// 5分钟超时,确保复杂代码有足够时间
|
|
1974
2055
|
});
|
|
1975
2056
|
const codeBlocks = parseCodeBlocks(response.content);
|
|
1976
2057
|
if (codeBlocks.length > 0) {
|
|
1977
2058
|
for (const block of codeBlocks) {
|
|
1978
2059
|
const filePath = path5__namespace.join(workingDir, block.filename);
|
|
1979
2060
|
const dir = path5__namespace.dirname(filePath);
|
|
1980
|
-
await
|
|
1981
|
-
await
|
|
2061
|
+
await fs4__namespace.mkdir(dir, { recursive: true });
|
|
2062
|
+
await fs4__namespace.writeFile(filePath, block.code, "utf-8");
|
|
1982
2063
|
files.push(block.filename);
|
|
1983
2064
|
}
|
|
1984
2065
|
loader.stop(chalk9__default.default.green(`${prefix} \u2713 ${item.title.slice(0, 25)} (${codeBlocks.length} \u4E2A\u6587\u4EF6)`));
|
|
1985
2066
|
} else {
|
|
1986
2067
|
const implDir = path5__namespace.join(workingDir, "src");
|
|
1987
|
-
await
|
|
2068
|
+
await fs4__namespace.mkdir(implDir, { recursive: true });
|
|
1988
2069
|
const fileName = `${item.title.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_")}.ts`;
|
|
1989
2070
|
const filePath = path5__namespace.join(implDir, fileName);
|
|
1990
|
-
await
|
|
2071
|
+
await fs4__namespace.writeFile(filePath, `// TODO: ${item.title}
|
|
1991
2072
|
// ${item.description}
|
|
1992
2073
|
`, "utf-8");
|
|
1993
2074
|
files.push(`src/${fileName}`);
|
|
@@ -2010,6 +2091,91 @@ ${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
|
2010
2091
|
};
|
|
2011
2092
|
}
|
|
2012
2093
|
}
|
|
2094
|
+
function buildStructureGuide(structure) {
|
|
2095
|
+
if (!structure) {
|
|
2096
|
+
return `## \u9879\u76EE\u7ED3\u6784
|
|
2097
|
+
- \u7EC4\u4EF6\u653E\u5728 src/components/
|
|
2098
|
+
- \u9875\u9762\u653E\u5728 src/pages/
|
|
2099
|
+
- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/`;
|
|
2100
|
+
}
|
|
2101
|
+
const lines = ["## \u9879\u76EE\u7ED3\u6784\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09"];
|
|
2102
|
+
if (structure.hasSrc) {
|
|
2103
|
+
lines.push(`- \u6E90\u7801\u76EE\u5F55: ${structure.srcDir}/`);
|
|
2104
|
+
}
|
|
2105
|
+
if (structure.hasPages) {
|
|
2106
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: ${structure.pagesDir}/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2107
|
+
} else {
|
|
2108
|
+
lines.push(`- \u9875\u9762\u76EE\u5F55: src/pages/ \uFF08\u9875\u9762\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2109
|
+
}
|
|
2110
|
+
if (structure.hasComponents) {
|
|
2111
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: ${structure.componentsDir}/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2112
|
+
} else {
|
|
2113
|
+
lines.push(`- \u7EC4\u4EF6\u76EE\u5F55: src/components/ \uFF08\u901A\u7528\u7EC4\u4EF6\u653E\u8FD9\u91CC\uFF09`);
|
|
2114
|
+
}
|
|
2115
|
+
if (structure.hasApi) {
|
|
2116
|
+
lines.push(`- API \u76EE\u5F55: src/api/ \u6216 src/services/`);
|
|
2117
|
+
}
|
|
2118
|
+
if (structure.hasHooks) {
|
|
2119
|
+
lines.push(`- Hooks \u76EE\u5F55: src/hooks/`);
|
|
2120
|
+
}
|
|
2121
|
+
if (structure.hasStore) {
|
|
2122
|
+
lines.push(`- \u72B6\u6001\u7BA1\u7406\u76EE\u5F55: src/store/`);
|
|
2123
|
+
}
|
|
2124
|
+
if (structure.hasUtils) {
|
|
2125
|
+
lines.push(`- \u5DE5\u5177\u51FD\u6570\u76EE\u5F55: src/utils/`);
|
|
2126
|
+
}
|
|
2127
|
+
lines.push("");
|
|
2128
|
+
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");
|
|
2129
|
+
return lines.join("\n");
|
|
2130
|
+
}
|
|
2131
|
+
function buildCodeGenerationSystemPrompt(session, structureGuide) {
|
|
2132
|
+
const framework = session.context?.framework || "React";
|
|
2133
|
+
const techStack = session.context?.techStack?.join(", ") || "TypeScript";
|
|
2134
|
+
const devStandards = session.context?.devStandards || "";
|
|
2135
|
+
const standardsText = devStandards.slice(0, 6e3);
|
|
2136
|
+
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
|
|
2137
|
+
|
|
2138
|
+
## \u26A0\uFE0F \u6838\u5FC3\u89C4\u5219\uFF08\u5FC5\u987B\u9075\u5B88\uFF09
|
|
2139
|
+
|
|
2140
|
+
1. **\u8BED\u8A00**: \u5FC5\u987B\u4F7F\u7528 TypeScript (.tsx/.ts \u6587\u4EF6)
|
|
2141
|
+
2. **\u6846\u67B6**: ${framework}
|
|
2142
|
+
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
|
|
2143
|
+
4. **\u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u6B63\u786E**\uFF1A\u4E25\u683C\u6309\u7167\u9879\u76EE\u7ED3\u6784\u653E\u7F6E\u6587\u4EF6
|
|
2144
|
+
5. **\u5FC5\u987B\u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801**\uFF0C\u4E0D\u8981\u5199 TODO \u6216\u5360\u4F4D\u7B26
|
|
2145
|
+
6. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA\u9875\u9762**\uFF0C\u5FC5\u987B\u751F\u6210\u9875\u9762\u7EC4\u4EF6
|
|
2146
|
+
7. **\u5982\u679C\u4EFB\u52A1\u6D89\u53CA UI**\uFF0C\u5FC5\u987B\u751F\u6210\u5BF9\u5E94\u7684\u7EC4\u4EF6\u548C\u6837\u5F0F
|
|
2147
|
+
|
|
2148
|
+
${structureGuide}
|
|
2149
|
+
|
|
2150
|
+
## \u9879\u76EE\u4FE1\u606F
|
|
2151
|
+
|
|
2152
|
+
- \u540D\u79F0: ${session.context?.name || "\u672A\u547D\u540D\u9879\u76EE"}
|
|
2153
|
+
- \u6846\u67B6: ${framework}
|
|
2154
|
+
- \u6280\u672F\u6808: ${techStack}
|
|
2155
|
+
|
|
2156
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
2157
|
+
|
|
2158
|
+
${standardsText || `### \u9ED8\u8BA4\u89C4\u8303
|
|
2159
|
+
- \u4F7F\u7528\u51FD\u6570\u5F0F\u7EC4\u4EF6
|
|
2160
|
+
- \u4F7F\u7528 TypeScript \u7C7B\u578B\u5B9A\u4E49
|
|
2161
|
+
- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65
|
|
2162
|
+
- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase
|
|
2163
|
+
- \u5BFC\u51FA\u4F7F\u7528 export default \u6216 export const`}
|
|
2164
|
+
|
|
2165
|
+
## \u8F93\u51FA\u683C\u5F0F
|
|
2166
|
+
|
|
2167
|
+
\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u5305\u88F9\uFF0C\u4F8B\u5982\uFF1A
|
|
2168
|
+
|
|
2169
|
+
\`\`\`src/pages/HomePage.tsx
|
|
2170
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
2171
|
+
\`\`\`
|
|
2172
|
+
|
|
2173
|
+
\`\`\`src/components/Button/Button.tsx
|
|
2174
|
+
// \u4EE3\u7801\u5185\u5BB9
|
|
2175
|
+
\`\`\`
|
|
2176
|
+
|
|
2177
|
+
\u73B0\u5728\u8BF7\u6839\u636E\u4EFB\u52A1\u8981\u6C42\u751F\u6210\u4EE3\u7801\u3002`;
|
|
2178
|
+
}
|
|
2013
2179
|
function buildTaskPrompt(session, item, index) {
|
|
2014
2180
|
const lines = [];
|
|
2015
2181
|
lines.push(`## \u5F53\u524D\u4EFB\u52A1 (#${index + 1})`);
|
|
@@ -2058,11 +2224,13 @@ async function executeReview(ctx, session) {
|
|
|
2058
2224
|
const workingDir = ctx.options.workingDirectory;
|
|
2059
2225
|
const issues = [];
|
|
2060
2226
|
const suggestions = [];
|
|
2227
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u5BA1\u6838\u4EE3\u7801");
|
|
2228
|
+
loader.start();
|
|
2061
2229
|
try {
|
|
2062
2230
|
const codeContents = [];
|
|
2063
2231
|
for (const file of session.implFiles) {
|
|
2064
2232
|
try {
|
|
2065
|
-
const content = await
|
|
2233
|
+
const content = await fs4__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2066
2234
|
codeContents.push(`// ${file}
|
|
2067
2235
|
${content}`);
|
|
2068
2236
|
} catch {
|
|
@@ -2071,7 +2239,7 @@ ${content}`);
|
|
|
2071
2239
|
const testContents = [];
|
|
2072
2240
|
for (const file of session.testFiles) {
|
|
2073
2241
|
try {
|
|
2074
|
-
const content = await
|
|
2242
|
+
const content = await fs4__namespace.readFile(path5__namespace.join(workingDir, file), "utf-8");
|
|
2075
2243
|
testContents.push(`// ${file}
|
|
2076
2244
|
${content}`);
|
|
2077
2245
|
} catch {
|
|
@@ -2120,7 +2288,9 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
|
2120
2288
|
const response = await ctx.modelService.sendMessage(messages, {
|
|
2121
2289
|
temperature: 0.2,
|
|
2122
2290
|
maxTokens: 2e3,
|
|
2123
|
-
agent: "code-reviewer"
|
|
2291
|
+
agent: "code-reviewer",
|
|
2292
|
+
timeout: 18e4
|
|
2293
|
+
// 3分钟超时
|
|
2124
2294
|
});
|
|
2125
2295
|
const result = response.content;
|
|
2126
2296
|
const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
|
|
@@ -2142,8 +2312,10 @@ ${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
|
2142
2312
|
const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
|
|
2143
2313
|
suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
|
|
2144
2314
|
}
|
|
2315
|
+
loader.stop(passed ? chalk9__default.default.green(" \u2713 \u5BA1\u6838\u901A\u8FC7") : chalk9__default.default.yellow(" \u26A0 \u53D1\u73B0\u95EE\u9898"));
|
|
2145
2316
|
return { passed, issues, suggestions };
|
|
2146
2317
|
} catch (error) {
|
|
2318
|
+
loader.stop(chalk9__default.default.red(" \u2717 \u5BA1\u6838\u51FA\u9519"));
|
|
2147
2319
|
suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
|
|
2148
2320
|
return { passed: true, issues, suggestions };
|
|
2149
2321
|
}
|
|
@@ -2161,9 +2333,9 @@ async function readProjectContext(workingDir) {
|
|
|
2161
2333
|
};
|
|
2162
2334
|
const agentsPath = path5__namespace.join(workingDir, "AGENTS.md");
|
|
2163
2335
|
try {
|
|
2164
|
-
const stats = await
|
|
2336
|
+
const stats = await fs4__namespace.stat(agentsPath);
|
|
2165
2337
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2166
|
-
context.agentsMd = await
|
|
2338
|
+
context.agentsMd = await fs4__namespace.readFile(agentsPath, "utf-8");
|
|
2167
2339
|
const nameMatch = context.agentsMd.match(/\|\s*项目名称\s*\|\s*([^\s|]+)/);
|
|
2168
2340
|
if (nameMatch) context.name = nameMatch[1];
|
|
2169
2341
|
const typeMatch = context.agentsMd.match(/\|\s*项目类型\s*\|\s*([^\s|]+)/);
|
|
@@ -2177,9 +2349,9 @@ async function readProjectContext(workingDir) {
|
|
|
2177
2349
|
}
|
|
2178
2350
|
const configPath = path5__namespace.join(workingDir, "openspec", "config.yaml");
|
|
2179
2351
|
try {
|
|
2180
|
-
const stats = await
|
|
2352
|
+
const stats = await fs4__namespace.stat(configPath);
|
|
2181
2353
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2182
|
-
context.configYaml = await
|
|
2354
|
+
context.configYaml = await fs4__namespace.readFile(configPath, "utf-8");
|
|
2183
2355
|
const nameMatch = context.configYaml.match(/name:\s*(.+)/);
|
|
2184
2356
|
if (nameMatch) context.name = nameMatch[1].trim();
|
|
2185
2357
|
const typeMatch = context.configYaml.match(/type:\s*(.+)/);
|
|
@@ -2200,14 +2372,173 @@ async function readProjectContext(workingDir) {
|
|
|
2200
2372
|
}
|
|
2201
2373
|
const devStandardsPath = path5__namespace.join(workingDir, ".sf-cli", "norms", "devstanded.md");
|
|
2202
2374
|
try {
|
|
2203
|
-
const stats = await
|
|
2375
|
+
const stats = await fs4__namespace.stat(devStandardsPath);
|
|
2204
2376
|
if (stats.size <= MAX_FILE_SIZE2) {
|
|
2205
|
-
context.devStandards = await
|
|
2377
|
+
context.devStandards = await fs4__namespace.readFile(devStandardsPath, "utf-8");
|
|
2206
2378
|
}
|
|
2207
2379
|
} catch {
|
|
2208
2380
|
}
|
|
2381
|
+
if (!context.framework) {
|
|
2382
|
+
const detectedFramework = await detectFramework2(workingDir);
|
|
2383
|
+
if (detectedFramework) {
|
|
2384
|
+
context.framework = detectedFramework;
|
|
2385
|
+
context.techStack = [detectedFramework, ...context.techStack.filter((t) => t !== detectedFramework)];
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
if (!context.devStandards) {
|
|
2389
|
+
context.devStandards = await analyzeProjectForStandards(workingDir, context);
|
|
2390
|
+
}
|
|
2391
|
+
context.projectStructure = await analyzeProjectStructure(workingDir);
|
|
2209
2392
|
return context;
|
|
2210
2393
|
}
|
|
2394
|
+
async function detectFramework2(workingDir) {
|
|
2395
|
+
try {
|
|
2396
|
+
const pkgPath = path5__namespace.join(workingDir, "package.json");
|
|
2397
|
+
const pkgContent = await fs4__namespace.readFile(pkgPath, "utf-8");
|
|
2398
|
+
const pkg = JSON.parse(pkgContent);
|
|
2399
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2400
|
+
if (deps["react"] || deps["next"]) return "React";
|
|
2401
|
+
if (deps["vue"] || deps["nuxt"]) return "Vue";
|
|
2402
|
+
if (deps["angular"] || deps["@angular/core"]) return "Angular";
|
|
2403
|
+
if (deps["svelte"]) return "Svelte";
|
|
2404
|
+
if (deps["solid-js"]) return "Solid";
|
|
2405
|
+
if (deps["vite"] || deps["webpack"]) return "JavaScript";
|
|
2406
|
+
if (deps["typescript"]) return "TypeScript";
|
|
2407
|
+
return null;
|
|
2408
|
+
} catch {
|
|
2409
|
+
return null;
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async function analyzeProjectForStandards(workingDir, context) {
|
|
2413
|
+
const standards = [];
|
|
2414
|
+
standards.push(`# \u9879\u76EE\u5F00\u53D1\u89C4\u8303\uFF08\u81EA\u52A8\u751F\u6210\uFF09
|
|
2415
|
+
> \u9879\u76EE\u540D\u79F0: ${context.name}
|
|
2416
|
+
> \u6846\u67B6: ${context.framework || "\u672A\u68C0\u6D4B\u5230"}
|
|
2417
|
+
> \u6280\u672F\u6808: ${context.techStack.join(", ") || "\u672A\u68C0\u6D4B\u5230"}
|
|
2418
|
+
`);
|
|
2419
|
+
try {
|
|
2420
|
+
const tsConfigPath = path5__namespace.join(workingDir, "tsconfig.json");
|
|
2421
|
+
await fs4__namespace.access(tsConfigPath);
|
|
2422
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 TypeScript");
|
|
2423
|
+
} catch {
|
|
2424
|
+
standards.push("\n## \u8BED\u8A00\n- \u4F7F\u7528 JavaScript");
|
|
2425
|
+
}
|
|
2426
|
+
try {
|
|
2427
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2428
|
+
const files = await listFilesDeep(srcDir);
|
|
2429
|
+
const hasCss = files.some((f) => f.endsWith(".css"));
|
|
2430
|
+
const hasScss = files.some((f) => f.endsWith(".scss") || f.endsWith(".sass"));
|
|
2431
|
+
const hasLess = files.some((f) => f.endsWith(".less"));
|
|
2432
|
+
const hasStyled = files.some((f) => f.includes(".styled.") || f.includes("styled-components"));
|
|
2433
|
+
if (hasScss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 SCSS/Sass");
|
|
2434
|
+
else if (hasLess) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 Less");
|
|
2435
|
+
else if (hasStyled) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 styled-components");
|
|
2436
|
+
else if (hasCss) standards.push("\n## \u6837\u5F0F\n- \u4F7F\u7528 CSS");
|
|
2437
|
+
} catch {
|
|
2438
|
+
}
|
|
2439
|
+
try {
|
|
2440
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2441
|
+
const entries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
|
|
2442
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2443
|
+
if (dirs.includes("components")) standards.push("\n## \u76EE\u5F55\u7ED3\u6784\n- \u7EC4\u4EF6\u653E\u5728 src/components/");
|
|
2444
|
+
if (dirs.includes("pages") || dirs.includes("views")) standards.push("- \u9875\u9762\u653E\u5728 src/pages/ \u6216 src/views/");
|
|
2445
|
+
if (dirs.includes("hooks")) standards.push("- Hooks \u653E\u5728 src/hooks/");
|
|
2446
|
+
if (dirs.includes("utils")) standards.push("- \u5DE5\u5177\u51FD\u6570\u653E\u5728 src/utils/");
|
|
2447
|
+
if (dirs.includes("api") || dirs.includes("services")) standards.push("- API \u8C03\u7528\u653E\u5728 src/api/ \u6216 src/services/");
|
|
2448
|
+
if (dirs.includes("store") || dirs.includes("stores")) standards.push("- \u72B6\u6001\u7BA1\u7406\u653E\u5728 src/store/");
|
|
2449
|
+
} catch {
|
|
2450
|
+
}
|
|
2451
|
+
standards.push("\n## \u4EE3\u7801\u89C4\u8303");
|
|
2452
|
+
standards.push("- \u4F7F\u7528 ES6+ \u8BED\u6CD5");
|
|
2453
|
+
standards.push("- \u7EC4\u4EF6\u4F7F\u7528\u51FD\u6570\u5F0F\u5199\u6CD5");
|
|
2454
|
+
standards.push("- \u4F7F\u7528 async/await \u5904\u7406\u5F02\u6B65");
|
|
2455
|
+
standards.push("- \u53D8\u91CF\u4F7F\u7528 camelCase\uFF0C\u7EC4\u4EF6\u4F7F\u7528 PascalCase");
|
|
2456
|
+
standards.push("- \u5E38\u91CF\u4F7F\u7528 UPPER_SNAKE_CASE");
|
|
2457
|
+
return standards.join("\n");
|
|
2458
|
+
}
|
|
2459
|
+
async function listFilesDeep(dir, maxDepth = 3) {
|
|
2460
|
+
const files = [];
|
|
2461
|
+
async function scan(currentDir, depth) {
|
|
2462
|
+
if (depth > maxDepth) return;
|
|
2463
|
+
try {
|
|
2464
|
+
const entries = await fs4__namespace.readdir(currentDir, { withFileTypes: true });
|
|
2465
|
+
for (const entry of entries) {
|
|
2466
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2467
|
+
const fullPath = path5__namespace.join(currentDir, entry.name);
|
|
2468
|
+
if (entry.isDirectory()) {
|
|
2469
|
+
await scan(fullPath, depth + 1);
|
|
2470
|
+
} else {
|
|
2471
|
+
files.push(fullPath);
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
} catch {
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
await scan(dir, 0);
|
|
2478
|
+
return files;
|
|
2479
|
+
}
|
|
2480
|
+
async function analyzeProjectStructure(workingDir) {
|
|
2481
|
+
const structure = {
|
|
2482
|
+
hasSrc: false,
|
|
2483
|
+
hasPages: false,
|
|
2484
|
+
hasComponents: false,
|
|
2485
|
+
hasApi: false,
|
|
2486
|
+
hasHooks: false,
|
|
2487
|
+
hasStore: false,
|
|
2488
|
+
hasUtils: false,
|
|
2489
|
+
srcDir: "src",
|
|
2490
|
+
pagesDir: "",
|
|
2491
|
+
componentsDir: ""
|
|
2492
|
+
};
|
|
2493
|
+
try {
|
|
2494
|
+
const srcDir = path5__namespace.join(workingDir, "src");
|
|
2495
|
+
try {
|
|
2496
|
+
const srcStat = await fs4__namespace.stat(srcDir);
|
|
2497
|
+
if (srcStat.isDirectory()) {
|
|
2498
|
+
structure.hasSrc = true;
|
|
2499
|
+
structure.srcDir = "src";
|
|
2500
|
+
const srcEntries = await fs4__namespace.readdir(srcDir, { withFileTypes: true });
|
|
2501
|
+
const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2502
|
+
if (srcDirs.includes("pages")) {
|
|
2503
|
+
structure.hasPages = true;
|
|
2504
|
+
structure.pagesDir = "src/pages";
|
|
2505
|
+
} else if (srcDirs.includes("views")) {
|
|
2506
|
+
structure.hasPages = true;
|
|
2507
|
+
structure.pagesDir = "src/views";
|
|
2508
|
+
}
|
|
2509
|
+
if (srcDirs.includes("components")) {
|
|
2510
|
+
structure.hasComponents = true;
|
|
2511
|
+
structure.componentsDir = "src/components";
|
|
2512
|
+
}
|
|
2513
|
+
if (srcDirs.includes("api") || srcDirs.includes("services")) {
|
|
2514
|
+
structure.hasApi = true;
|
|
2515
|
+
}
|
|
2516
|
+
if (srcDirs.includes("hooks")) {
|
|
2517
|
+
structure.hasHooks = true;
|
|
2518
|
+
}
|
|
2519
|
+
if (srcDirs.includes("store") || srcDirs.includes("stores")) {
|
|
2520
|
+
structure.hasStore = true;
|
|
2521
|
+
}
|
|
2522
|
+
if (srcDirs.includes("utils")) {
|
|
2523
|
+
structure.hasUtils = true;
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
} catch {
|
|
2527
|
+
const rootEntries = await fs4__namespace.readdir(workingDir, { withFileTypes: true });
|
|
2528
|
+
const rootDirs = rootEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2529
|
+
if (rootDirs.includes("pages")) {
|
|
2530
|
+
structure.hasPages = true;
|
|
2531
|
+
structure.pagesDir = "pages";
|
|
2532
|
+
}
|
|
2533
|
+
if (rootDirs.includes("components")) {
|
|
2534
|
+
structure.hasComponents = true;
|
|
2535
|
+
structure.componentsDir = "components";
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
} catch {
|
|
2539
|
+
}
|
|
2540
|
+
return structure;
|
|
2541
|
+
}
|
|
2211
2542
|
function analyzeComplexity(requirement, context) {
|
|
2212
2543
|
let score = 3;
|
|
2213
2544
|
if (requirement.length > 100) score += 1;
|
|
@@ -2349,7 +2680,8 @@ ${r.analysis}`).join("\n\n") : "\u65E0"}
|
|
|
2349
2680
|
], {
|
|
2350
2681
|
temperature: 0.3,
|
|
2351
2682
|
maxTokens: 4e3,
|
|
2352
|
-
timeout:
|
|
2683
|
+
timeout: 3e5
|
|
2684
|
+
// 5分钟超时
|
|
2353
2685
|
});
|
|
2354
2686
|
try {
|
|
2355
2687
|
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
@@ -2557,7 +2889,7 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
2557
2889
|
- \u96C6\u6210\u6D4B\u8BD5
|
|
2558
2890
|
|
|
2559
2891
|
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\u3002`;
|
|
2560
|
-
const loader = new
|
|
2892
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u62C6\u5206\u89C4\u683C");
|
|
2561
2893
|
loader.start();
|
|
2562
2894
|
try {
|
|
2563
2895
|
const response = await ctx.modelService.sendMessage([
|
|
@@ -2565,8 +2897,8 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
2565
2897
|
], {
|
|
2566
2898
|
temperature: 0.3,
|
|
2567
2899
|
maxTokens: 4e3,
|
|
2568
|
-
timeout:
|
|
2569
|
-
//
|
|
2900
|
+
timeout: 3e5
|
|
2901
|
+
// 5分钟超时
|
|
2570
2902
|
});
|
|
2571
2903
|
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
2572
2904
|
if (jsonMatch) {
|
|
@@ -2609,12 +2941,96 @@ ${questions.filter((q) => q.answered).map((q) => `- ${q.question}: ${q.answer}`)
|
|
|
2609
2941
|
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
2610
2942
|
}
|
|
2611
2943
|
}
|
|
2944
|
+
async function generateSpecItemsWithFeedback(requirement, context, bddScenarios, questions, references, feedbacks, ctx) {
|
|
2945
|
+
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
|
|
2946
|
+
|
|
2947
|
+
## \u9700\u6C42\u63CF\u8FF0
|
|
2948
|
+
${requirement}
|
|
2949
|
+
|
|
2950
|
+
## \u9879\u76EE\u4E0A\u4E0B\u6587
|
|
2951
|
+
- \u6280\u672F\u6808: ${context.techStack?.join(", ") || "TypeScript"}
|
|
2952
|
+
- \u6846\u67B6: ${context.framework || "\u672A\u6307\u5B9A"}
|
|
2953
|
+
${context.devStandards ? `
|
|
2954
|
+
## \u5F00\u53D1\u89C4\u8303\uFF08\u5FC5\u987B\u9075\u5FAA\uFF09
|
|
2955
|
+
${context.devStandards.slice(0, 2e3)}
|
|
2956
|
+
` : ""}
|
|
2957
|
+
|
|
2958
|
+
## \u4E4B\u524D\u7684 BDD \u573A\u666F
|
|
2959
|
+
${bddScenarios.map((s) => `- Feature: ${s.feature}`).join("\n")}
|
|
2960
|
+
|
|
2961
|
+
## \u7528\u6237\u53CD\u9988\uFF08\u5FC5\u987B\u89E3\u51B3\u8FD9\u4E9B\u95EE\u9898\uFF09
|
|
2962
|
+
${feedbacks.map((f, i) => `${i + 1}. ${f}`).join("\n")}
|
|
2963
|
+
|
|
2964
|
+
## \u8981\u6C42
|
|
2965
|
+
1. **\u5FC5\u987B\u89E3\u51B3\u7528\u6237\u53CD\u9988\u4E2D\u7684\u6240\u6709\u95EE\u9898**
|
|
2966
|
+
2. \u5982\u679C\u7528\u6237\u6307\u51FA\u9057\u6F0F\u7684\u529F\u80FD\u70B9\uFF0C\u8BF7\u8865\u5145\u76F8\u5173\u4EFB\u52A1
|
|
2967
|
+
3. \u5982\u679C\u7528\u6237\u6307\u51FA\u62C6\u5206\u4E0D\u5408\u7406\uFF0C\u8BF7\u91CD\u65B0\u8C03\u6574\u7C92\u5EA6
|
|
2968
|
+
4. \u5982\u679C\u7528\u6237\u9700\u8981\u66F4\u591A\u7EC6\u8282\uFF0C\u8BF7\u62C6\u5206\u5F97\u66F4\u7EC6\u81F4
|
|
2969
|
+
5. \u6BCF\u4E2A\u4EFB\u52A1\u5E94\u8BE5\u5355\u4E00\u804C\u8D23\u30012-4\u5C0F\u65F6\u53EF\u5B8C\u6210
|
|
2970
|
+
|
|
2971
|
+
## \u8F93\u51FA\u683C\u5F0F (JSON)
|
|
2972
|
+
\`\`\`json
|
|
2973
|
+
[
|
|
2974
|
+
{
|
|
2975
|
+
"id": "T001",
|
|
2976
|
+
"title": "\u4EFB\u52A1\u6807\u9898\uFF08\u7B80\u77ED\u660E\u786E\uFF09",
|
|
2977
|
+
"description": "\u8BE6\u7EC6\u63CF\u8FF0",
|
|
2978
|
+
"priority": "high",
|
|
2979
|
+
"acceptanceCriteria": ["\u9A8C\u6536\u6807\u51C61", "\u9A8C\u6536\u6807\u51C62"]
|
|
2980
|
+
}
|
|
2981
|
+
]
|
|
2982
|
+
\`\`\`
|
|
2983
|
+
|
|
2984
|
+
\u8BF7\u76F4\u63A5\u8F93\u51FA JSON \u6570\u7EC4\uFF0C\u786E\u4FDD\u89E3\u51B3\u4E86\u7528\u6237\u7684\u6240\u6709\u53CD\u9988\u3002`;
|
|
2985
|
+
try {
|
|
2986
|
+
const response = await ctx.modelService.sendMessage([
|
|
2987
|
+
{ role: "user", content: prompt2 }
|
|
2988
|
+
], {
|
|
2989
|
+
temperature: 0.3,
|
|
2990
|
+
maxTokens: 4e3,
|
|
2991
|
+
timeout: 3e5
|
|
2992
|
+
// 5分钟超时
|
|
2993
|
+
});
|
|
2994
|
+
const jsonMatch = response.content.match(/```json\s*([\s\S]*?)```/);
|
|
2995
|
+
if (jsonMatch) {
|
|
2996
|
+
try {
|
|
2997
|
+
const parsed = JSON.parse(jsonMatch[1].trim());
|
|
2998
|
+
return parsed.map((item, index) => ({
|
|
2999
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
3000
|
+
title: item.title,
|
|
3001
|
+
description: item.description,
|
|
3002
|
+
priority: item.priority || "medium",
|
|
3003
|
+
files: [],
|
|
3004
|
+
tests: item.acceptanceCriteria || []
|
|
3005
|
+
}));
|
|
3006
|
+
} catch {
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
try {
|
|
3010
|
+
const parsed = JSON.parse(response.content);
|
|
3011
|
+
if (Array.isArray(parsed)) {
|
|
3012
|
+
return parsed.map((item, index) => ({
|
|
3013
|
+
id: item.id || `T${String(index + 1).padStart(3, "0")}`,
|
|
3014
|
+
title: item.title,
|
|
3015
|
+
description: item.description,
|
|
3016
|
+
priority: item.priority || "medium",
|
|
3017
|
+
files: [],
|
|
3018
|
+
tests: item.acceptanceCriteria || []
|
|
3019
|
+
}));
|
|
3020
|
+
}
|
|
3021
|
+
} catch {
|
|
3022
|
+
}
|
|
3023
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
3024
|
+
} catch {
|
|
3025
|
+
return generateSpecItems(requirement, context, bddScenarios, questions, references);
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
2612
3028
|
async function saveSpecFile(workingDir, session) {
|
|
2613
3029
|
const specDir = path5__namespace.join(workingDir, "openspec", "changes");
|
|
2614
|
-
await
|
|
3030
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
2615
3031
|
const specPath = path5__namespace.join(specDir, `${session.id}-spec.md`);
|
|
2616
3032
|
const content = formatSpecFile(session);
|
|
2617
|
-
await
|
|
3033
|
+
await fs4__namespace.writeFile(specPath, content, "utf-8");
|
|
2618
3034
|
return specPath;
|
|
2619
3035
|
}
|
|
2620
3036
|
function formatSpecFile(session) {
|
|
@@ -2675,6 +3091,16 @@ function formatSpecFile(session) {
|
|
|
2675
3091
|
lines.push("");
|
|
2676
3092
|
}
|
|
2677
3093
|
}
|
|
3094
|
+
if (session.specFeedbacks && session.specFeedbacks.length > 0) {
|
|
3095
|
+
lines.push("## \u89C4\u683C\u8FED\u4EE3\u53CD\u9988");
|
|
3096
|
+
lines.push("");
|
|
3097
|
+
for (let i = 0; i < session.specFeedbacks.length; i++) {
|
|
3098
|
+
lines.push(`${i + 1}. ${session.specFeedbacks[i]}`);
|
|
3099
|
+
}
|
|
3100
|
+
lines.push("");
|
|
3101
|
+
lines.push("---");
|
|
3102
|
+
lines.push("");
|
|
3103
|
+
}
|
|
2678
3104
|
lines.push("## \u4EFB\u52A1\u5217\u8868");
|
|
2679
3105
|
lines.push("");
|
|
2680
3106
|
for (const item of session.specItems) {
|
|
@@ -2689,22 +3115,22 @@ function formatSpecFile(session) {
|
|
|
2689
3115
|
}
|
|
2690
3116
|
async function generateTests(workingDir, session, ctx) {
|
|
2691
3117
|
const testDir = path5__namespace.join(workingDir, "tests");
|
|
2692
|
-
await
|
|
3118
|
+
await fs4__namespace.mkdir(testDir, { recursive: true });
|
|
2693
3119
|
const testFiles = [];
|
|
2694
3120
|
if (ctx?.modelService) {
|
|
2695
3121
|
for (const scenario of session.bddScenarios) {
|
|
2696
3122
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2697
3123
|
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2698
|
-
const loader = new
|
|
3124
|
+
const loader = new LoadingIndicator2(`\u751F\u6210\u6D4B\u8BD5: ${scenario.feature.slice(0, 20)}...`);
|
|
2699
3125
|
loader.start();
|
|
2700
3126
|
try {
|
|
2701
3127
|
const content = await generateTestFileWithAI(scenario, session, ctx);
|
|
2702
|
-
await
|
|
3128
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2703
3129
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2704
3130
|
loader.stop(chalk9__default.default.green(` \u2713 \u6D4B\u8BD5\u6587\u4EF6\u5DF2\u751F\u6210`));
|
|
2705
3131
|
} catch {
|
|
2706
3132
|
const content = generateTestFile(scenario, session);
|
|
2707
|
-
await
|
|
3133
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2708
3134
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2709
3135
|
loader.stop(chalk9__default.default.yellow(" \u26A0 \u4F7F\u7528\u57FA\u7840\u6D4B\u8BD5\u6A21\u677F"));
|
|
2710
3136
|
}
|
|
@@ -2714,7 +3140,7 @@ async function generateTests(workingDir, session, ctx) {
|
|
|
2714
3140
|
const testName = scenario.feature.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]/g, "_");
|
|
2715
3141
|
const testPath = path5__namespace.join(testDir, `${testName}.test.ts`);
|
|
2716
3142
|
const content = generateTestFile(scenario, session);
|
|
2717
|
-
await
|
|
3143
|
+
await fs4__namespace.writeFile(testPath, content, "utf-8");
|
|
2718
3144
|
testFiles.push(`tests/${testName}.test.ts`);
|
|
2719
3145
|
}
|
|
2720
3146
|
}
|
|
@@ -2750,7 +3176,9 @@ ${scenario.scenarios.map((s) => `
|
|
|
2750
3176
|
{ role: "user", content: prompt2 }
|
|
2751
3177
|
], {
|
|
2752
3178
|
temperature: 0.3,
|
|
2753
|
-
maxTokens: 4e3
|
|
3179
|
+
maxTokens: 4e3,
|
|
3180
|
+
timeout: 18e4
|
|
3181
|
+
// 3分钟超时
|
|
2754
3182
|
});
|
|
2755
3183
|
const codeMatch = response.content.match(/```(?:typescript|ts|javascript|js)?\n([\s\S]*?)```/);
|
|
2756
3184
|
if (codeMatch) {
|
|
@@ -2806,7 +3234,7 @@ function generateTestFile(scenario, session) {
|
|
|
2806
3234
|
async function archiveWorkflow(workingDir) {
|
|
2807
3235
|
if (!activeSession) return;
|
|
2808
3236
|
const archiveDir = path5__namespace.join(workingDir, "openspec", "spec");
|
|
2809
|
-
await
|
|
3237
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
2810
3238
|
const archivePath = path5__namespace.join(archiveDir, `${activeSession.id}.md`);
|
|
2811
3239
|
const content = `# \u5F52\u6863: ${activeSession.requirement.slice(0, 50)}
|
|
2812
3240
|
|
|
@@ -2833,7 +3261,7 @@ ${activeSession.refinedRequirement}
|
|
|
2833
3261
|
|
|
2834
3262
|
${activeSession.testFiles.map((f) => `- ${f}`).join("\n") || "\u65E0"}
|
|
2835
3263
|
`;
|
|
2836
|
-
await
|
|
3264
|
+
await fs4__namespace.writeFile(archivePath, content, "utf-8");
|
|
2837
3265
|
}
|
|
2838
3266
|
function generateSessionId() {
|
|
2839
3267
|
const timestamp = Date.now().toString(36);
|
|
@@ -2849,7 +3277,10 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
|
2849
3277
|
const type = detectResourceType(url);
|
|
2850
3278
|
let content = "";
|
|
2851
3279
|
let analysis = "";
|
|
3280
|
+
const loader = new LoadingIndicator2(`\u83B7\u53D6 ${url.slice(0, 40)}...`);
|
|
3281
|
+
loader.start();
|
|
2852
3282
|
try {
|
|
3283
|
+
loader.update("\u6B63\u5728\u83B7\u53D6\u7F51\u9875\u5185\u5BB9");
|
|
2853
3284
|
const response = await fetch(url, {
|
|
2854
3285
|
headers: {
|
|
2855
3286
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
@@ -2860,11 +3291,14 @@ async function fetchAndAnalyzeReference(url, ctx, projectContext) {
|
|
|
2860
3291
|
}
|
|
2861
3292
|
content = await response.text();
|
|
2862
3293
|
if (ctx.modelService.getCurrentModel()) {
|
|
3294
|
+
loader.update("\u6B63\u5728\u5206\u6790\u5185\u5BB9");
|
|
2863
3295
|
analysis = await analyzeReferenceContent(url, content, type, ctx, projectContext);
|
|
2864
3296
|
} else {
|
|
2865
3297
|
analysis = extractBasicInfo(content, type);
|
|
2866
3298
|
}
|
|
3299
|
+
loader.stop();
|
|
2867
3300
|
} catch (error) {
|
|
3301
|
+
loader.stop();
|
|
2868
3302
|
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u53C2\u8003\u8D44\u6E90: ${error.message}`);
|
|
2869
3303
|
}
|
|
2870
3304
|
return { url, type, content: content.slice(0, 1e4), analysis };
|
|
@@ -2933,14 +3367,16 @@ ${content.slice(0, 8e3)}
|
|
|
2933
3367
|
\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
|
|
2934
3368
|
\u6280\u672F\u5B9E\u73B0\u65B9\u6848\u7531\u9879\u76EE\u89C4\u8303\u51B3\u5B9A\uFF0C\u6B64\u5904\u4E0D\u6D89\u53CA\u3002
|
|
2935
3369
|
`;
|
|
2936
|
-
const loader = new
|
|
3370
|
+
const loader = new LoadingIndicator2("AI \u6B63\u5728\u5206\u6790\u53C2\u8003\u8D44\u6E90");
|
|
2937
3371
|
loader.start();
|
|
2938
3372
|
try {
|
|
2939
3373
|
const response = await ctx.modelService.sendMessage([
|
|
2940
3374
|
{ role: "user", content: prompt2 }
|
|
2941
3375
|
], {
|
|
2942
3376
|
temperature: 0.3,
|
|
2943
|
-
maxTokens: 4e3
|
|
3377
|
+
maxTokens: 4e3,
|
|
3378
|
+
timeout: 18e4
|
|
3379
|
+
// 3分钟超时
|
|
2944
3380
|
});
|
|
2945
3381
|
loader.stop(chalk9__default.default.green(" \u2713 \u5206\u6790\u5B8C\u6210"));
|
|
2946
3382
|
return response.content;
|
|
@@ -2987,11 +3423,11 @@ function getActiveSession() {
|
|
|
2987
3423
|
function clearActiveSession() {
|
|
2988
3424
|
activeSession = null;
|
|
2989
3425
|
}
|
|
2990
|
-
var
|
|
3426
|
+
var LoadingIndicator2, MAX_FILE_SIZE2, COMPLEXITY_THRESHOLD, CLARITY_THRESHOLD, activeSession, new_default;
|
|
2991
3427
|
var init_new = __esm({
|
|
2992
3428
|
"src/commands/new.ts"() {
|
|
2993
3429
|
init_cjs_shims();
|
|
2994
|
-
|
|
3430
|
+
LoadingIndicator2 = class {
|
|
2995
3431
|
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
2996
3432
|
frameIndex = 0;
|
|
2997
3433
|
interval = null;
|
|
@@ -3247,11 +3683,11 @@ var NormsManager = class {
|
|
|
3247
3683
|
const files = await this.collectProjectFiles(projectPath);
|
|
3248
3684
|
for (const filePath of files.slice(0, MAX_FILES_TO_SCAN)) {
|
|
3249
3685
|
try {
|
|
3250
|
-
const stats = await
|
|
3686
|
+
const stats = await fs4__namespace.stat(filePath);
|
|
3251
3687
|
if (stats.size > MAX_FILE_SIZE) {
|
|
3252
3688
|
continue;
|
|
3253
3689
|
}
|
|
3254
|
-
const content = await
|
|
3690
|
+
const content = await fs4__namespace.readFile(filePath, "utf-8");
|
|
3255
3691
|
const patterns = await this.learnFromFile(filePath, content);
|
|
3256
3692
|
result.patternsFound += patterns.length;
|
|
3257
3693
|
result.filesScanned++;
|
|
@@ -3877,7 +4313,7 @@ ${response}`;
|
|
|
3877
4313
|
const files = [];
|
|
3878
4314
|
async function scan(dir) {
|
|
3879
4315
|
try {
|
|
3880
|
-
const entries = await
|
|
4316
|
+
const entries = await fs4__namespace.readdir(dir, { withFileTypes: true });
|
|
3881
4317
|
for (const entry of entries) {
|
|
3882
4318
|
if (entry.isDirectory()) {
|
|
3883
4319
|
if (!IGNORED_DIRS.includes(entry.name)) {
|
|
@@ -3920,8 +4356,8 @@ ${response}`;
|
|
|
3920
4356
|
* 确保规范目录存在
|
|
3921
4357
|
*/
|
|
3922
4358
|
async ensureNormsDir() {
|
|
3923
|
-
await
|
|
3924
|
-
await
|
|
4359
|
+
await fs4__namespace.mkdir(this.config.normsDir, { recursive: true });
|
|
4360
|
+
await fs4__namespace.mkdir(path5__namespace.join(this.config.normsDir, "weekly"), { recursive: true });
|
|
3925
4361
|
}
|
|
3926
4362
|
/**
|
|
3927
4363
|
* 加载已有规范
|
|
@@ -3929,7 +4365,7 @@ ${response}`;
|
|
|
3929
4365
|
async loadStandards() {
|
|
3930
4366
|
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
3931
4367
|
try {
|
|
3932
|
-
const content = await
|
|
4368
|
+
const content = await fs4__namespace.readFile(standardsPath, "utf-8");
|
|
3933
4369
|
const data = JSON.parse(content);
|
|
3934
4370
|
for (const standard of data) {
|
|
3935
4371
|
this.standards.set(standard.id, {
|
|
@@ -3947,7 +4383,7 @@ ${response}`;
|
|
|
3947
4383
|
async loadWeights() {
|
|
3948
4384
|
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
3949
4385
|
try {
|
|
3950
|
-
const content = await
|
|
4386
|
+
const content = await fs4__namespace.readFile(weightsPath, "utf-8");
|
|
3951
4387
|
const data = JSON.parse(content);
|
|
3952
4388
|
for (const weight of data) {
|
|
3953
4389
|
this.weights.set(weight.standardId, {
|
|
@@ -3963,7 +4399,7 @@ ${response}`;
|
|
|
3963
4399
|
*/
|
|
3964
4400
|
async saveStandards() {
|
|
3965
4401
|
const standardsPath = path5__namespace.join(this.config.normsDir, "patterns.json");
|
|
3966
|
-
await
|
|
4402
|
+
await fs4__namespace.writeFile(
|
|
3967
4403
|
standardsPath,
|
|
3968
4404
|
JSON.stringify(Array.from(this.standards.values()), null, 2),
|
|
3969
4405
|
"utf-8"
|
|
@@ -3974,7 +4410,7 @@ ${response}`;
|
|
|
3974
4410
|
*/
|
|
3975
4411
|
async saveWeights() {
|
|
3976
4412
|
const weightsPath = path5__namespace.join(this.config.normsDir, "weights.json");
|
|
3977
|
-
await
|
|
4413
|
+
await fs4__namespace.writeFile(
|
|
3978
4414
|
weightsPath,
|
|
3979
4415
|
JSON.stringify(Array.from(this.weights.values()), null, 2),
|
|
3980
4416
|
"utf-8"
|
|
@@ -4002,7 +4438,7 @@ ${response}`;
|
|
|
4002
4438
|
*/
|
|
4003
4439
|
async saveWeeklyReport(weekId, report) {
|
|
4004
4440
|
const reportPath = path5__namespace.join(this.config.normsDir, "weekly", `${weekId}.json`);
|
|
4005
|
-
await
|
|
4441
|
+
await fs4__namespace.writeFile(reportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
4006
4442
|
}
|
|
4007
4443
|
/**
|
|
4008
4444
|
* 更新 devstanded.md
|
|
@@ -4011,7 +4447,7 @@ ${response}`;
|
|
|
4011
4447
|
const standards = this.getEffectiveStandards();
|
|
4012
4448
|
const content = this.generateDevStandedMd(standards);
|
|
4013
4449
|
const mdPath = path5__namespace.join(this.config.normsDir, "devstanded.md");
|
|
4014
|
-
await
|
|
4450
|
+
await fs4__namespace.writeFile(mdPath, content, "utf-8");
|
|
4015
4451
|
}
|
|
4016
4452
|
/**
|
|
4017
4453
|
* 生成 devstanded.md 内容
|
|
@@ -4150,7 +4586,7 @@ async function handleInit(args, ctx) {
|
|
|
4150
4586
|
async function initProject(options = {}, workingDir) {
|
|
4151
4587
|
const cwd = workingDir || process.cwd();
|
|
4152
4588
|
try {
|
|
4153
|
-
const stats = await
|
|
4589
|
+
const stats = await fs4__namespace.stat(cwd);
|
|
4154
4590
|
if (!stats.isDirectory()) {
|
|
4155
4591
|
return {
|
|
4156
4592
|
output: chalk9__default.default.red(`\u9519\u8BEF: ${cwd} \u4E0D\u662F\u6709\u6548\u76EE\u5F55`)
|
|
@@ -4182,7 +4618,7 @@ async function initProject(options = {}, workingDir) {
|
|
|
4182
4618
|
await normsManager.initialize();
|
|
4183
4619
|
const scanResult = await normsManager.scanProject(cwd);
|
|
4184
4620
|
const agentsContent = generateAgentsMd(projectInfo, scanResult);
|
|
4185
|
-
await
|
|
4621
|
+
await fs4__namespace.writeFile(agentsMdPath, agentsContent, "utf-8");
|
|
4186
4622
|
await generateOpenSpecConfig(openspecDir, projectInfo);
|
|
4187
4623
|
return {
|
|
4188
4624
|
output: chalk9__default.default.green("\u2713 \u9879\u76EE\u521D\u59CB\u5316\u5B8C\u6210\n") + chalk9__default.default.gray(` \u9879\u76EE\u7C7B\u578B: ${projectInfo.type}
|
|
@@ -4223,7 +4659,7 @@ async function createSfCliDirectory(basePath) {
|
|
|
4223
4659
|
"logs"
|
|
4224
4660
|
];
|
|
4225
4661
|
for (const dir of dirs) {
|
|
4226
|
-
await
|
|
4662
|
+
await fs4__namespace.mkdir(path5__namespace.join(basePath, dir), { recursive: true });
|
|
4227
4663
|
}
|
|
4228
4664
|
const config = {
|
|
4229
4665
|
version: "1.0.0",
|
|
@@ -4231,22 +4667,22 @@ async function createSfCliDirectory(basePath) {
|
|
|
4231
4667
|
yolo: false,
|
|
4232
4668
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4233
4669
|
};
|
|
4234
|
-
await
|
|
4670
|
+
await fs4__namespace.writeFile(
|
|
4235
4671
|
path5__namespace.join(basePath, "config.json"),
|
|
4236
4672
|
JSON.stringify(config, null, 2),
|
|
4237
4673
|
"utf-8"
|
|
4238
4674
|
);
|
|
4239
|
-
await
|
|
4675
|
+
await fs4__namespace.writeFile(
|
|
4240
4676
|
path5__namespace.join(basePath, "agents", "registry.json"),
|
|
4241
4677
|
JSON.stringify({ version: "1.0.0", agents: {} }, null, 2),
|
|
4242
4678
|
"utf-8"
|
|
4243
4679
|
);
|
|
4244
|
-
await
|
|
4680
|
+
await fs4__namespace.writeFile(
|
|
4245
4681
|
path5__namespace.join(basePath, "skills", "registry.json"),
|
|
4246
4682
|
JSON.stringify({ version: "1.0.0", skills: {} }, null, 2),
|
|
4247
4683
|
"utf-8"
|
|
4248
4684
|
);
|
|
4249
|
-
await
|
|
4685
|
+
await fs4__namespace.writeFile(
|
|
4250
4686
|
path5__namespace.join(basePath, "health", "health.md"),
|
|
4251
4687
|
generateHealthTemplate(),
|
|
4252
4688
|
"utf-8"
|
|
@@ -4256,8 +4692,8 @@ async function createOpenSpecDirectory(basePath) {
|
|
|
4256
4692
|
const changesDir = path5__namespace.join(basePath, "changes");
|
|
4257
4693
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
4258
4694
|
const specDir = path5__namespace.join(basePath, "spec");
|
|
4259
|
-
await
|
|
4260
|
-
await
|
|
4695
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
4696
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
4261
4697
|
}
|
|
4262
4698
|
async function analyzeProject(cwd) {
|
|
4263
4699
|
const result = {
|
|
@@ -4282,7 +4718,7 @@ async function analyzeProject(cwd) {
|
|
|
4282
4718
|
};
|
|
4283
4719
|
const pkgPath = path5__namespace.join(cwd, "package.json");
|
|
4284
4720
|
try {
|
|
4285
|
-
const pkgContent = await
|
|
4721
|
+
const pkgContent = await fs4__namespace.readFile(pkgPath, "utf-8");
|
|
4286
4722
|
const pkg = JSON.parse(pkgContent);
|
|
4287
4723
|
result.name = pkg.name || result.name;
|
|
4288
4724
|
result.dependencies = pkg.dependencies || {};
|
|
@@ -4365,7 +4801,7 @@ async function analyzeDirectoryStructure(cwd) {
|
|
|
4365
4801
|
others: []
|
|
4366
4802
|
};
|
|
4367
4803
|
try {
|
|
4368
|
-
const entries = await
|
|
4804
|
+
const entries = await fs4__namespace.readdir(cwd, { withFileTypes: true });
|
|
4369
4805
|
for (const entry of entries) {
|
|
4370
4806
|
if (!entry.isDirectory()) continue;
|
|
4371
4807
|
const name = entry.name;
|
|
@@ -4410,7 +4846,7 @@ async function findEntryPoints(cwd) {
|
|
|
4410
4846
|
}
|
|
4411
4847
|
async function saveProjectAnalysis(sfCliDir, analysis) {
|
|
4412
4848
|
const analysisPath = path5__namespace.join(sfCliDir, "cache", "analysis", "project-analysis.json");
|
|
4413
|
-
await
|
|
4849
|
+
await fs4__namespace.writeFile(
|
|
4414
4850
|
analysisPath,
|
|
4415
4851
|
JSON.stringify(analysis, null, 2),
|
|
4416
4852
|
"utf-8"
|
|
@@ -4451,7 +4887,7 @@ architecture:
|
|
|
4451
4887
|
# \u5F00\u53D1\u89C4\u8303 (\u4ECE\u4EE3\u7801\u4E2D\u5B66\u4E60)
|
|
4452
4888
|
standards: []
|
|
4453
4889
|
`;
|
|
4454
|
-
await
|
|
4890
|
+
await fs4__namespace.writeFile(configPath, content, "utf-8");
|
|
4455
4891
|
}
|
|
4456
4892
|
function generateAgentsMd(info, scanResult) {
|
|
4457
4893
|
const techStackList = info.techStack.length > 0 ? info.techStack.map((t) => `| ${t} | \u2713 |`).join("\n") : "| \u5F85\u8BC6\u522B | - |";
|
|
@@ -4573,7 +5009,7 @@ function generateHealthTemplate() {
|
|
|
4573
5009
|
}
|
|
4574
5010
|
async function fileExists(filePath) {
|
|
4575
5011
|
try {
|
|
4576
|
-
await
|
|
5012
|
+
await fs4__namespace.access(filePath);
|
|
4577
5013
|
return true;
|
|
4578
5014
|
} catch {
|
|
4579
5015
|
return false;
|
|
@@ -5040,19 +5476,19 @@ var WorkflowEngine = class {
|
|
|
5040
5476
|
async loadProjectContext() {
|
|
5041
5477
|
const agentsMdPath = path5__namespace.join(this.projectPath, "AGENTS.md");
|
|
5042
5478
|
try {
|
|
5043
|
-
this.projectContext = await
|
|
5479
|
+
this.projectContext = await fs4__namespace.readFile(agentsMdPath, "utf-8");
|
|
5044
5480
|
} catch {
|
|
5045
5481
|
this.projectContext = "";
|
|
5046
5482
|
}
|
|
5047
5483
|
const configPath = path5__namespace.join(this.openspecPath, "config.yaml");
|
|
5048
5484
|
try {
|
|
5049
|
-
this.projectConfig = await
|
|
5485
|
+
this.projectConfig = await fs4__namespace.readFile(configPath, "utf-8");
|
|
5050
5486
|
} catch {
|
|
5051
5487
|
this.projectConfig = "";
|
|
5052
5488
|
}
|
|
5053
5489
|
const devstandedPath = path5__namespace.join(this.projectPath, ".sf-cli", "norms", "devstanded.md");
|
|
5054
5490
|
try {
|
|
5055
|
-
this.devStandards = await
|
|
5491
|
+
this.devStandards = await fs4__namespace.readFile(devstandedPath, "utf-8");
|
|
5056
5492
|
} catch {
|
|
5057
5493
|
this.devStandards = "";
|
|
5058
5494
|
}
|
|
@@ -5081,7 +5517,7 @@ var WorkflowEngine = class {
|
|
|
5081
5517
|
const specPath = this.getSpecFilePath();
|
|
5082
5518
|
if (!specPath) return false;
|
|
5083
5519
|
try {
|
|
5084
|
-
await
|
|
5520
|
+
await fs4__namespace.access(specPath);
|
|
5085
5521
|
return true;
|
|
5086
5522
|
} catch {
|
|
5087
5523
|
return false;
|
|
@@ -5315,11 +5751,11 @@ var WorkflowEngine = class {
|
|
|
5315
5751
|
const workflows = [];
|
|
5316
5752
|
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
5317
5753
|
try {
|
|
5318
|
-
const files = await
|
|
5754
|
+
const files = await fs4__namespace.readdir(changesDir);
|
|
5319
5755
|
for (const file of files) {
|
|
5320
5756
|
if (!file.endsWith(".md") || file.includes("-spec.md")) continue;
|
|
5321
5757
|
const filePath = path5__namespace.join(changesDir, file);
|
|
5322
|
-
const content = await
|
|
5758
|
+
const content = await fs4__namespace.readFile(filePath, "utf-8");
|
|
5323
5759
|
const state = this.parseChangeRecord(content);
|
|
5324
5760
|
if (state && state.status === "running") {
|
|
5325
5761
|
workflows.push(state);
|
|
@@ -5370,7 +5806,7 @@ var WorkflowEngine = class {
|
|
|
5370
5806
|
}
|
|
5371
5807
|
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${changeId}.json`);
|
|
5372
5808
|
try {
|
|
5373
|
-
const content = await
|
|
5809
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5374
5810
|
this.state = JSON.parse(content, (key, value) => {
|
|
5375
5811
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5376
5812
|
return new Date(value);
|
|
@@ -5383,7 +5819,7 @@ var WorkflowEngine = class {
|
|
|
5383
5819
|
const changesDir = path5__namespace.join(this.openspecPath, "changes");
|
|
5384
5820
|
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
5385
5821
|
try {
|
|
5386
|
-
const content = await
|
|
5822
|
+
const content = await fs4__namespace.readFile(changeFile, "utf-8");
|
|
5387
5823
|
const parsed = this.parseChangeRecord(content);
|
|
5388
5824
|
if (parsed && parsed.status === "running") {
|
|
5389
5825
|
this.state = parsed;
|
|
@@ -5448,8 +5884,8 @@ var WorkflowEngine = class {
|
|
|
5448
5884
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
5449
5885
|
const changeFile = path5__namespace.join(changesDir, `${changeId}.md`);
|
|
5450
5886
|
const archiveFile = path5__namespace.join(archiveDir, `${changeId}.md`);
|
|
5451
|
-
await
|
|
5452
|
-
await
|
|
5887
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5888
|
+
await fs4__namespace.rename(changeFile, archiveFile).catch(() => {
|
|
5453
5889
|
});
|
|
5454
5890
|
this.state = null;
|
|
5455
5891
|
this.snapshots.clear();
|
|
@@ -5475,15 +5911,15 @@ var WorkflowEngine = class {
|
|
|
5475
5911
|
const archiveDir = path5__namespace.join(changesDir, "archive");
|
|
5476
5912
|
const specDir = path5__namespace.join(this.openspecPath, "spec");
|
|
5477
5913
|
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
5478
|
-
await
|
|
5479
|
-
await
|
|
5480
|
-
await
|
|
5914
|
+
await fs4__namespace.mkdir(archiveDir, { recursive: true });
|
|
5915
|
+
await fs4__namespace.mkdir(specDir, { recursive: true });
|
|
5916
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5481
5917
|
}
|
|
5482
5918
|
async restoreState() {
|
|
5483
5919
|
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5484
5920
|
let activeId = null;
|
|
5485
5921
|
try {
|
|
5486
|
-
const activeContent = await
|
|
5922
|
+
const activeContent = await fs4__namespace.readFile(activePath, "utf-8");
|
|
5487
5923
|
const activeData = JSON.parse(activeContent);
|
|
5488
5924
|
activeId = activeData.activeId;
|
|
5489
5925
|
} catch {
|
|
@@ -5491,7 +5927,7 @@ var WorkflowEngine = class {
|
|
|
5491
5927
|
if (activeId) {
|
|
5492
5928
|
const statePath = path5__namespace.join(this.openspecPath, ".workflow-states", `${activeId}.json`);
|
|
5493
5929
|
try {
|
|
5494
|
-
const content = await
|
|
5930
|
+
const content = await fs4__namespace.readFile(statePath, "utf-8");
|
|
5495
5931
|
this.state = JSON.parse(content, (key, value) => {
|
|
5496
5932
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5497
5933
|
return new Date(value);
|
|
@@ -5504,7 +5940,7 @@ var WorkflowEngine = class {
|
|
|
5504
5940
|
}
|
|
5505
5941
|
const oldStatePath = path5__namespace.join(this.openspecPath, ".workflow-state.json");
|
|
5506
5942
|
try {
|
|
5507
|
-
const content = await
|
|
5943
|
+
const content = await fs4__namespace.readFile(oldStatePath, "utf-8");
|
|
5508
5944
|
this.state = JSON.parse(content, (key, value) => {
|
|
5509
5945
|
if (key.endsWith("At") && typeof value === "string") {
|
|
5510
5946
|
return new Date(value);
|
|
@@ -5513,7 +5949,7 @@ var WorkflowEngine = class {
|
|
|
5513
5949
|
});
|
|
5514
5950
|
if (this.state) {
|
|
5515
5951
|
await this.saveState();
|
|
5516
|
-
await
|
|
5952
|
+
await fs4__namespace.unlink(oldStatePath).catch(() => {
|
|
5517
5953
|
});
|
|
5518
5954
|
}
|
|
5519
5955
|
} catch (e) {
|
|
@@ -5527,16 +5963,16 @@ var WorkflowEngine = class {
|
|
|
5527
5963
|
async saveState() {
|
|
5528
5964
|
if (!this.state) return;
|
|
5529
5965
|
const statesDir = path5__namespace.join(this.openspecPath, ".workflow-states");
|
|
5530
|
-
await
|
|
5966
|
+
await fs4__namespace.mkdir(statesDir, { recursive: true });
|
|
5531
5967
|
const statePath = path5__namespace.join(statesDir, `${this.state.id}.json`);
|
|
5532
|
-
await
|
|
5968
|
+
await fs4__namespace.writeFile(statePath, JSON.stringify(this.state, null, 2));
|
|
5533
5969
|
const activePath = path5__namespace.join(this.openspecPath, ".workflow-active.json");
|
|
5534
|
-
await
|
|
5970
|
+
await fs4__namespace.writeFile(activePath, JSON.stringify({ activeId: this.state.id }, null, 2));
|
|
5535
5971
|
}
|
|
5536
5972
|
async restoreSnapshots() {
|
|
5537
5973
|
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5538
5974
|
try {
|
|
5539
|
-
const content = await
|
|
5975
|
+
const content = await fs4__namespace.readFile(snapshotsPath, "utf-8");
|
|
5540
5976
|
const data = JSON.parse(content, (key, value) => {
|
|
5541
5977
|
if (key === "timestamp" && typeof value === "string") {
|
|
5542
5978
|
return new Date(value);
|
|
@@ -5553,7 +5989,7 @@ var WorkflowEngine = class {
|
|
|
5553
5989
|
async saveSnapshots() {
|
|
5554
5990
|
const snapshotsPath = path5__namespace.join(this.openspecPath, ".workflow-snapshots.json");
|
|
5555
5991
|
const data = Array.from(this.snapshots.values());
|
|
5556
|
-
await
|
|
5992
|
+
await fs4__namespace.writeFile(snapshotsPath, JSON.stringify(data, null, 2));
|
|
5557
5993
|
}
|
|
5558
5994
|
async createSnapshot() {
|
|
5559
5995
|
if (!this.state) return;
|
|
@@ -5574,7 +6010,7 @@ var WorkflowEngine = class {
|
|
|
5574
6010
|
async createChangeRecord() {
|
|
5575
6011
|
if (!this.state) return;
|
|
5576
6012
|
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5577
|
-
await
|
|
6013
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5578
6014
|
}
|
|
5579
6015
|
async updateChangeRecord(status) {
|
|
5580
6016
|
if (!this.state) return;
|
|
@@ -5582,7 +6018,7 @@ var WorkflowEngine = class {
|
|
|
5582
6018
|
this.state.status = status;
|
|
5583
6019
|
}
|
|
5584
6020
|
const changePath = path5__namespace.join(this.openspecPath, "changes", `${this.state.id}.md`);
|
|
5585
|
-
await
|
|
6021
|
+
await fs4__namespace.writeFile(changePath, this.formatChangeRecord());
|
|
5586
6022
|
}
|
|
5587
6023
|
formatChangeRecord() {
|
|
5588
6024
|
if (!this.state) return "";
|
|
@@ -5646,7 +6082,7 @@ ${this.state.steps.map((s) => `- [${s.status === "completed" ? "x" : " "}] ${s.s
|
|
|
5646
6082
|
|
|
5647
6083
|
${this.state.artifacts.map((a) => `- ${a}`).join("\n") || "\u6682\u65E0"}
|
|
5648
6084
|
`;
|
|
5649
|
-
await
|
|
6085
|
+
await fs4__namespace.writeFile(specPath, content);
|
|
5650
6086
|
}
|
|
5651
6087
|
};
|
|
5652
6088
|
var ConfirmationRequiredError = class extends Error {
|
|
@@ -6143,15 +6579,15 @@ async function loadProjectContext(workingDirectory) {
|
|
|
6143
6579
|
devStandards: ""
|
|
6144
6580
|
};
|
|
6145
6581
|
try {
|
|
6146
|
-
context.agentsMd = await
|
|
6582
|
+
context.agentsMd = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, "AGENTS.md"), "utf-8");
|
|
6147
6583
|
} catch {
|
|
6148
6584
|
}
|
|
6149
6585
|
try {
|
|
6150
|
-
context.configYaml = await
|
|
6586
|
+
context.configYaml = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, "openspec", "config.yaml"), "utf-8");
|
|
6151
6587
|
} catch {
|
|
6152
6588
|
}
|
|
6153
6589
|
try {
|
|
6154
|
-
context.devStandards = await
|
|
6590
|
+
context.devStandards = await fs4__namespace.readFile(path5__namespace.join(workingDirectory, ".sf-cli", "norms", "devstanded.md"), "utf-8");
|
|
6155
6591
|
} catch {
|
|
6156
6592
|
}
|
|
6157
6593
|
return context;
|
|
@@ -7073,9 +7509,9 @@ async function handleFileReference(filePath, ctx) {
|
|
|
7073
7509
|
const cwd = ctx.options.workingDirectory;
|
|
7074
7510
|
const absolutePath = path5__namespace.isAbsolute(filePath) ? filePath : path5__namespace.join(cwd, filePath);
|
|
7075
7511
|
try {
|
|
7076
|
-
const stats = await
|
|
7512
|
+
const stats = await fs4__namespace.stat(absolutePath);
|
|
7077
7513
|
if (stats.isDirectory()) {
|
|
7078
|
-
const files = await
|
|
7514
|
+
const files = await fs4__namespace.readdir(absolutePath);
|
|
7079
7515
|
return {
|
|
7080
7516
|
output: chalk9__default.default.cyan(`\u{1F4C1} ${filePath}/`) + chalk9__default.default.gray(`
|
|
7081
7517
|
${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.default.gray(`
|
|
@@ -7083,7 +7519,7 @@ ${files.slice(0, 20).join("\n")}`) + (files.length > 20 ? chalk9__default.defaul
|
|
|
7083
7519
|
contextUsed: 0
|
|
7084
7520
|
};
|
|
7085
7521
|
}
|
|
7086
|
-
const content = await
|
|
7522
|
+
const content = await fs4__namespace.readFile(absolutePath, "utf-8");
|
|
7087
7523
|
const lines = content.split("\n");
|
|
7088
7524
|
ctx.contextManager.addMessage({
|
|
7089
7525
|
role: "user",
|
|
@@ -7161,6 +7597,36 @@ async function executeShell(command, ctx) {
|
|
|
7161
7597
|
// src/commands/natural.ts
|
|
7162
7598
|
init_cjs_shims();
|
|
7163
7599
|
init_new();
|
|
7600
|
+
var LoadingIndicator3 = class {
|
|
7601
|
+
frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
7602
|
+
frameIndex = 0;
|
|
7603
|
+
interval = null;
|
|
7604
|
+
message;
|
|
7605
|
+
constructor(message) {
|
|
7606
|
+
this.message = message;
|
|
7607
|
+
}
|
|
7608
|
+
start() {
|
|
7609
|
+
process.stdout.write("\x1B[?25l");
|
|
7610
|
+
this.interval = setInterval(() => {
|
|
7611
|
+
const frame = this.frames[this.frameIndex];
|
|
7612
|
+
process.stdout.write(`\r${chalk9__default.default.cyan(frame)} ${this.message}...`);
|
|
7613
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
7614
|
+
}, 80);
|
|
7615
|
+
}
|
|
7616
|
+
stop(finalMessage) {
|
|
7617
|
+
if (this.interval) {
|
|
7618
|
+
clearInterval(this.interval);
|
|
7619
|
+
this.interval = null;
|
|
7620
|
+
}
|
|
7621
|
+
process.stdout.write("\x1B[?25h");
|
|
7622
|
+
if (finalMessage) {
|
|
7623
|
+
process.stdout.write(`\r${finalMessage}
|
|
7624
|
+
`);
|
|
7625
|
+
} else {
|
|
7626
|
+
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
};
|
|
7164
7630
|
async function handleNaturalLanguage(input, ctx) {
|
|
7165
7631
|
const trimmedInput = input.trim();
|
|
7166
7632
|
const session = getActiveSession();
|
|
@@ -7180,6 +7646,8 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
7180
7646
|
contextUsed: 0
|
|
7181
7647
|
};
|
|
7182
7648
|
}
|
|
7649
|
+
const loader = new LoadingIndicator3("AI \u601D\u8003\u4E2D");
|
|
7650
|
+
loader.start();
|
|
7183
7651
|
try {
|
|
7184
7652
|
const response = await ctx.modelService.sendMessage(
|
|
7185
7653
|
[
|
|
@@ -7197,6 +7665,7 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
7197
7665
|
maxTokens: 2e3
|
|
7198
7666
|
}
|
|
7199
7667
|
);
|
|
7668
|
+
loader.stop();
|
|
7200
7669
|
ctx.contextManager.addMessage({
|
|
7201
7670
|
role: "user",
|
|
7202
7671
|
content: trimmedInput
|
|
@@ -7210,6 +7679,7 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
7210
7679
|
contextUsed: response.usage?.totalTokens || 0
|
|
7211
7680
|
};
|
|
7212
7681
|
} catch (error) {
|
|
7682
|
+
loader.stop();
|
|
7213
7683
|
const errorMessage = error.message;
|
|
7214
7684
|
if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
|
|
7215
7685
|
return {
|
|
@@ -7635,14 +8105,14 @@ ${summary}`,
|
|
|
7635
8105
|
async persist() {
|
|
7636
8106
|
if (!this.persistPath) return;
|
|
7637
8107
|
const dir = path5__namespace.dirname(this.persistPath);
|
|
7638
|
-
await
|
|
8108
|
+
await fs4__namespace.mkdir(dir, { recursive: true });
|
|
7639
8109
|
const data = {
|
|
7640
8110
|
messages: this.state.messages,
|
|
7641
8111
|
totalTokens: this.state.totalTokens,
|
|
7642
8112
|
limit: this.state.limit,
|
|
7643
8113
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7644
8114
|
};
|
|
7645
|
-
await
|
|
8115
|
+
await fs4__namespace.writeFile(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
|
|
7646
8116
|
}
|
|
7647
8117
|
/**
|
|
7648
8118
|
* 加载持久化的上下文
|
|
@@ -7650,7 +8120,7 @@ ${summary}`,
|
|
|
7650
8120
|
async loadPersistedContext() {
|
|
7651
8121
|
if (!this.persistPath) return;
|
|
7652
8122
|
try {
|
|
7653
|
-
const content = await
|
|
8123
|
+
const content = await fs4__namespace.readFile(this.persistPath, "utf-8");
|
|
7654
8124
|
const data = JSON.parse(content);
|
|
7655
8125
|
this.state.messages = data.messages || [];
|
|
7656
8126
|
this.state.totalTokens = data.totalTokens || 0;
|
|
@@ -7739,7 +8209,7 @@ var ConfigManager = class {
|
|
|
7739
8209
|
this.projectPath = projectPath;
|
|
7740
8210
|
this.configPath = path5__namespace.join(projectPath, ".sf-cli", "config.json");
|
|
7741
8211
|
try {
|
|
7742
|
-
const content = await
|
|
8212
|
+
const content = await fs4__namespace.readFile(this.configPath, "utf-8");
|
|
7743
8213
|
const loaded = JSON.parse(content);
|
|
7744
8214
|
if (loaded.apiKey && loaded.apiKeyEncrypted) {
|
|
7745
8215
|
loaded.apiKey = this.decrypt(loaded.apiKey);
|
|
@@ -7758,8 +8228,8 @@ var ConfigManager = class {
|
|
|
7758
8228
|
configToSave.apiKey = encrypted;
|
|
7759
8229
|
configToSave.apiKeyEncrypted = true;
|
|
7760
8230
|
}
|
|
7761
|
-
await
|
|
7762
|
-
await
|
|
8231
|
+
await fs4__namespace.mkdir(path5__namespace.dirname(this.configPath), { recursive: true });
|
|
8232
|
+
await fs4__namespace.writeFile(
|
|
7763
8233
|
this.configPath,
|
|
7764
8234
|
JSON.stringify(configToSave, null, 2),
|
|
7765
8235
|
"utf-8"
|
|
@@ -8011,8 +8481,8 @@ var ModelService = class {
|
|
|
8011
8481
|
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
8012
8482
|
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
8013
8483
|
const [daily, total] = await Promise.all([
|
|
8014
|
-
|
|
8015
|
-
|
|
8484
|
+
fs4__namespace.readFile(dailyPath, "utf-8").catch(() => "{}"),
|
|
8485
|
+
fs4__namespace.readFile(totalPath, "utf-8").catch(() => '{"totalInput":0,"totalOutput":0}')
|
|
8016
8486
|
]);
|
|
8017
8487
|
const dailyData = JSON.parse(daily);
|
|
8018
8488
|
const totalData = JSON.parse(total);
|
|
@@ -8025,12 +8495,12 @@ var ModelService = class {
|
|
|
8025
8495
|
async saveStats() {
|
|
8026
8496
|
if (!this.statsPath) return;
|
|
8027
8497
|
try {
|
|
8028
|
-
await
|
|
8498
|
+
await fs4__namespace.mkdir(this.statsPath, { recursive: true });
|
|
8029
8499
|
const dailyPath = path5__namespace.join(this.statsPath, "daily.json");
|
|
8030
8500
|
const totalPath = path5__namespace.join(this.statsPath, "total.json");
|
|
8031
8501
|
await Promise.all([
|
|
8032
|
-
|
|
8033
|
-
|
|
8502
|
+
fs4__namespace.writeFile(dailyPath, JSON.stringify(this.stats.byDate, null, 2)),
|
|
8503
|
+
fs4__namespace.writeFile(totalPath, JSON.stringify({
|
|
8034
8504
|
totalInput: this.stats.totalInput,
|
|
8035
8505
|
totalOutput: this.stats.totalOutput
|
|
8036
8506
|
}))
|
|
@@ -8243,7 +8713,7 @@ var Completer = class {
|
|
|
8243
8713
|
prefix = filePath.slice(lastSlash + 1);
|
|
8244
8714
|
}
|
|
8245
8715
|
try {
|
|
8246
|
-
const entries = await
|
|
8716
|
+
const entries = await fs4__namespace.readdir(dirPath, { withFileTypes: true });
|
|
8247
8717
|
const matches = [];
|
|
8248
8718
|
for (const entry of entries) {
|
|
8249
8719
|
if (entry.name.startsWith(prefix)) {
|
|
@@ -8325,7 +8795,7 @@ async function startInteractiveMode(options) {
|
|
|
8325
8795
|
const historyFile = path5__namespace.join(options.workingDirectory, ".sf-cli", "history.json");
|
|
8326
8796
|
let history = [];
|
|
8327
8797
|
try {
|
|
8328
|
-
const historyData = await
|
|
8798
|
+
const historyData = await fs4__namespace.readFile(historyFile, "utf-8");
|
|
8329
8799
|
history = JSON.parse(historyData);
|
|
8330
8800
|
} catch {
|
|
8331
8801
|
}
|
|
@@ -8412,8 +8882,8 @@ async function startInteractiveMode(options) {
|
|
|
8412
8882
|
}
|
|
8413
8883
|
const saveHistory = async () => {
|
|
8414
8884
|
try {
|
|
8415
|
-
await
|
|
8416
|
-
await
|
|
8885
|
+
await fs4__namespace.mkdir(path5__namespace.dirname(historyFile), { recursive: true });
|
|
8886
|
+
await fs4__namespace.writeFile(historyFile, JSON.stringify(history.slice(-500)));
|
|
8417
8887
|
} catch {
|
|
8418
8888
|
}
|
|
8419
8889
|
};
|