refactor: 品牌重命名 + maxTokens 支持 + 设置面板简化

- CodeGuard → Code Purifier / 净码特工(displayName、命令、配置标题)
- 新增 ai.maxTokens 配置项,所有 Provider 及 fixer 传入 maxTokens
- AI 引擎增强:repairJsonEscapes + JSON 解析 fallback + 详细错误信息
- 设置面板规则管理改为文件级(list/delete .yaml),addRule 改为 AI 从 Markdown 生成 YAML
- yaml-parser 简化:移除 config.yaml 的 enable/disable 过滤逻辑
- 审查报告面板:errorBanner 优先显示具体错误、lint 诊断显示 suggestion、移除 translatedDiagnostics 独立渲染
- merger 中 translatedDiagnostics 覆盖原始 lint 诊断 message/suggestion
- HTML linter 配置项、测试用例重写、typescript-eslint 移入 dependencies
This commit is contained in:
范智鹏
2026-07-20 20:24:40 +08:00
parent 805737fcfc
commit a734cdf009
31 changed files with 462 additions and 293 deletions
+50 -8
View File
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import type { AIProvider } from './providers/base';
import { createProvider } from './factory';
import { getAIProvider, getAIModel, getAIBaseUrl, getAITemperature, getAITimeout, getAIOutputLanguage, getApiKey } from '../config';
import { getAIProvider, getAIModel, getAIBaseUrl, getAITemperature, getAITimeout, getAIMaxTokens, getAIOutputLanguage, getApiKey } from '../config';
import type { LinterDiagnostic, CustomRule } from '../types';
import type {
AIEngineResult,
@@ -26,14 +26,54 @@ function addLineNumbers(code: string): string {
return code.split('\n').map((line, i) => `${String(i + 1).padStart(4, ' ')}| ${line}`).join('\n');
}
function repairJsonEscapes(str: string): string {
let s = str.replace(/\\"\\n/g, '\\n').replace(/\\"/g, '"');
let inString = false;
let out = '';
for (let i = 0; i < s.length; i++) {
const ch = s[i];
if (ch === '\\') {
out += ch;
if (i + 1 < s.length) { out += s[++i]; }
} else if (ch === '"') {
if (!inString) {
inString = true;
out += ch;
} else {
let j = i + 1;
while (j < s.length && s[j] === ' ') { j++; }
if (j < s.length && ':,\]}'.includes(s[j])) {
inString = false;
out += ch;
} else {
out += '\\"';
}
}
} else {
out += ch;
}
}
return out;
}
function parseJsonResponse(raw: string): object {
const trimmed = raw.trim();
const start = trimmed.indexOf('{');
const end = trimmed.lastIndexOf('}');
if (start === -1 || end === -1) {
throw new Error('响应中未找到 JSON');
throw new Error(`响应中未找到 JSON。原始响应(前200字符):${trimmed.slice(0, 200)}`);
}
let jsonStr = trimmed.substring(start, end + 1);
try {
return JSON.parse(jsonStr);
} catch {
jsonStr = repairJsonEscapes(jsonStr);
try {
return JSON.parse(jsonStr);
} catch {
throw new Error(`JSON 解析失败。原始响应(前200字符):${jsonStr.slice(0, 200)}`);
}
}
return JSON.parse(trimmed.substring(start, end + 1));
}
const CUSTOM_RULE_SYSTEM_PROMPT = `你是代码规则审查员,只评估以下自定义规则是否被违反。
@@ -48,7 +88,8 @@ const DEEP_REVIEW_SYSTEM_PROMPT = `你是资深代码审查专家,完成两个
重点:安全漏洞、逻辑错误、性能问题、设计缺陷
不要重复静态分析已报告的问题。
仅输出 JSON格式:
仅输出 JSON字符串中的双引号必须用 \" 转义。
格式:
{
"translatedDiagnostics": [{ "originalRuleId": "原始ID", "translatedMessage": "翻译", "translatedSuggestion": "建议", "codeDiff": "可选" }],
"findings": [{ "ruleId": "kebab-case", "severity": "error|warning|info", "category": "bug|performance|security|style|design", "title": "标题", "description": "描述", "suggestion": "建议", "codeDiff": "可选", "line": 行号 }]
@@ -91,6 +132,7 @@ export async function runAIReview(
const options = {
model: getAIModel(),
temperature: getAITemperature(),
maxTokens: getAIMaxTokens(),
timeoutMs: getAITimeout() * 1000,
};
@@ -123,8 +165,8 @@ export async function runAIReview(
...r,
ruleId: `custom:${r.ruleId}`,
}));
} catch {
errors.push('自定义规则响应解析失败');
} catch (e) {
errors.push(`自定义规则响应解析失败: ${e instanceof Error ? e.message : String(e)}`);
}
} else {
errors.push(`自定义规则请求失败: ${resultA.reason}`);
@@ -140,8 +182,8 @@ export async function runAIReview(
};
translatedDiagnostics = parsed.translatedDiagnostics ?? [];
findings = parsed.findings ?? [];
} catch {
errors.push('AI 审查响应解析失败');
} catch (e) {
errors.push(`AI 审查响应解析失败: ${e instanceof Error ? e.message : String(e)}`);
}
} else {
errors.push(`AI 审查请求失败: ${resultB.reason}`);