@nick848/sf-cli 1.0.11 → 1.0.12
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 +28 -0
- package/dist/cli/index.js +267 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +267 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +267 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ 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.11 (2026-03-22)
|
|
9
|
+
|
|
10
|
+
**重大更新 - 规格确认后自动执行开发流程**
|
|
11
|
+
|
|
12
|
+
- ✨ 规格确认后自动执行:TDD 测试生成 → 开发实现 → 代码审核
|
|
13
|
+
- 🤖 `executeDevelopment()` - AI 自动生成代码实现
|
|
14
|
+
- 🔍 `executeReview()` - AI 自动进行代码审核
|
|
15
|
+
- 📦 自动解析代码块并写入文件
|
|
16
|
+
- 🎯 审核通过自动归档,审核失败可重新审核或回退
|
|
17
|
+
|
|
18
|
+
**新增函数**
|
|
19
|
+
|
|
20
|
+
- `executeDevelopment()` - 调用 AI 生成代码,自动写入文件
|
|
21
|
+
- `executeReview()` - 调用 AI 审核代码,返回问题和建议
|
|
22
|
+
- `buildDevelopmentPrompt()` - 构建开发提示词
|
|
23
|
+
- `parseCodeBlocks()` - 解析 AI 返回的代码块
|
|
24
|
+
|
|
25
|
+
**流程优化**
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
规格确认(y)
|
|
29
|
+
→ 自动生成测试
|
|
30
|
+
→ 自动生成代码
|
|
31
|
+
→ 自动审核
|
|
32
|
+
→ 通过:自动归档
|
|
33
|
+
→ 失败:y 重新审核 / n 回退规格
|
|
34
|
+
```
|
|
35
|
+
|
|
8
36
|
## v1.0.10 (2026-03-22)
|
|
9
37
|
|
|
10
38
|
**重大重构 - 工作流系统重写**
|
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),
|