chore(release): bump version to 0.66.0

This commit is contained in:
coso
2026-02-14 15:20:30 +08:00
parent 82477c97b1
commit 73e9199107
74 changed files with 8869 additions and 155 deletions
+47
View File
@@ -0,0 +1,47 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "AI Code Verify Configuration",
"description": "AI 代码验证工具的配置文件 schema",
"type": "object",
"properties": {
"level": {
"type": "number",
"description": "验证等级(0-2)",
"minimum": 0,
"maximum": 2,
"default": 0
},
"enabled": {
"type": "boolean",
"description": "是否启用验证",
"default": true
},
"ignorePatterns": {
"type": "array",
"description": "忽略的文件/目录模式",
"items": {
"type": "string"
},
"default": ["node_modules", "dist", "build", ".git"]
},
"includePatterns": {
"type": "array",
"description": "白名单(glob 模式)",
"items": {
"type": "string"
}
},
"generatePrompt": {
"type": "boolean",
"description": "是否生成 AI Prompt(而非直接验证)",
"default": false
},
"minScore": {
"type": "number",
"description": "最低通过分数",
"minimum": 0,
"maximum": 100,
"default": 60
}
}
}
+438
View File
@@ -0,0 +1,438 @@
#!/usr/bin/env tsx
/**
* AI 代码验证工具
*
* 利用模型自身能力进行代码质量验证(一致性检查、自我批评、事实检查)
* 无需外部工具侵入,无需 API Key
*/
import { readFile } from 'node:fs/promises'
import { existsSync } from 'node:fs'
import { resolve } from 'node:path'
import { execSync } from 'node:child_process'
import type { Config } from './types.ts'
interface VerifyResult {
file: string
level: number
passed: boolean
issues: string[]
score: number
prompt?: string
}
/**
* 加载配置文件
*/
async function loadConfig(): Promise<Config> {
const configPath = resolve(process.cwd(), '.ai-code-verify.json')
if (!existsSync(configPath)) {
return {
level: 0,
enabled: true,
ignorePatterns: ['node_modules', 'dist', 'build', '.git'],
includePatterns: ['src/**/*.{ts,tsx,js,jsx}', 'src-tauri/**/*.rs'],
}
}
const content = await readFile(configPath, 'utf-8')
return JSON.parse(content)
}
/**
* 静态代码检查(不调用 AI)
*/
function staticChecks(code: string, filePath: string): VerifyResult {
const issues: string[] = []
let score = 100
// JavaScript/TypeScript 安全检查
if (/\.(ts|tsx|js|jsx)$/.test(filePath)) {
// 危险模式检查
const dangerousPatterns = [
{ pattern: /eval\s*\(/, msg: '使用 eval() 可能存在代码注入风险', impact: -20 },
{ pattern: /Function\s*\(\s*['"]/, msg: '使用 Function 构造器可能存在安全风险', impact: -20 },
{ pattern: /innerHTML\s*=/, msg: '使用 innerHTML 可能存在 XSS 风险', impact: -15 },
{ pattern: /dangerouslySetInnerHTML/, msg: '使用 dangerouslySetInnerHTML 可能存在 XSS 风险', impact: -15 },
{ pattern: /document\.write\s*\(/, msg: '使用 document.write() 可能存在安全风险', impact: -10 },
{ pattern: /\.exec\s*\(/, msg: '使用 .exec() 可能存在命令注入风险', impact: -15 },
]
dangerousPatterns.forEach(({ pattern, msg, impact }) => {
if (pattern.test(code)) {
issues.push(msg)
score += impact
}
})
// 代码质量问题
if (code.includes('console.log')) {
issues.push('代码中包含 console.log,应该清理')
score -= 5
}
if (code.includes('debugger')) {
issues.push('代码中包含 debugger 语句')
score -= 5
}
// TODO 检查
const todoCount = (code.match(/\/\/ TODO/g) || []).length
if (todoCount > 3) {
issues.push(`存在 ${todoCount} 个 TODO 未处理`)
score -= Math.min(todoCount * 2, 10)
}
// 空长行检查
const lines = code.split('\n')
const longLines = lines.filter(line => line.length > 120)
if (longLines.length > 0) {
issues.push(`存在 ${longLines.length} 行超过 120 字符的代码`)
score -= Math.min(longLines.length, 5)
}
}
// Rust 安全检查
if (/\.rs$/.test(filePath)) {
if (code.includes('unsafe {')) {
issues.push('使用 unsafe 块,需要手动验证安全性')
score -= 10
}
if (code.includes('.unwrap()')) {
issues.push('使用 .unwrap() 可能导致 panic')
score -= 5
}
if (code.includes('.expect(') && !code.includes('.ok(')) {
issues.push('使用 .expect() 但没有 .ok() 处理错误')
score -= 10
}
}
score = Math.max(0, Math.min(100, score))
const passed = score >= 60
return {
file: filePath,
level: 0,
passed,
issues,
score,
}
}
/**
* 生成 AI 验证 Prompt(供用户复制到 AI 对话框)
*/
function generateVerifyPrompt(code: string, filePath: string, level: number): string {
const prompts = {
0: `# AI 代码验证请求 (Level 0: 基础验证)
请分析以下代码并进行一致性检查:
**文件**: ${filePath}
\`\`\`${
code.split('\n').map((line, i) => `${(i + 1).toString().padStart(4, ' ')}│${line}`).join('\n')
}\`\`\`
**步骤**:
1. 生成解决方案 A:从第一性原理思考这个问题的解决方案
2. 生成解决方案 B:使用**不同的推理路径**(避免参考步骤 A)
3. 一致性检查:比较 A 和 B 的核心逻辑,标识关键差异
4. 选择更合理/简洁/可维护的方案
5. 说明选择理由
**输出格式**:
\`\`\`markdown
## 验证报告
### 一致性分析
[说明 A 和 B 方案的核心逻辑、差异、选择理由]
### 发现的问题
- [问题 1]
- [问题 2]
...
### 评分
[0-100,说明理由]
### 建议
[如何改进]
\`\`\`
`,
1: `# AI 代码验证请求 (Level 1: 中级验证)
请分析以下代码并进行安全审查和自我批评:
**文件**: ${filePath}
\`\`\`${
code.split('\n').map((line, i) => `${(i + 1).toString().padStart(4, ' ')}│${line}`).join('\n')
}\`\`\`
**步骤**:
1. 一致性检查(生成 A/B 方案并比较)
2. 安全审查:
- 输入验证:是否验证用户输入?是否防注入?
- 权限控制:是否有未授权访问风险?
- 数据保护:是否有敏感数据泄露?
- 依赖安全:使用的库是否有已知漏洞?
- 错误处理:是否暴露内部信息?
3. 自我批评:
- 逻辑正确性:是否有边界情况未处理?
- 代码质量:是否过度复杂?是否有重复代码?
- 可维护性:后续修改会困难吗?
- 安全性:有注入风险吗?有敏感信息泄露吗?
**输出格式**:
\`\`\`markdown
## 验证报告
### 一致性分析
[...]
### 安全审查
- 输入验证:[...]
- 权限控制:[...]
- 数据保护:[...]
- 依赖安全:[...]
- 错误处理:[...]
### 自我批评
- 逻辑正确性:[...]
- 代码质量:[...]
- 可维护性:[...]
- 安全性:[...]
### 发现的问题
- [安全问题1]
- [质量问题2]
...
### 评分
[0-100,说明理由]
### 建议
[如何改进]
\`\`\`
`,
2: `# AI 代码验证请求 (Level 2: 高级验证)
请对以下代码进行深度反思验证:
**文件**: ${filePath}
\`\`\`${
code.split('\n').map((line, i) => `${(i + 1).toString().padStart(4, ' ')}│${line}`).join('\n')
}\`\`\`
**步骤**:
1. 一致性检查(生成 A/B 方案并比较)
2. 安全审查和自我批评(同 Level 1)
3. 深度反思:
- 元认知反思:推理过程是否合理?是否有认知偏差?
- 替代理理:如果是另一个 AI,会如何评价这个代码?
- 场景模拟:在生产环境、高并发、异常情况会发生什么?
**输出格式**:
\`\`\`markdown
## 验证报告
### 一致性分析
[...]
### 安全审查
[...]
### 自我批评
[...]
### 深度反思
#### 元认知反思
[...]
#### 替代理理
[...]
#### 场景模拟
[...]
### 发现的问题
- [深层问题1]
- [深层问题2]
...
### 评分
[0-100,说明理由]
### 建议
[如何改进]
\`\`\`
`,
}
return prompts[level as keyof typeof prompts] || prompts[0]
}
/**
* 验证单个文件
*/
async function verifyFile(filePath: string, config: Config): Promise<VerifyResult> {
try {
const content = await readFile(filePath, 'utf-8')
// 静态检查
const staticResult = staticChecks(content, filePath)
// 生成 AI Prompt(如果需要)
if (config.generatePrompt) {
staticResult.prompt = generateVerifyPrompt(content, filePath, config.level)
}
return staticResult
}
catch (error) {
return {
file: filePath,
level: 0,
passed: false,
issues: [`验证失败: ${error}`],
score: 0,
}
}
}
/**
* 获取待验证的文件列表
*/
function getFilesToVerify(config: Config): string[] {
try {
// 验证器自身包含规则关键字,跳过以避免自触发误报
const selfExcludedFiles = new Set(['scripts/ai-code-verify.ts'])
// 获取 git 暂存的文件
const output = execSync('git diff --cached --name-only --diff-filter=ACM', {
encoding: 'utf-8',
}).trim()
if (!output) {
return []
}
const files = output.split('\n')
.filter(file => {
if (selfExcludedFiles.has(file)) {
return false
}
// 过滤忽略的目录
const shouldIgnore = config.ignorePatterns.some(pattern =>
file.includes(pattern)
)
return !shouldIgnore
})
return files
}
catch (error) {
console.error('获取文件列表失败:', error)
return []
}
}
/**
* 主函数
*/
async function main() {
const args = process.argv.slice(2)
const config = await loadConfig()
// 解析命令行参数
const filesArg = args.findIndex(arg => arg === '--files')
const levelArg = args.findIndex(arg => arg === '--level')
const generatePromptArg = args.findIndex(arg => arg === '--generate-prompt')
if (levelArg !== -1) {
config.level = Number.parseInt(args[levelArg + 1], 10)
}
if (generatePromptArg !== -1) {
config.generatePrompt = true
}
// 获取待验证文件
let filesToVerify: string[] = []
if (filesArg !== -1) {
// 手动指定文件
filesToVerify = args.slice(filesArg + 1).filter(f => !f.startsWith('--'))
}
else {
// 从 git 获取暂存的文件(pre-commit 模式)
filesToVerify = getFilesToVerify(config)
}
if (filesToVerify.length === 0) {
console.log('✅ 没有文件需要验证')
process.exit(0)
}
console.log(`🔍 AI 代码验证 (Level ${config.level})`)
console.log(`📁 待验证文件: ${filesToVerify.length}\n`)
// 验证所有文件
const results: VerifyResult[] = []
for (const file of filesToVerify) {
console.log(`⏳ 验证: ${file}`)
const result = await verifyFile(file, config)
results.push(result)
if (result.passed) {
console.log(` ✅ 通过 (${result.score}/100)`)
}
else {
console.log(` ❌ 失败 (${result.score}/100)`)
result.issues.forEach(issue => console.log(` - ${issue}`))
}
if (result.prompt && config.generatePrompt) {
console.log(`\n📋 AI 验证 Prompt:\n`)
console.log('━'.repeat(50))
console.log(result.prompt)
console.log('━'.repeat(50))
console.log('\n提示:将上述 Prompt 复制到 AI 对话框中获取详细分析\n')
}
console.log()
}
// 汇总
const passed = results.filter(r => r.passed).length
const failed = results.length - passed
const avgScore = Math.round(
results.reduce((sum, r) => sum + r.score, 0) / results.length
)
console.log('━'.repeat(50))
console.log(`📊 验证结果: ${passed} 通过, ${failed} 失败`)
console.log(`📊 平均分: ${avgScore}/100`)
if (failed > 0) {
console.log('\n❌ 存在文件未通过验证')
console.log('\n提示:使用 --generate-prompt 生成 AI 验证 Prompt')
process.exit(1)
}
console.log('\n✅ 所有文件验证通过')
process.exit(0)
}
main().catch(error => {
console.error('验证工具执行失败:', error)
process.exit(1)
})
+40
View File
@@ -0,0 +1,40 @@
/**
* AI 代码验证工具配置类型
*/
export interface Config {
/**
* 验证等级(0-2)
* - Level 0: 基本验证 + 一致性检查
* - Level 1: Level 0 + 安全审查 + 自我批评
* - Level 2: Level 1 + 深度反思
*/
level: number
/**
* 是否启用验证
*/
enabled: boolean
/**
* 忽略的文件/目录模式
*/
ignorePatterns: string[]
/**
* 白名单:仅验证这些文件(glob 模式)
*/
includePatterns?: string[]
/**
* 是否生成 AI Prompt(而非调 API)
* true: 输出 Prompt 供用户复制到 AI 对话框
* false: 仅运行静态检查
*/
generatePrompt?: boolean
/**
* 最低通过分数
*/
minScore?: number
}