@nick848/sf-cli 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/dist/cli/index.js +415 -56
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +388 -50
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +388 -50
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,67 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## v1.0.12 (2026-03-22)
|
|
9
|
+
|
|
10
|
+
**交互优化 - API Key 配置引导**
|
|
11
|
+
|
|
12
|
+
- ✨ 启动时检查 API Key 配置状态,未配置时显示引导信息
|
|
13
|
+
- ✨ 自然语言处理功能实现 - 可直接与 AI 对话
|
|
14
|
+
- 🔧 简化权限逻辑:
|
|
15
|
+
- `/model` 等基础命令始终可用
|
|
16
|
+
- `/new` 需要先配置 API Key
|
|
17
|
+
- 自然语言输入始终可用(内部检查 API Key)
|
|
18
|
+
|
|
19
|
+
**引导流程**
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
启动 CLI
|
|
23
|
+
↓
|
|
24
|
+
检查 API Key
|
|
25
|
+
↓ 未配置
|
|
26
|
+
━━━━━━━━━━━━━━━━━━━
|
|
27
|
+
⚠️ 未配置 API Key
|
|
28
|
+
|
|
29
|
+
请先配置模型以启用 AI 功能:
|
|
30
|
+
/model - 交互式选择模型并配置 API Key
|
|
31
|
+
/model list - 查看可用模型列表
|
|
32
|
+
|
|
33
|
+
支持: GLM-5, GPT-4o, Claude
|
|
34
|
+
━━━━━━━━━━━━━━━━━━━
|
|
35
|
+
↓ 配置完成
|
|
36
|
+
✓ 已配置模型: GLM-5
|
|
37
|
+
💡 使用 /new <需求描述> 启动新工作流
|
|
38
|
+
或直接输入自然语言与 AI 交互
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## v1.0.11 (2026-03-22)
|
|
42
|
+
|
|
43
|
+
**重大更新 - 规格确认后自动执行开发流程**
|
|
44
|
+
|
|
45
|
+
- ✨ 规格确认后自动执行:TDD 测试生成 → 开发实现 → 代码审核
|
|
46
|
+
- 🤖 `executeDevelopment()` - AI 自动生成代码实现
|
|
47
|
+
- 🔍 `executeReview()` - AI 自动进行代码审核
|
|
48
|
+
- 📦 自动解析代码块并写入文件
|
|
49
|
+
- 🎯 审核通过自动归档,审核失败可重新审核或回退
|
|
50
|
+
|
|
51
|
+
**新增函数**
|
|
52
|
+
|
|
53
|
+
- `executeDevelopment()` - 调用 AI 生成代码,自动写入文件
|
|
54
|
+
- `executeReview()` - 调用 AI 审核代码,返回问题和建议
|
|
55
|
+
- `buildDevelopmentPrompt()` - 构建开发提示词
|
|
56
|
+
- `parseCodeBlocks()` - 解析 AI 返回的代码块
|
|
57
|
+
|
|
58
|
+
**流程优化**
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
规格确认(y)
|
|
62
|
+
→ 自动生成测试
|
|
63
|
+
→ 自动生成代码
|
|
64
|
+
→ 自动审核
|
|
65
|
+
→ 通过:自动归档
|
|
66
|
+
→ 失败:y 重新审核 / n 回退规格
|
|
67
|
+
```
|
|
68
|
+
|
|
8
69
|
## v1.0.10 (2026-03-22)
|
|
9
70
|
|
|
10
71
|
**重大重构 - 工作流系统重写**
|
package/dist/cli/index.js
CHANGED
|
@@ -1971,18 +1971,67 @@ async function executeWorkflow(ctx) {
|
|
|
1971
1971
|
lines.push("");
|
|
1972
1972
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 7/8: \u5F00\u53D1\u5B9E\u73B0 \u2501\u2501\u2501"));
|
|
1973
1973
|
lines.push("");
|
|
1974
|
-
lines.push(chalk9__default.default.yellow(" \u{
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1974
|
+
lines.push(chalk9__default.default.yellow(" \u{1F680} \u6B63\u5728\u8C03\u7528 AI \u751F\u6210\u4EE3\u7801..."));
|
|
1975
|
+
try {
|
|
1976
|
+
const developResult = await executeDevelopment(ctx, activeSession);
|
|
1977
|
+
if (developResult.success) {
|
|
1978
|
+
lines.push(chalk9__default.default.green(" \u2713 \u4EE3\u7801\u751F\u6210\u5B8C\u6210"));
|
|
1979
|
+
for (const file of developResult.files) {
|
|
1980
|
+
lines.push(chalk9__default.default.gray(` - ${file}`));
|
|
1981
|
+
}
|
|
1982
|
+
activeSession.implFiles = developResult.files;
|
|
1983
|
+
activeSession.phase = "review";
|
|
1984
|
+
} else {
|
|
1985
|
+
lines.push(chalk9__default.default.red(` \u2717 \u4EE3\u7801\u751F\u6210\u5931\u8D25: ${developResult.error}`));
|
|
1986
|
+
lines.push(chalk9__default.default.gray(" \u8BF7\u624B\u52A8\u5B9E\u73B0\u4EE3\u7801\u540E\u8F93\u5165 continue \u7EE7\u7EED"));
|
|
1987
|
+
return { output: lines.join("\n") };
|
|
1988
|
+
}
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
lines.push(chalk9__default.default.red(` \u2717 \u5F00\u53D1\u9636\u6BB5\u51FA\u9519: ${error.message}`));
|
|
1991
|
+
lines.push(chalk9__default.default.gray(" \u8BF7\u624B\u52A8\u5B9E\u73B0\u4EE3\u7801\u540E\u8F93\u5165 continue \u7EE7\u7EED"));
|
|
1992
|
+
return { output: lines.join("\n") };
|
|
1993
|
+
}
|
|
1978
1994
|
}
|
|
1979
1995
|
if (activeSession.phase === "review") {
|
|
1980
1996
|
lines.push("");
|
|
1981
1997
|
lines.push(chalk9__default.default.cyan("\u2501\u2501\u2501 \u9636\u6BB5 8/8: \u4EE3\u7801\u5BA1\u6838 \u2501\u2501\u2501"));
|
|
1982
1998
|
lines.push("");
|
|
1983
|
-
lines.push(chalk9__default.default.yellow(" \u{1F50D} \u4EE3\u7801\u5BA1\u6838
|
|
1984
|
-
|
|
1985
|
-
|
|
1999
|
+
lines.push(chalk9__default.default.yellow(" \u{1F50D} \u6B63\u5728\u8FDB\u884C\u4EE3\u7801\u5BA1\u6838..."));
|
|
2000
|
+
try {
|
|
2001
|
+
const reviewResult = await executeReview(ctx, activeSession);
|
|
2002
|
+
if (reviewResult.passed) {
|
|
2003
|
+
lines.push(chalk9__default.default.green(" \u2713 \u4EE3\u7801\u5BA1\u6838\u901A\u8FC7"));
|
|
2004
|
+
if (reviewResult.suggestions.length > 0) {
|
|
2005
|
+
lines.push(chalk9__default.default.gray(" \u5EFA\u8BAE:"));
|
|
2006
|
+
for (const s of reviewResult.suggestions.slice(0, 3)) {
|
|
2007
|
+
lines.push(chalk9__default.default.gray(` - ${s}`));
|
|
2008
|
+
}
|
|
2009
|
+
}
|
|
2010
|
+
await archiveWorkflow(ctx.options.workingDirectory);
|
|
2011
|
+
const summary = activeSession.refinedRequirement;
|
|
2012
|
+
activeSession = null;
|
|
2013
|
+
lines.push("");
|
|
2014
|
+
lines.push(chalk9__default.default.green.bold("\u{1F389} \u5DE5\u4F5C\u6D41\u5DF2\u5B8C\u6210\uFF01"));
|
|
2015
|
+
lines.push(chalk9__default.default.gray(`\u9700\u6C42: ${summary.slice(0, 60)}${summary.length > 60 ? "..." : ""}`));
|
|
2016
|
+
lines.push("");
|
|
2017
|
+
lines.push(chalk9__default.default.cyan("\u4F7F\u7528 /new <\u9700\u6C42> \u5F00\u59CB\u65B0\u7684\u5DE5\u4F5C\u6D41"));
|
|
2018
|
+
return { output: lines.join("\n") };
|
|
2019
|
+
} else {
|
|
2020
|
+
lines.push(chalk9__default.default.red(" \u2717 \u4EE3\u7801\u5BA1\u6838\u672A\u901A\u8FC7"));
|
|
2021
|
+
lines.push(chalk9__default.default.gray(" \u95EE\u9898:"));
|
|
2022
|
+
for (const issue of reviewResult.issues.slice(0, 5)) {
|
|
2023
|
+
lines.push(chalk9__default.default.red(` - ${issue}`));
|
|
2024
|
+
}
|
|
2025
|
+
lines.push("");
|
|
2026
|
+
lines.push(chalk9__default.default.yellow(" \u8BF7\u4FEE\u590D\u95EE\u9898\u540E\u8F93\u5165 y \u91CD\u65B0\u5BA1\u6838"));
|
|
2027
|
+
lines.push(chalk9__default.default.gray(" \u6216\u8F93\u5165 n \u56DE\u9000\u5230\u89C4\u683C\u9636\u6BB5"));
|
|
2028
|
+
return { output: lines.join("\n") };
|
|
2029
|
+
}
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
lines.push(chalk9__default.default.red(` \u2717 \u5BA1\u6838\u9636\u6BB5\u51FA\u9519: ${error.message}`));
|
|
2032
|
+
lines.push(chalk9__default.default.gray(" \u8F93\u5165 pass \u5F3A\u5236\u901A\u8FC7\uFF0C\u6216 fix \u4FEE\u590D\u95EE\u9898"));
|
|
2033
|
+
return { output: lines.join("\n") };
|
|
2034
|
+
}
|
|
1986
2035
|
}
|
|
1987
2036
|
return { output: lines.join("\n") };
|
|
1988
2037
|
} catch (error) {
|
|
@@ -2065,19 +2114,25 @@ async function handleWorkflowInput(input, ctx) {
|
|
|
2065
2114
|
}
|
|
2066
2115
|
}
|
|
2067
2116
|
if (activeSession.phase === "review") {
|
|
2068
|
-
if (trimmed === "
|
|
2117
|
+
if (trimmed === "pass" || trimmed === "\u901A\u8FC7" || trimmed === "\u5F3A\u5236\u901A\u8FC7") {
|
|
2069
2118
|
await archiveWorkflow(ctx.options.workingDirectory);
|
|
2070
2119
|
const summary = activeSession.refinedRequirement;
|
|
2071
2120
|
activeSession = null;
|
|
2072
2121
|
return {
|
|
2073
2122
|
output: chalk9__default.default.green("\u2713 \u5DE5\u4F5C\u6D41\u5DF2\u5B8C\u6210") + chalk9__default.default.gray(`
|
|
2074
|
-
\u9700\u6C42: ${summary.slice(0, 60)}
|
|
2123
|
+
\u9700\u6C42: ${summary.slice(0, 60)}${summary.length > 60 ? "..." : ""}`) + chalk9__default.default.cyan("\n\n\u4F7F\u7528 /new <\u9700\u6C42> \u5F00\u59CB\u65B0\u7684\u5DE5\u4F5C\u6D41")
|
|
2075
2124
|
};
|
|
2076
2125
|
}
|
|
2077
|
-
if (trimmed === "
|
|
2126
|
+
if (trimmed === "y" || trimmed === "yes" || trimmed === "\u91CD\u65B0\u5BA1\u6838" || trimmed === "retry") {
|
|
2127
|
+
return executeWorkflow(ctx);
|
|
2128
|
+
}
|
|
2129
|
+
if (trimmed === "n" || trimmed === "no" || trimmed === "\u56DE\u9000" || trimmed === "rollback") {
|
|
2078
2130
|
activeSession.phase = "spec";
|
|
2131
|
+
return executeWorkflow(ctx);
|
|
2132
|
+
}
|
|
2133
|
+
if (trimmed === "fix" || trimmed === "\u4FEE\u590D") {
|
|
2079
2134
|
return {
|
|
2080
|
-
output: chalk9__default.default.yellow("\
|
|
2135
|
+
output: chalk9__default.default.yellow("\u{1F4DD} \u8BF7\u624B\u52A8\u4FEE\u590D\u4EE3\u7801\u95EE\u9898") + chalk9__default.default.gray("\n\u4FEE\u590D\u5B8C\u6210\u540E\u8F93\u5165 y \u91CD\u65B0\u5BA1\u6838") + chalk9__default.default.gray("\n\u6216\u8F93\u5165 n \u56DE\u9000\u5230\u89C4\u683C\u9636\u6BB5")
|
|
2081
2136
|
};
|
|
2082
2137
|
}
|
|
2083
2138
|
}
|
|
@@ -2199,6 +2254,207 @@ function getCategoryLabel(category) {
|
|
|
2199
2254
|
};
|
|
2200
2255
|
return labels[category] || category;
|
|
2201
2256
|
}
|
|
2257
|
+
async function executeDevelopment(ctx, session) {
|
|
2258
|
+
const workingDir = ctx.options.workingDirectory;
|
|
2259
|
+
const files = [];
|
|
2260
|
+
try {
|
|
2261
|
+
const systemPrompt = buildDevelopmentPrompt(session);
|
|
2262
|
+
const messages = [
|
|
2263
|
+
{
|
|
2264
|
+
role: "system",
|
|
2265
|
+
content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u5DE5\u7A0B\u5E08\u3002\u8BF7\u6839\u636E\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002
|
|
2266
|
+
|
|
2267
|
+
\u8981\u6C42\uFF1A
|
|
2268
|
+
1. \u751F\u6210\u5B8C\u6574\u3001\u53EF\u8FD0\u884C\u7684\u4EE3\u7801
|
|
2269
|
+
2. \u9075\u5FAA\u9879\u76EE\u73B0\u6709\u7684\u4EE3\u7801\u98CE\u683C\u548C\u89C4\u8303
|
|
2270
|
+
3. \u4F7F\u7528\u9879\u76EE\u6307\u5B9A\u7684\u6280\u672F\u6808
|
|
2271
|
+
4. \u4EE3\u7801\u8981\u6709\u9002\u5F53\u7684\u6CE8\u91CA
|
|
2272
|
+
5. \u8FD4\u56DE\u683C\u5F0F\uFF1A\u6BCF\u4E2A\u6587\u4EF6\u7528 \`\`\`filename \u4EE3\u7801 \`\`\` \u5305\u88F9
|
|
2273
|
+
|
|
2274
|
+
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
2275
|
+
- \u540D\u79F0: ${session.context?.name}
|
|
2276
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
2277
|
+
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
2278
|
+
|
|
2279
|
+
${session.context?.devStandards ? `\u5F00\u53D1\u89C4\u8303\uFF1A
|
|
2280
|
+
${session.context.devStandards.slice(0, 2e3)}` : ""}`
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
role: "user",
|
|
2284
|
+
content: systemPrompt
|
|
2285
|
+
}
|
|
2286
|
+
];
|
|
2287
|
+
const response = await ctx.modelService.sendMessage(messages, {
|
|
2288
|
+
temperature: 0.3,
|
|
2289
|
+
maxTokens: 8e3,
|
|
2290
|
+
agent: "frontend-dev"
|
|
2291
|
+
});
|
|
2292
|
+
const codeBlocks = parseCodeBlocks(response.content);
|
|
2293
|
+
for (const block of codeBlocks) {
|
|
2294
|
+
const filePath = path7__namespace.join(workingDir, block.filename);
|
|
2295
|
+
const dir = path7__namespace.dirname(filePath);
|
|
2296
|
+
await fs7__namespace.mkdir(dir, { recursive: true });
|
|
2297
|
+
await fs7__namespace.writeFile(filePath, block.code, "utf-8");
|
|
2298
|
+
files.push(block.filename);
|
|
2299
|
+
}
|
|
2300
|
+
if (files.length === 0) {
|
|
2301
|
+
const implDir = path7__namespace.join(workingDir, "src", "features");
|
|
2302
|
+
await fs7__namespace.mkdir(implDir, { recursive: true });
|
|
2303
|
+
const featureName = session.specItems[0]?.title || "feature";
|
|
2304
|
+
const fileName = `${featureName.replace(/[^a-zA-Z0-9]/g, "_")}.ts`;
|
|
2305
|
+
const filePath = path7__namespace.join(implDir, fileName);
|
|
2306
|
+
const stubCode = `/**
|
|
2307
|
+
* ${session.requirement}
|
|
2308
|
+
*
|
|
2309
|
+
* TODO: \u6B64\u6587\u4EF6\u7531 AI \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u6839\u636E\u9700\u6C42\u5B8C\u5584\u5B9E\u73B0
|
|
2310
|
+
*/
|
|
2311
|
+
|
|
2312
|
+
export function ${featureName.replace(/[^a-zA-Z0-9]/g, "")}() {
|
|
2313
|
+
// TODO: \u5B9E\u73B0\u529F\u80FD
|
|
2314
|
+
console.log('${featureName} - \u5F85\u5B9E\u73B0');
|
|
2315
|
+
}
|
|
2316
|
+
`;
|
|
2317
|
+
await fs7__namespace.writeFile(filePath, stubCode, "utf-8");
|
|
2318
|
+
files.push(`src/features/${fileName}`);
|
|
2319
|
+
}
|
|
2320
|
+
return { success: true, files };
|
|
2321
|
+
} catch (error) {
|
|
2322
|
+
return {
|
|
2323
|
+
success: false,
|
|
2324
|
+
files: [],
|
|
2325
|
+
error: error.message
|
|
2326
|
+
};
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
function buildDevelopmentPrompt(session) {
|
|
2330
|
+
const lines = [];
|
|
2331
|
+
lines.push("## \u9700\u6C42\u63CF\u8FF0");
|
|
2332
|
+
lines.push(session.refinedRequirement);
|
|
2333
|
+
lines.push("");
|
|
2334
|
+
lines.push("## BDD \u573A\u666F");
|
|
2335
|
+
for (const scenario of session.bddScenarios) {
|
|
2336
|
+
lines.push(`### ${scenario.feature}`);
|
|
2337
|
+
for (const s of scenario.scenarios) {
|
|
2338
|
+
lines.push(`- ${s.name}`);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
lines.push("");
|
|
2342
|
+
lines.push("## \u4EFB\u52A1\u5217\u8868");
|
|
2343
|
+
for (const item of session.specItems) {
|
|
2344
|
+
lines.push(`- [${item.id}] ${item.title}: ${item.description}`);
|
|
2345
|
+
}
|
|
2346
|
+
lines.push("");
|
|
2347
|
+
lines.push("\u8BF7\u6839\u636E\u4EE5\u4E0A\u9700\u6C42\u89C4\u683C\u751F\u6210\u4EE3\u7801\u5B9E\u73B0\u3002");
|
|
2348
|
+
return lines.join("\n");
|
|
2349
|
+
}
|
|
2350
|
+
function parseCodeBlocks(content) {
|
|
2351
|
+
const blocks = [];
|
|
2352
|
+
const regex = /```(\S+)\n([\s\S]*?)```/g;
|
|
2353
|
+
let match;
|
|
2354
|
+
while ((match = regex.exec(content)) !== null) {
|
|
2355
|
+
const filename = match[1];
|
|
2356
|
+
const code = match[2].trim();
|
|
2357
|
+
if (["text", "json", "markdown", "md"].includes(filename.toLowerCase())) {
|
|
2358
|
+
continue;
|
|
2359
|
+
}
|
|
2360
|
+
blocks.push({ filename, code });
|
|
2361
|
+
}
|
|
2362
|
+
return blocks;
|
|
2363
|
+
}
|
|
2364
|
+
async function executeReview(ctx, session) {
|
|
2365
|
+
const workingDir = ctx.options.workingDirectory;
|
|
2366
|
+
const issues = [];
|
|
2367
|
+
const suggestions = [];
|
|
2368
|
+
try {
|
|
2369
|
+
const codeContents = [];
|
|
2370
|
+
for (const file of session.implFiles) {
|
|
2371
|
+
try {
|
|
2372
|
+
const content = await fs7__namespace.readFile(path7__namespace.join(workingDir, file), "utf-8");
|
|
2373
|
+
codeContents.push(`// ${file}
|
|
2374
|
+
${content}`);
|
|
2375
|
+
} catch {
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
const testContents = [];
|
|
2379
|
+
for (const file of session.testFiles) {
|
|
2380
|
+
try {
|
|
2381
|
+
const content = await fs7__namespace.readFile(path7__namespace.join(workingDir, file), "utf-8");
|
|
2382
|
+
testContents.push(`// ${file}
|
|
2383
|
+
${content}`);
|
|
2384
|
+
} catch {
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
const messages = [
|
|
2388
|
+
{
|
|
2389
|
+
role: "system",
|
|
2390
|
+
content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u4EE3\u7801\u5BA1\u6838\u4E13\u5BB6\u3002\u8BF7\u5BA1\u6838\u4EE5\u4E0B\u4EE3\u7801\u5B9E\u73B0\u662F\u5426\u7B26\u5408\u9700\u6C42\u89C4\u683C\u3002
|
|
2391
|
+
|
|
2392
|
+
\u5BA1\u6838\u6807\u51C6\uFF1A
|
|
2393
|
+
1. \u4EE3\u7801\u662F\u5426\u6B63\u786E\u5B9E\u73B0\u4E86\u9700\u6C42\u89C4\u683C\u4E2D\u7684\u529F\u80FD
|
|
2394
|
+
2. \u4EE3\u7801\u8D28\u91CF\uFF08\u53EF\u8BFB\u6027\u3001\u53EF\u7EF4\u62A4\u6027\uFF09
|
|
2395
|
+
3. \u6F5C\u5728\u7684 bug \u548C\u8FB9\u754C\u60C5\u51B5\u5904\u7406
|
|
2396
|
+
4. \u4EE3\u7801\u98CE\u683C\u662F\u5426\u7B26\u5408\u9879\u76EE\u89C4\u8303
|
|
2397
|
+
5. \u6D4B\u8BD5\u8986\u76D6\u662F\u5426\u5145\u5206
|
|
2398
|
+
|
|
2399
|
+
\u8FD4\u56DE\u683C\u5F0F\uFF1A
|
|
2400
|
+
- \u5982\u679C\u901A\u8FC7\u5BA1\u6838\uFF0C\u8FD4\u56DE "\u5BA1\u6838\u901A\u8FC7" \u5E76\u5217\u51FA\u6539\u8FDB\u5EFA\u8BAE
|
|
2401
|
+
- \u5982\u679C\u4E0D\u901A\u8FC7\uFF0C\u5217\u51FA\u5177\u4F53\u95EE\u9898
|
|
2402
|
+
|
|
2403
|
+
\u9879\u76EE\u4FE1\u606F\uFF1A
|
|
2404
|
+
- \u6846\u67B6: ${session.context?.framework || "\u672A\u6307\u5B9A"}
|
|
2405
|
+
- \u6280\u672F\u6808: ${session.context?.techStack.join(", ") || "\u672A\u6307\u5B9A"}
|
|
2406
|
+
|
|
2407
|
+
${session.context?.devStandards ? `\u5F00\u53D1\u89C4\u8303\uFF1A
|
|
2408
|
+
${session.context.devStandards.slice(0, 1e3)}` : ""}`
|
|
2409
|
+
},
|
|
2410
|
+
{
|
|
2411
|
+
role: "user",
|
|
2412
|
+
content: `## \u9700\u6C42\u89C4\u683C
|
|
2413
|
+
${session.refinedRequirement}
|
|
2414
|
+
|
|
2415
|
+
## \u4EFB\u52A1\u5217\u8868
|
|
2416
|
+
${session.specItems.map((item) => `- [${item.id}] ${item.title}`).join("\n")}
|
|
2417
|
+
|
|
2418
|
+
## \u5B9E\u73B0\u4EE3\u7801
|
|
2419
|
+
${codeContents.join("\n\n") || "\uFF08\u65E0\u4EE3\u7801\u6587\u4EF6\uFF09"}
|
|
2420
|
+
|
|
2421
|
+
## \u6D4B\u8BD5\u4EE3\u7801
|
|
2422
|
+
${testContents.join("\n\n") || "\uFF08\u65E0\u6D4B\u8BD5\u6587\u4EF6\uFF09"}
|
|
2423
|
+
|
|
2424
|
+
\u8BF7\u5BA1\u6838\u4EE5\u4E0A\u4EE3\u7801\u5B9E\u73B0\u3002`
|
|
2425
|
+
}
|
|
2426
|
+
];
|
|
2427
|
+
const response = await ctx.modelService.sendMessage(messages, {
|
|
2428
|
+
temperature: 0.2,
|
|
2429
|
+
maxTokens: 2e3,
|
|
2430
|
+
agent: "code-reviewer"
|
|
2431
|
+
});
|
|
2432
|
+
const result = response.content;
|
|
2433
|
+
const passed = result.includes("\u5BA1\u6838\u901A\u8FC7") || result.includes("\u901A\u8FC7") || !result.includes("\u4E0D\u901A\u8FC7");
|
|
2434
|
+
const issueMatches = result.match(/问题[::]\s*([\s\S]*?)(?=建议|$)/i);
|
|
2435
|
+
if (issueMatches) {
|
|
2436
|
+
const issueList = issueMatches[1].split(/[\n-]/).filter((s) => s.trim());
|
|
2437
|
+
issues.push(...issueList.map((s) => s.trim()).filter((s) => s));
|
|
2438
|
+
}
|
|
2439
|
+
const suggestionMatches = result.match(/建议[::]\s*([\s\S]*?)$/i);
|
|
2440
|
+
if (suggestionMatches) {
|
|
2441
|
+
const suggestionList = suggestionMatches[1].split(/[\n-]/).filter((s) => s.trim());
|
|
2442
|
+
suggestions.push(...suggestionList.map((s) => s.trim()).filter((s) => s));
|
|
2443
|
+
}
|
|
2444
|
+
if (issues.length === 0 && !passed) {
|
|
2445
|
+
const lines = result.split("\n").filter((l) => l.includes("\u95EE\u9898") || l.includes("\u9519\u8BEF") || l.includes("\u7F3A\u9677"));
|
|
2446
|
+
issues.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
|
|
2447
|
+
}
|
|
2448
|
+
if (suggestions.length === 0) {
|
|
2449
|
+
const lines = result.split("\n").filter((l) => l.includes("\u5EFA\u8BAE") || l.includes("\u6539\u8FDB") || l.includes("\u4F18\u5316"));
|
|
2450
|
+
suggestions.push(...lines.map((l) => l.replace(/^[-*]\s*/, "").trim()).filter((l) => l));
|
|
2451
|
+
}
|
|
2452
|
+
return { passed, issues, suggestions };
|
|
2453
|
+
} catch (error) {
|
|
2454
|
+
suggestions.push(`\u5BA1\u6838\u8FC7\u7A0B\u51FA\u9519: ${error.message}`);
|
|
2455
|
+
return { passed: true, issues, suggestions };
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2202
2458
|
async function readProjectContext(workingDir) {
|
|
2203
2459
|
const context = {
|
|
2204
2460
|
name: path7__namespace.basename(workingDir),
|
|
@@ -6680,7 +6936,7 @@ async function executeShell(command, ctx) {
|
|
|
6680
6936
|
init_cjs_shims();
|
|
6681
6937
|
init_new();
|
|
6682
6938
|
async function handleNaturalLanguage(input, ctx) {
|
|
6683
|
-
input.trim()
|
|
6939
|
+
const trimmedInput = input.trim();
|
|
6684
6940
|
const session = getActiveSession();
|
|
6685
6941
|
if (session) {
|
|
6686
6942
|
const result = await handleWorkflowInput(input, ctx);
|
|
@@ -6691,81 +6947,163 @@ async function handleNaturalLanguage(input, ctx) {
|
|
|
6691
6947
|
};
|
|
6692
6948
|
}
|
|
6693
6949
|
}
|
|
6694
|
-
ctx.
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6950
|
+
const apiKey = ctx.configManager.get("apiKey");
|
|
6951
|
+
if (!apiKey) {
|
|
6952
|
+
return {
|
|
6953
|
+
output: chalk9__default.default.yellow("\u26A0\uFE0F \u672A\u914D\u7F6E API Key\uFF0C\u65E0\u6CD5\u4F7F\u7528 AI \u529F\u80FD") + chalk9__default.default.gray("\n\n\u8BF7\u5148\u6267\u884C /model \u914D\u7F6E\u6A21\u578B") + chalk9__default.default.gray("\n\u652F\u6301: GLM-5, GPT-4o, Claude"),
|
|
6954
|
+
contextUsed: 0
|
|
6955
|
+
};
|
|
6956
|
+
}
|
|
6957
|
+
try {
|
|
6958
|
+
const response = await ctx.modelService.sendMessage(
|
|
6959
|
+
[
|
|
6960
|
+
{
|
|
6961
|
+
role: "system",
|
|
6962
|
+
content: buildSystemPrompt(ctx)
|
|
6963
|
+
},
|
|
6964
|
+
{
|
|
6965
|
+
role: "user",
|
|
6966
|
+
content: trimmedInput
|
|
6967
|
+
}
|
|
6968
|
+
],
|
|
6969
|
+
{
|
|
6970
|
+
temperature: 0.7,
|
|
6971
|
+
maxTokens: 2e3
|
|
6972
|
+
}
|
|
6973
|
+
);
|
|
6974
|
+
ctx.contextManager.addMessage({
|
|
6975
|
+
role: "user",
|
|
6976
|
+
content: trimmedInput
|
|
6977
|
+
});
|
|
6978
|
+
ctx.contextManager.addMessage({
|
|
6979
|
+
role: "assistant",
|
|
6980
|
+
content: response.content
|
|
6981
|
+
});
|
|
6982
|
+
return {
|
|
6983
|
+
output: response.content,
|
|
6984
|
+
contextUsed: response.usage?.totalTokens || 0
|
|
6985
|
+
};
|
|
6986
|
+
} catch (error) {
|
|
6987
|
+
const errorMessage = error.message;
|
|
6988
|
+
if (errorMessage.includes("\u672A\u914D\u7F6E") || errorMessage.includes("\u672A\u521D\u59CB\u5316")) {
|
|
6989
|
+
return {
|
|
6990
|
+
output: chalk9__default.default.yellow("\u26A0\uFE0F \u6A21\u578B\u672A\u6B63\u786E\u914D\u7F6E") + chalk9__default.default.gray("\n\n\u8BF7\u6267\u884C /model \u91CD\u65B0\u914D\u7F6E"),
|
|
6991
|
+
contextUsed: 0
|
|
6992
|
+
};
|
|
6993
|
+
}
|
|
6994
|
+
if (errorMessage.includes("timeout") || errorMessage.includes("\u8D85\u65F6")) {
|
|
6995
|
+
return {
|
|
6996
|
+
output: chalk9__default.default.red("\u2717 \u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"),
|
|
6997
|
+
contextUsed: 0
|
|
6998
|
+
};
|
|
6999
|
+
}
|
|
7000
|
+
return {
|
|
7001
|
+
output: chalk9__default.default.red(`\u2717 \u5904\u7406\u5931\u8D25: ${errorMessage}`),
|
|
7002
|
+
contextUsed: 0
|
|
7003
|
+
};
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
function buildSystemPrompt(ctx) {
|
|
7007
|
+
const parts = [
|
|
7008
|
+
"\u4F60\u662F sf-cli \u7684 AI \u52A9\u624B\uFF0C\u4E00\u4E2A\u4E13\u4E1A\u7684\u8F6F\u4EF6\u5F00\u53D1\u52A9\u624B\u3002",
|
|
7009
|
+
"",
|
|
7010
|
+
"\u4F60\u53EF\u4EE5\u5E2E\u52A9\u7528\u6237\uFF1A",
|
|
7011
|
+
"1. \u7406\u89E3\u548C\u5206\u6790\u9700\u6C42",
|
|
7012
|
+
"2. \u63D0\u4F9B\u4EE3\u7801\u5EFA\u8BAE\u548C\u5B9E\u73B0\u65B9\u6848",
|
|
7013
|
+
"3. \u56DE\u7B54\u6280\u672F\u95EE\u9898",
|
|
7014
|
+
"4. \u8F85\u52A9\u8FDB\u884C\u4EE3\u7801\u5BA1\u67E5",
|
|
7015
|
+
"",
|
|
7016
|
+
"\u5F53\u524D\u9879\u76EE\u4FE1\u606F\uFF1A",
|
|
7017
|
+
`- \u5DE5\u4F5C\u76EE\u5F55: ${ctx.options.workingDirectory}`,
|
|
7018
|
+
`- \u6A21\u578B: ${ctx.modelService.getCurrentModel() || "\u672A\u6307\u5B9A"}`
|
|
7019
|
+
];
|
|
7020
|
+
return parts.join("\n");
|
|
6703
7021
|
}
|
|
6704
7022
|
|
|
6705
7023
|
// src/cli/executor.ts
|
|
6706
7024
|
init_new();
|
|
6707
|
-
var
|
|
7025
|
+
var ALWAYS_ALLOWED = [
|
|
6708
7026
|
"help",
|
|
6709
7027
|
"h",
|
|
6710
7028
|
"?",
|
|
6711
|
-
"init",
|
|
6712
|
-
"i",
|
|
6713
7029
|
"model",
|
|
6714
7030
|
"m",
|
|
6715
|
-
"new",
|
|
6716
|
-
"n",
|
|
6717
7031
|
"exit",
|
|
6718
7032
|
"e",
|
|
6719
7033
|
"q",
|
|
6720
7034
|
"quit",
|
|
6721
7035
|
"clear",
|
|
6722
7036
|
"c",
|
|
6723
|
-
"update",
|
|
6724
|
-
"u",
|
|
6725
7037
|
"version",
|
|
6726
7038
|
"v"
|
|
6727
7039
|
];
|
|
7040
|
+
var REQUIRES_API_KEY = [
|
|
7041
|
+
"new",
|
|
7042
|
+
"n",
|
|
7043
|
+
"init",
|
|
7044
|
+
"i",
|
|
7045
|
+
"update",
|
|
7046
|
+
"u"
|
|
7047
|
+
];
|
|
6728
7048
|
var CommandExecutor = class {
|
|
6729
7049
|
async execute(parseResult, ctx) {
|
|
6730
7050
|
if (!parseResult.success || !parseResult.command) {
|
|
6731
7051
|
return { output: chalk9__default.default.red(`\u9519\u8BEF: ${parseResult.error}`) };
|
|
6732
7052
|
}
|
|
6733
7053
|
const { command } = parseResult;
|
|
7054
|
+
const hasApiKey = !!ctx.configManager.get("apiKey");
|
|
6734
7055
|
const hasActiveWorkflow = getActiveSession() !== null;
|
|
6735
|
-
if (
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
7056
|
+
if (command.type === "slash" /* SLASH */) {
|
|
7057
|
+
const cmd = command.command?.toLowerCase() || "";
|
|
7058
|
+
if (ALWAYS_ALLOWED.includes(cmd)) {
|
|
7059
|
+
return this.executeSlashCommand(command, ctx);
|
|
7060
|
+
}
|
|
7061
|
+
if (REQUIRES_API_KEY.includes(cmd)) {
|
|
7062
|
+
if (!hasApiKey) {
|
|
6739
7063
|
return {
|
|
6740
|
-
output: chalk9__default.default.yellow("\
|
|
7064
|
+
output: chalk9__default.default.yellow("\u26A0\uFE0F \u8BF7\u5148\u914D\u7F6E API Key") + chalk9__default.default.gray("\n\n\u6267\u884C /model \u9009\u62E9\u6A21\u578B\u5E76\u914D\u7F6E API Key")
|
|
6741
7065
|
};
|
|
6742
7066
|
}
|
|
6743
|
-
|
|
7067
|
+
return this.executeSlashCommand(command, ctx);
|
|
7068
|
+
}
|
|
7069
|
+
if (!hasActiveWorkflow) {
|
|
7070
|
+
return {
|
|
7071
|
+
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
7072
|
+
};
|
|
7073
|
+
}
|
|
7074
|
+
return this.executeSlashCommand(command, ctx);
|
|
7075
|
+
}
|
|
7076
|
+
if (command.type === "dollar" /* DOLLAR */) {
|
|
7077
|
+
if (!hasActiveWorkflow) {
|
|
6744
7078
|
return {
|
|
6745
7079
|
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u8C03\u7528 Agent") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
6746
7080
|
};
|
|
6747
|
-
}
|
|
7081
|
+
}
|
|
7082
|
+
if (!hasApiKey) {
|
|
7083
|
+
return {
|
|
7084
|
+
output: chalk9__default.default.yellow("\u26A0\uFE0F \u8BF7\u5148\u914D\u7F6E API Key") + chalk9__default.default.gray("\n\n\u6267\u884C /model \u914D\u7F6E\u6A21\u578B")
|
|
7085
|
+
};
|
|
7086
|
+
}
|
|
7087
|
+
return this.executeAgent(command, ctx);
|
|
7088
|
+
}
|
|
7089
|
+
if (command.type === "shell" /* SHELL */) {
|
|
7090
|
+
if (!hasActiveWorkflow) {
|
|
6748
7091
|
return {
|
|
6749
7092
|
output: chalk9__default.default.yellow("\u5F53\u524D\u6CA1\u6709\u6D3B\u8DC3\u7684\u5DE5\u4F5C\u6D41\uFF0C\u65E0\u6CD5\u6267\u884C Shell \u547D\u4EE4") + chalk9__default.default.gray("\n\u8BF7\u5148\u4F7F\u7528 ") + chalk9__default.default.cyan("/new <\u9700\u6C42\u63CF\u8FF0>") + chalk9__default.default.gray(" \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41")
|
|
6750
7093
|
};
|
|
6751
7094
|
}
|
|
7095
|
+
return this.executeShell(command, ctx);
|
|
6752
7096
|
}
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
case "dollar" /* DOLLAR */:
|
|
6759
|
-
return this.executeAgent(command, ctx);
|
|
6760
|
-
case "shell" /* SHELL */:
|
|
6761
|
-
return this.executeShell(command, ctx);
|
|
6762
|
-
case "natural" /* NATURAL */:
|
|
6763
|
-
return this.executeNaturalLanguage(command, ctx);
|
|
6764
|
-
case "yolo" /* YOLO */:
|
|
6765
|
-
return this.executeYolo(ctx);
|
|
6766
|
-
default:
|
|
6767
|
-
return { output: chalk9__default.default.red("\u672A\u77E5\u7684\u547D\u4EE4\u7C7B\u578B") };
|
|
7097
|
+
if (command.type === "at" /* AT */) {
|
|
7098
|
+
return this.executeFileReference(command, ctx);
|
|
7099
|
+
}
|
|
7100
|
+
if (command.type === "natural" /* NATURAL */) {
|
|
7101
|
+
return this.executeNaturalLanguage(command, ctx);
|
|
6768
7102
|
}
|
|
7103
|
+
if (command.type === "yolo" /* YOLO */) {
|
|
7104
|
+
return this.executeYolo(ctx);
|
|
7105
|
+
}
|
|
7106
|
+
return { output: chalk9__default.default.red("\u672A\u77E5\u7684\u547D\u4EE4\u7C7B\u578B") };
|
|
6769
7107
|
}
|
|
6770
7108
|
async executeSlashCommand(command, ctx) {
|
|
6771
7109
|
const result = await runSlashCommand(
|
|
@@ -7414,6 +7752,7 @@ async function startInteractiveMode(options) {
|
|
|
7414
7752
|
contextLimit: 512 * 1024
|
|
7415
7753
|
};
|
|
7416
7754
|
const currentModel = modelService.getCurrentModel() || "GLM-5";
|
|
7755
|
+
const hasApiKey = !!configManager.get("apiKey");
|
|
7417
7756
|
const activeSession2 = getActiveSession();
|
|
7418
7757
|
displayStatus({
|
|
7419
7758
|
directory: options.workingDirectory,
|
|
@@ -7422,14 +7761,34 @@ async function startInteractiveMode(options) {
|
|
|
7422
7761
|
services: [],
|
|
7423
7762
|
workflowStep: activeSession2?.phase
|
|
7424
7763
|
});
|
|
7425
|
-
if (
|
|
7426
|
-
console.log(
|
|
7764
|
+
if (!hasApiKey) {
|
|
7765
|
+
console.log("");
|
|
7766
|
+
console.log(chalk9__default.default.yellow.bold("\u26A0\uFE0F \u672A\u914D\u7F6E API Key"));
|
|
7767
|
+
console.log("");
|
|
7768
|
+
console.log(chalk9__default.default.gray("\u8BF7\u5148\u914D\u7F6E\u6A21\u578B\u4EE5\u542F\u7528 AI \u529F\u80FD:"));
|
|
7769
|
+
console.log("");
|
|
7770
|
+
console.log(chalk9__default.default.cyan(" /model ") + chalk9__default.default.gray("- \u4EA4\u4E92\u5F0F\u9009\u62E9\u6A21\u578B\u5E76\u914D\u7F6E API Key"));
|
|
7771
|
+
console.log(chalk9__default.default.cyan(" /model list ") + chalk9__default.default.gray("- \u67E5\u770B\u53EF\u7528\u6A21\u578B\u5217\u8868"));
|
|
7772
|
+
console.log(chalk9__default.default.cyan(" /model set <model-id> ") + chalk9__default.default.gray("- \u76F4\u63A5\u8BBE\u7F6E\u6A21\u578B"));
|
|
7773
|
+
console.log("");
|
|
7774
|
+
console.log(chalk9__default.default.gray("\u652F\u6301\u7684\u6A21\u578B:"));
|
|
7775
|
+
console.log(chalk9__default.default.gray(" \u2022 GLM-5 (\u667A\u8C31AI) - \u63A8\u8350"));
|
|
7776
|
+
console.log(chalk9__default.default.gray(" \u2022 GPT-4o (OpenAI)"));
|
|
7777
|
+
console.log(chalk9__default.default.gray(" \u2022 Claude (Anthropic)"));
|
|
7778
|
+
console.log("");
|
|
7779
|
+
console.log(chalk9__default.default.gray("\u2500".repeat(50)));
|
|
7780
|
+
} else {
|
|
7781
|
+
if (activeSession2) {
|
|
7782
|
+
console.log(chalk9__default.default.cyan(`
|
|
7427
7783
|
\u{1F4CB} \u6D3B\u8DC3\u5DE5\u4F5C\u6D41: ${activeSession2.requirement.slice(0, 40)}...`));
|
|
7428
|
-
|
|
7429
|
-
|
|
7784
|
+
console.log(chalk9__default.default.gray(` \u9636\u6BB5: ${activeSession2.phase}`));
|
|
7785
|
+
console.log(chalk9__default.default.gray(` \u8F93\u5165\u4EFB\u610F\u5185\u5BB9\u7EE7\u7EED
|
|
7430
7786
|
`));
|
|
7431
|
-
|
|
7432
|
-
|
|
7787
|
+
} else {
|
|
7788
|
+
console.log(chalk9__default.default.green("\n\u2713 \u5DF2\u914D\u7F6E\u6A21\u578B: ") + chalk9__default.default.white(currentModel));
|
|
7789
|
+
console.log(chalk9__default.default.gray("\n\u{1F4A1} \u4F7F\u7528 /new <\u9700\u6C42\u63CF\u8FF0> \u542F\u52A8\u65B0\u5DE5\u4F5C\u6D41"));
|
|
7790
|
+
console.log(chalk9__default.default.gray(" \u6216\u76F4\u63A5\u8F93\u5165\u81EA\u7136\u8BED\u8A00\u4E0E AI \u4EA4\u4E92\n"));
|
|
7791
|
+
}
|
|
7433
7792
|
}
|
|
7434
7793
|
const saveHistory = async () => {
|
|
7435
7794
|
try {
|